Le but de ce programme est de transformer un fichier PNG ou JPG en STL (3D). L'image doit être en fond transparent ou en fond noir, voir gris clair avec l'objet en Blanc ou noir (exemple ci-dessous).
# Programme : Eric LEFEBURE : 2025
import cv2
import os
import numpy as np
# Nom de l'image à traiter
image_name = "image_objet.png"
# Charger l'image PNG avec alpha (si elle existe déjà)
image = cv2.imread(image_name, cv2.IMREAD_UNCHANGED)
if image is None:
print("Erreur de chargement de l'image.")
exit()
# Si l'image n'a pas de canal alpha, ajouter un canal alpha
if image.shape[2] == 3:
image = cv2.cvtColor(image, cv2.COLOR_BGR2BGRA)
# Créer une copie de l'image
new_image = image.copy()
# Conversion de l'image en niveaux de gris
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Seuillage pour obtenir une image binaire (noir et blanc)
ret, binary = cv2.threshold(gray, 100, 255, cv2.THRESH_OTSU)
# Inversion de l'image binaire (fond noir, objet blanc)
inverted_binary = ~binary
# Détection des contours
contours, hierarchy = cv2.findContours(inverted_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# Créer un masque vide pour l'objet
mask = np.zeros_like(gray)
# Dessiner les contours sur le masque (les deux contours internes et externes)
cv2.drawContours(mask, contours, -1, (255), thickness=cv2.FILLED)
# Créer une image avec fond transparent
transparent_background = np.zeros_like(image)
# Placer l'objet détecté sur le fond transparent
transparent_background[:, :, :3] = cv2.bitwise_and(image[:, :, :3], image[:, :, :3], mask=mask)
# Ajouter un canal alpha (transparence) avec 255 pour l'objet et 0 pour le fond
transparent_background[:, :, 3] = mask
# Enregistrer l'image avec fond transparent
output_image_name = os.path.splitext(image_name)[0] + '-transparent.png'
cv2.imwrite(output_image_name, transparent_background)
print(f"Image enregistrée sous : {output_image_name}")
# Afficher l'image pour vérification
from stl import mesh
# Définir le nom de l'image PNG avec fond transparent
image_path = output_image_name
# Charger l'image (en supposant que l'image a déjà été traitée pour avoir une transparence)
# Charger l'image en niveau de gris, on suppose que l'image binaire existe
image = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)
# Vérifier si l'image est bien chargée
if image is None:
print(f"Erreur : Impossible de charger l'image depuis {image_path}.")
exit()
# Extraire le canal alpha de l'image pour créer le masque
alpha_channel = image[:, :, 3] # Canal alpha (transparence)
# Appliquer un seuil pour obtenir un masque binaire
_, threshold = cv2.threshold(alpha_channel, 1, 255, cv2.THRESH_BINARY)
# Paramètres d'épaisseur
thickness = 15 # Épaisseur de l'objet (distance entre les deux faces)
# Créer une grille de points 3D basée sur la carte de profondeur
height, width = threshold.shape
vertices = []
faces = []
# Créer la topologie de la forme 3D en fonction du masque
for y in range(height - 1):
for x in range(width - 1):
if threshold[y, x] > 0 and threshold[y + 1, x] > 0 and threshold[y, x + 1] > 0 and threshold[y + 1, x + 1] > 0:
# Calculer la hauteur (z) en fonction de la valeur du pixel
z_top = threshold[y, x] / 255.0 * 10 # Face supérieure
z_bottom = z_top - thickness # Face inférieure (épaisseur ajoutée)
# Définir les quatre coins du carré pour la face supérieure
v1_top = (x, y, z_top)
v2_top = (x + 1, y, z_top)
v3_top = (x, y + 1, z_top)
v4_top = (x + 1, y + 1, z_top)
# Définir les quatre coins du carré pour la face inférieure
v1_bottom = (x, y, z_bottom)
v2_bottom = (x + 1, y, z_bottom)
v3_bottom = (x, y + 1, z_bottom)
v4_bottom = (x + 1, y + 1, z_bottom)
# Ajouter les sommets
vertex_index = len(vertices)
vertices.extend([v1_top, v2_top, v3_top, v4_top, v1_bottom, v2_bottom, v3_bottom, v4_bottom])
# Ajouter les faces pour la face supérieure
faces.append([vertex_index, vertex_index + 1, vertex_index + 2]) # Triangle 1
faces.append([vertex_index + 1, vertex_index + 3, vertex_index + 2]) # Triangle 2
# Ajouter les faces pour la face inférieure (inversées pour respecter l'orientation)
faces.append([vertex_index + 4, vertex_index + 6, vertex_index + 5]) # Triangle 1
faces.append([vertex_index + 6, vertex_index + 7, vertex_index + 5]) # Triangle 2
# Ajouter les faces latérales pour connecter les deux faces
faces.append([vertex_index, vertex_index + 4, vertex_index + 5]) # Côté 1
faces.append([vertex_index, vertex_index + 5, vertex_index + 1])
faces.append([vertex_index + 1, vertex_index + 5, vertex_index + 6]) # Côté 2
faces.append([vertex_index + 1, vertex_index + 6, vertex_index + 3])
faces.append([vertex_index + 3, vertex_index + 7, vertex_index + 6]) # Côté 3
faces.append([vertex_index + 3, vertex_index + 2, vertex_index + 7])
faces.append([vertex_index + 2, vertex_index + 4, vertex_index + 7]) # Côté 4
faces.append([vertex_index + 2, vertex_index + 6, vertex_index + 7])
# Convertir les points et les faces en tableaux NumPy
vertices = np.array(vertices, dtype=np.float32)
faces = np.array(faces, dtype=np.uint32)
# Créer le maillage STL
stl_mesh = mesh.Mesh(np.zeros(faces.shape[0], dtype=mesh.Mesh.dtype))
for i, f in enumerate(faces):
for j in range(3):
stl_mesh.vectors[i][j] = vertices[f[j], :]
# Enregistrer le fichier STL
output_path = os.path.splitext(image_name)[0] + "-Traitement.stl"
stl_mesh.save(output_path)
print(f"Le modèle 3D avec épaisseur a été enregistré dans {output_path}.")