-
Notifications
You must be signed in to change notification settings - Fork 0
/
imageTo3D_API.py
162 lines (130 loc) · 6.62 KB
/
imageTo3D_API.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
import meshio
from tkinter import filedialog
class ImageTo3D:
def __init__(self, image_path=None, reduction_factor=10, extrudeScale=5,inverse=False):
if reduction_factor is None:
reduction_factor = self.reccomendedReduction()
self.image_path = image_path
self.mesh=None
self.inverse=inverse
self.bottomWidth=10*extrudeScale/25
def new_image(self, image_path=None, reduction_factor=None, extrudeScale=None,inverse=False):
self.image_path = image_path
print(self.image_path)
if image_path is None:
self.upload_file()
if reduction_factor is not None:
self.reduction_factor = reduction_factor
if extrudeScale is not None:
self.extrudeScale = extrudeScale/25
# Open the image and convert to black and white
try:
self.image = Image.open(self.image_path)
except:
print("Invalid file path")
return
self.image.convert("RGB")
if reduction_factor is None:
reduction_factor = self.reccomendedReduction()
self.image_bw = self.image.convert('L')
self.mesh=None
self.inverse=inverse
def generate_mesh(self):
image = self.image
image_bw = self.image_bw.transpose(Image.FLIP_LEFT_RIGHT)
if image.width//self.reduction_factor < 3 or image.height//self.reduction_factor < 3:
print("Image too small, decrease reduction_factor")
return
# Reduce the size of the image by reduction factor to speed up processing
image_bw = image_bw.resize((image.width // self.reduction_factor, image.height // self.reduction_factor))
# Generate 3D coordinates based on color values
x, y = np.meshgrid(range(image_bw.width), range(image_bw.height))
z = np.array(image_bw)
if self.inverse:
z=255-z
x, y, z = x.flatten(), y.flatten(), z.flatten()
# Makes a list of points based on x, y and z, scaled to look better
mod=1
if image_bw.width > 1000 and image_bw.height > 1000:
mod=1/2
points = np.column_stack((x*mod, y*mod, z*(self.extrudeScale)+self.bottomWidth+1))
verticiesCount = len(points)
faces = []
for i in range(image_bw.height - 1):
for j in range(image_bw.width - 1):
# First triangle
faces.append([i * image_bw.width + j + 1, (i + 1) * image_bw.width + j, i * image_bw.width + j])
# Second triangle
faces.append([i * image_bw.width + j + 1, (i + 1) * image_bw.width + j + 1,(i + 1) * image_bw.width + j])
bottom_points = np.column_stack((x*mod, y*mod, np.zeros_like(z)))
# Combine top and bottom plane vertices
points = np.vstack((points, bottom_points))
# Add bottom plane faces
bottom_faces = []
bottom_offset = len(points) - len(bottom_points)
for i in range(image_bw.height - 1):
for j in range(image_bw.width - 1):
# Bottom plane
bottom_faces.append([i * image_bw.width + j + bottom_offset,
(i + 1) * image_bw.width + j + bottom_offset,
i * image_bw.width + j + 1 + bottom_offset])
bottom_faces.append([(i + 1) * image_bw.width + j + bottom_offset,
(i + 1) * image_bw.width + j + 1 + bottom_offset,
i * image_bw.width + j + 1 + bottom_offset])
bottom_faces = np.array(bottom_faces)
for i in range(image_bw.height - 1):
#left faces
faces.append([i * image_bw.width, i * image_bw.width + verticiesCount, (i + 1) * image_bw.width])
faces.append([(i + 1) * image_bw.width, i * image_bw.width + verticiesCount, (i + 1) * image_bw.width + verticiesCount])
#right faces
faces.append([i * image_bw.width + image_bw.width - 1, i * image_bw.width + image_bw.width - 1 + verticiesCount, (i + 1) * image_bw.width + image_bw.width - 1])
faces.append([(i + 1) * image_bw.width + image_bw.width - 1, i * image_bw.width + image_bw.width - 1 + verticiesCount, (i + 1) * image_bw.width + image_bw.width - 1 + verticiesCount])
for i in range(image_bw.width - 1):
#top faces
faces.append([i, i + 1, i + verticiesCount])
faces.append([i + 1, i + 1 + verticiesCount, i + verticiesCount])
#bottom faces
faces.append([i + image_bw.width * (image_bw.height - 1), i + image_bw.width * (image_bw.height - 1) + verticiesCount, i + 1 + image_bw.width * (image_bw.height - 1)])
faces.append([i + 1 + image_bw.width * (image_bw.height - 1), i + image_bw.width * (image_bw.height - 1) + verticiesCount, i + 1 + verticiesCount + image_bw.width * (image_bw.height - 1)])
# Combine top and bottom plane faces
faces = np.vstack((faces, bottom_faces))
faces = np.array(faces)
mesh = meshio.Mesh(points=points, cells=[("triangle", faces)])
self.mesh=mesh
return mesh
def export_mesh(self, mesh):
f_types = [('STL Files', '*.stl')]
filename = filedialog.asksaveasfilename(filetypes=f_types)
if not filename.endswith(".stl"):
filename+=".stl"
meshio.write(filename, mesh)
print(f"3D model exported to {filename}")
def reccomendedReduction(self):
width = self.image.width
height = self.image.height
print("Reccomended reduction factor: ",round((width*height)/200000))
return round((width*height)/200000)
def update_reduction_factor(self, reduction_factor):
self.reduction_factor = reduction_factor
def update_extrude_scale(self, extrudeScale):
self.extrudeScale = extrudeScale/25
def update_inverse(self,inverse):
self.inverse=inverse
def upload_file(self):
#lets the user upload an image, and returns the path to the image
f_types = [('JPG Files', '*.jpg'), ('PNG Files', '*.png'), ('JPEG Files', '*.jpeg')]
self.image_path = filedialog.askopenfilename(filetypes=f_types)
def preview(self):
#shows a preview of the image
plt.imshow(self.image)
plt.show()
if __name__ == "__main__":
inv=True
print("Enter the path to the image you want to convert to 3D")
imageTo3D = ImageTo3D(reduction_factor=6,extrudeScale=1/2,inverse=inv)
imageTo3D.new_image()
mesh_data = imageTo3D.generate_mesh()
imageTo3D.export_mesh(mesh_data)