Hi Tweakie,
It's a honor to have you since I red your laser project topic several years ago.
I made a python script to convert an image in Gcode, not that bad but more tuning is needed to get the fullscale gray level potential of my laser. Here's the code :
from PIL import Image, ImageOps
# --- PARAMÈTRES UTILISATEUR ---
input_image_path = "IMG_3901.jpg"
output_file = "IMG_3901.nc"
target_width_mm = 50.0
feedrate = 3000
line_step = 0.14
min_power = 10 # Blanc / Gris très clair
max_power = 25 # Noir profond (Saturation à partir de 20)
# --- REGLAGE DU CONTRASTE ---
# gamma > 1.0 : éclaircit les gris moyens (donne plus de nuances dans les zones sombres)
gamma_correction = 1.6
# --- CALIBRATION DYNAMIQUE ---
laser_latency_ms = 16.0
boost_at_3000 = -0.25
reference_feed = 1500.0
pre_move = 15.0
# --- CALCUL DE L'OFFSET ---
base_offset = (feedrate * laser_latency_ms) / 60000
extra_boost = 0.0
if feedrate > reference_feed:
extra_boost = boost_at_3000 * ((feedrate - reference_feed) / (3000.0 - reference_feed))
current_offset = base_offset + extra_boost
# --- TRAITEMENT IMAGE ---
img = Image.open(input_image_path).convert('L')
# 1. Égalisation et Contraste pour utiliser toute l'échelle de puissance
img = ImageOps.equalize(img)
img = ImageOps.autocontrast(img, cutoff=1)
# 2. Redimensionnement proportionnel
original_w, original_h = img.size
width_pixels = int(target_width_mm / line_step)
height_pixels = int(width_pixels * (original_h / original_w))
img = img.resize((width_pixels, height_pixels), Image.Resampling.LANCZOS)
real_width_mm = width_pixels * line_step
real_height_mm = height_pixels * line_step
# --- GÉNÉRATION G-CODE ---
gcode = []
gcode.append(f"( Image: {input_image_path} | Gamma: {gamma_correction} )")
gcode.append("G21 G90 G17 G94")
gcode.append("M3 M67 E0 Q0")
# Boucle de gravure (de bas en haut pour origine en bas à gauche)
for py in range(height_pixels - 1, -1, -1):
y_pos = (height_pixels - 1 - py) * line_step
is_forward = (py % 2 == 0)
if is_forward:
x_start, x_end, x_dir = 0.0, (width_pixels - 1) * line_step, 1
x_range = range(width_pixels)
corr = current_offset
else:
x_start, x_end, x_dir = (width_pixels - 1) * line_step, 0.0, -1
x_range = range(width_pixels - 1, -1, -1)
corr = -current_offset
# Approche (Runway)
gcode.append(f"G0 X{x_start - (pre_move * x_dir):.3f} Y{y_pos:.3f}")
gcode.append(f"G1 X{x_start + corr:.3f} F{feedrate}")
# Gravure de la ligne pixel par pixel
for px in x_range:
pixel_val = img.getpixel((px, py)) # 0 à 255
# Inversion et Normalisation (0.0 à 1.0)
norm_val = 1.0 - (pixel_val / 255.0)
# Courbe Gamma
corrected_val = norm_val ** gamma_correction
# Conversion en puissance réelle
power = min_power + (corrected_val * (max_power - min_power))
x_pixel = px * line_step
gcode.append(f"M67 E0 Q{power:.2f}")
gcode.append(f"G1 X{x_pixel + corr:.3f}")
# Fin de ligne et Overscan
gcode.append("M67 E0 Q0")
gcode.append(f"G1 X{x_end + (pre_move * x_dir):.3f}")
gcode.append("M5\nM30")
# Écriture du fichier
with open(output_file, "w") as f:
f.write("\n".join(gcode))
# Affichage des informations
print(f"Fichier généré avec correction gamma {gamma_correction}")
print(f"Nombre de pixels : {width_pixels}x{height_pixels}")
print(f"Taille de sortie : {real_width_mm:.2f}x{real_height_mm:.2f} mm")
I tried to upload an image but an error occured, I uploaded it :
https://i.postimg.cc/Zq9d0N0h/IMG-4230c.jpgPs : Don't mind the spoiler board, the real one is underneath !
Ps2 : the verification method is broken, forced to listen to it ? (The letters you typed don't match the letters that were shown in the picture)