|
import os |
|
import numpy as np |
|
from PIL import Image |
|
from stl import mesh |
|
from flask import Flask, render_template_string, request, send_file |
|
from werkzeug.utils import secure_filename |
|
import cv2 |
|
|
|
app = Flask(__name__) |
|
|
|
UPLOAD_FOLDER = 'uploads' |
|
OUTPUT_FOLDER = 'outputs' |
|
os.makedirs(UPLOAD_FOLDER, exist_ok=True) |
|
os.makedirs(OUTPUT_FOLDER, exist_ok=True) |
|
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER |
|
app.config['OUTPUT_FOLDER'] = OUTPUT_FOLDER |
|
|
|
HTML_TEMPLATE = """ |
|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<title>3D Printing Model Generator</title> |
|
</head> |
|
<body> |
|
<h1>Upload Image to Generate 3D Printable Model</h1> |
|
<form action="/upload" method="post" enctype="multipart/form-data"> |
|
<label>Image:</label><input type="file" name="image" required><br><br> |
|
<input type="submit" value="Upload & Generate 3D Model"> |
|
</form> |
|
{% if download_link %} |
|
<h2>3D Model Ready:</h2> |
|
<a href="{{ download_link }}" download>Download STL File</a> |
|
{% endif %} |
|
</body> |
|
</html> |
|
""" |
|
|
|
def image_to_height_map(image_path, size=(256, 256)): |
|
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) |
|
resized = cv2.resize(image, size) |
|
normalized = resized / 255.0 |
|
return normalized |
|
|
|
def height_map_to_stl(height_map, output_path, scale=10.0): |
|
rows, cols = height_map.shape |
|
vertices = [] |
|
faces = [] |
|
|
|
for i in range(rows): |
|
for j in range(cols): |
|
z = height_map[i, j] * scale |
|
vertices.append([j, i, z]) |
|
|
|
vertices = np.array(vertices).reshape((rows, cols, 3)) |
|
face_list = [] |
|
|
|
for i in range(rows - 1): |
|
for j in range(cols - 1): |
|
face_list.append([vertices[i, j], vertices[i + 1, j], vertices[i, j + 1]]) |
|
face_list.append([vertices[i + 1, j], vertices[i + 1, j + 1], vertices[i, j + 1]]) |
|
|
|
faces_np = np.array(face_list) |
|
model = mesh.Mesh(np.zeros(faces_np.shape[0], dtype=mesh.Mesh.dtype)) |
|
|
|
for i, f in enumerate(faces_np): |
|
model.vectors[i] = f |
|
|
|
model.save(output_path) |
|
|
|
def create_3d_model(image_path): |
|
height_map = image_to_height_map(image_path) |
|
output_path = os.path.join(app.config['OUTPUT_FOLDER'], 'model.stl') |
|
height_map_to_stl(height_map, output_path) |
|
return output_path |
|
|
|
@app.route('/', methods=['GET']) |
|
def index(): |
|
return render_template_string(HTML_TEMPLATE, download_link=None) |
|
|
|
@app.route('/upload', methods=['POST']) |
|
def upload(): |
|
uploaded_file = request.files['image'] |
|
if uploaded_file: |
|
filename = secure_filename(uploaded_file.filename) |
|
image_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) |
|
uploaded_file.save(image_path) |
|
|
|
stl_path = create_3d_model(image_path) |
|
return render_template_string(HTML_TEMPLATE, download_link=f'/download/{os.path.basename(stl_path)}') |
|
return render_template_string(HTML_TEMPLATE, download_link=None) |
|
|
|
@app.route('/download/<filename>', methods=['GET']) |
|
def download_file(filename): |
|
return send_file(os.path.join(app.config['OUTPUT_FOLDER'], filename), as_attachment=True) |
|
|
|
|