Refactored planar and curved model creation.

master
Rostislav Lán 2 years ago
parent d664407d76
commit 73dc3722d7

@ -103,7 +103,6 @@ class fingerprint_app:
help="switch to mirror input image") help="switch to mirror input image")
# another boolean switch argument, this time with value, name of the new file and dimensions # another boolean switch argument, this time with value, name of the new file and dimensions
# TODO: behaves absolutely randomly for some reason, fix it
parser.add_argument('-s', "--stl", type=str, nargs='*', parser.add_argument('-s', "--stl", type=str, nargs='*',
help="create stl model from processed image") help="create stl model from processed image")
@ -125,11 +124,14 @@ class fingerprint_app:
# Get stl filename # Get stl filename
self.stl_path = self.output_file.rsplit('/', 1)[0] + '/' self.stl_path = self.output_file.rsplit('/', 1)[0] + '/'
self.mode = self.args.stl[0] self.mode = self.args.stl[0]
log.print_message("Stl generation in", self.mode, "mode")
# Default values for stl generation parameters
def_val = {"hl": 2, "hb": 10, "crx": 2, "cry": 2, "it": 2, "fx": 0, "fy": 0, "fz": 0}
# Get mode and model parameters # Get mode and model parameters
if self.mode == 'p': if self.mode == 'p':
def_val = {"hl": 1.0, "hb": 0.2}
self.height_line = float(self.args.stl[1]) if len( self.height_line = float(self.args.stl[1]) if len(
self.args.stl) > 1 else def_val.get("hl") self.args.stl) > 1 else def_val.get("hl")
self.height_base = float(self.args.stl[2]) if len( self.height_base = float(self.args.stl[2]) if len(
@ -137,14 +139,12 @@ class fingerprint_app:
if len(self.args.stl) < 3: if len(self.args.stl) < 3:
log.print_message( log.print_message(
"Warning: Too few arguments, using default values") "Warning: Too few arguments, using some default values")
log.print_message("Base height:", self.height_base, log.print_message("Base height:", self.height_base,
"mm, lines depth/height:", self.height_line, "mm") "mm, lines depth/height:", self.height_line, "mm")
elif self.mode == 'c': elif self.mode == 'c':
def_val = {"hl": 0.2, "hb": 10.0,
"crx": 2, "cry": 4}
self.height_line = float(self.args.stl[1]) if len( self.height_line = float(self.args.stl[1]) if len(
self.args.stl) > 1 else def_val.get("hl") self.args.stl) > 1 else def_val.get("hl")
self.height_base = float(self.args.stl[2]) if len( self.height_base = float(self.args.stl[2]) if len(
@ -156,16 +156,14 @@ class fingerprint_app:
if len(self.args.stl) < 5: if len(self.args.stl) < 5:
log.print_message( log.print_message(
"Warning: Too few arguments, using default values") "Warning: Too few arguments, using some default values")
log.print_message("Line height:", self.height_line, "mm, base height:", self.height_base, log.print_message("Line height:", self.height_line, "mm, base height:", self.height_base,
"mm, x axis curvature:", self.curv_rate_x, ", y axis curvature:", self.curv_rate_y) "mm, x axis curvature:", self.curv_rate_x, ", y axis curvature:", self.curv_rate_y)
elif self.mode == 'm': elif self.mode == 'm':
def_val = {"hl": 0.2, "it": 2,
"fx": 0, "fy": 0, "fz": 0}
self.height_line = float(self.args.stl[1]) if len( self.height_line = float(self.args.stl[1]) if len(
self.args.stl) > 1 else def_val.get("hl") self.args.stl) > 1 else def_val.get("hl")/10
self.iter = int(self.args.stl[2]) if len( self.iter = int(self.args.stl[2]) if len(
self.args.stl) > 2 else def_val.get("it") self.args.stl) > 2 else def_val.get("it")
self.finger_x = float(self.args.stl[3]) if len( self.finger_x = float(self.args.stl[3]) if len(
@ -177,14 +175,13 @@ class fingerprint_app:
if len(self.args.stl) < 6: if len(self.args.stl) < 6:
log.print_message( log.print_message(
"Warning: Too few arguments, using default values for mapped mode") "Warning: Too few arguments, using some default values")
log.print_message("Line height:", self.height_line, "mm, iterations:", self.iter, log.print_message("Line height:", self.height_line, "mm, iterations:", self.iter,
", finger position:", self.finger_x, self.finger_y, self.finger_z) ", finger position:", self.finger_x, self.finger_y, self.finger_z)
else: else:
log.error_exit("Unrecognized generation mode") log.error_exit("Unrecognized generation mode")
log.print_message("Stl generation in", self.mode, "mode")
self.run_stl() self.run_stl()
# ------------------------- FILTERING -------------------------# # ------------------------- FILTERING -------------------------#
@ -269,8 +266,6 @@ class fingerprint_app:
# create ID for the model from all its parameters # create ID for the model from all its parameters
self.get_ID() self.get_ID()
log.print_message("Creating mesh")
# Create a mesh using one of two modes # Create a mesh using one of two modes
if self.mode == "p": if self.mode == "p":
self.make_stl_planar() self.make_stl_planar()
@ -283,7 +278,6 @@ class fingerprint_app:
plt.show() plt.show()
self.save_stl() self.save_stl()
log.print_message("Saving model to", self.stl_filename)
def prepare_heightmap(self): def prepare_heightmap(self):
'''Scale image values to get values from 0 to 255. '''Scale image values to get values from 0 to 255.
@ -293,7 +287,7 @@ class fingerprint_app:
''' '''
if self.img.dtype != np.uint8: if self.img.dtype != np.uint8:
log.print_message("Converting to uint8") log.print_message("Converting heightmap to uint8")
self.img = self.img / np.max(self.img) * 255 self.img = self.img / np.max(self.img) * 255
self.img = self.img.astype(np.uint8) self.img = self.img.astype(np.uint8)
@ -458,6 +452,7 @@ class fingerprint_app:
# TODO: this does not always work, fix it # TODO: this does not always work, fix it
# add the bottom array # add the bottom array
OFFSET = 0.01 OFFSET = 0.01
for i in range(self.height): for i in range(self.height):
if self.mode == "p": if self.mode == "p":
for j in range(self.width): for j in range(self.width):
@ -479,6 +474,7 @@ class fingerprint_app:
# Convert lists to numpy arrays # Convert lists to numpy arrays
faces = np.array(faces) faces = np.array(faces)
vertices = np.array(vertices) vertices = np.array(vertices)
c = 0
# Create the mesh - vertices.shape (no_faces, 3, 3) # Create the mesh - vertices.shape (no_faces, 3, 3)
self.stl_model = mesh.Mesh( self.stl_model = mesh.Mesh(
@ -486,37 +482,27 @@ class fingerprint_app:
for i, face in enumerate(faces): for i, face in enumerate(faces):
for j in range(3): for j in range(3):
self.stl_model.vectors[i][j] = vertices[face[j], :] self.stl_model.vectors[i][j] = vertices[face[j], :]
# Prints out generation progress
if i % 100 == 0:
percentage = round(i/len(faces) * 100, 2)
if percentage > c:
log.print_message("Creating model " + str(c) + "%")
c += 10
log.print_message("Model creation finished")
def make_stl_planar(self): def create_faces(self, top_vert_arr, bottom_vert_arr):
'''Create mesh from meshgrid. '''Create faces for the model.
Create vertices from meshgrid, add depth values from image.
Create faces from vertices. Add vectors and faces to the model.
From wikipedia.org/wiki/STL_(file_format):
ascii stl format consists of repeating structures:
facet normal ni nj nk # normal vector Iterate over all vertices, append to vector and create faces from indices
outer loop
vertex v1x v1y v1z # vertex 1
vertex v2x v2y v2z # vertex 2
vertex v3x v3y v3z # vertex 3
endloop
endfacet
''' '''
# Add the image matrix to the 2D meshgrid and create 1D array of 3D points
top_vert_arr = np.vstack(list(map(np.ravel, self.meshgrid))).T
z = (self.img / 10).reshape(-1, 1)
top_vert_arr = np.concatenate((top_vert_arr, z), axis=1)
# Convert 1D array back to matrix of 3D points
top_vert_arr = top_vert_arr.reshape(self.height, self.width, 3)
count = 0 count = 0
vertices = [] vertices = []
faces = [] faces = []
# Iterate over all vertices, create faces # Front side faces
for i in range(self.height - 1): for i in range(self.height - 1):
for j in range(self.width - 1): for j in range(self.width - 1):
@ -524,13 +510,8 @@ class fingerprint_app:
vertices.append([top_vert_arr[i][j+1]]) vertices.append([top_vert_arr[i][j+1]])
vertices.append([top_vert_arr[i+1][j]]) vertices.append([top_vert_arr[i+1][j]])
vertices.append([top_vert_arr[i+1][j+1]]) vertices.append([top_vert_arr[i+1][j+1]])
count = self.append_faces(faces, count) count = self.append_faces(faces, count)
# Prepare image with plotted text for the backside of the lithophane
bottom_vert_arr = np.copy(top_vert_arr)
self.engrave_text(bottom_vert_arr, top_vert_arr)
# Back side faces # Back side faces
for i in range(self.height - 1): for i in range(self.height - 1):
for j in range(self.width - 1): for j in range(self.width - 1):
@ -539,45 +520,66 @@ class fingerprint_app:
vertices.append([bottom_vert_arr[i+1][j]]) vertices.append([bottom_vert_arr[i+1][j]])
vertices.append([bottom_vert_arr[i][j+1]]) vertices.append([bottom_vert_arr[i][j+1]])
vertices.append([bottom_vert_arr[i+1][j+1]]) vertices.append([bottom_vert_arr[i+1][j+1]])
count = self.append_faces(faces, count) count = self.append_faces(faces, count)
# Horizontal side faces # Horizontal side faces
for i in range(self.height - 1): for i in range(self.height - 1):
vertices.append([top_vert_arr[i][0]]) vertices.append([top_vert_arr[i][0]])
vertices.append([top_vert_arr[i+1][0]]) vertices.append([top_vert_arr[i+1][0]])
vertices.append([bottom_vert_arr[i][0]]) vertices.append([bottom_vert_arr[i][0]])
vertices.append([bottom_vert_arr[i+1][0]]) vertices.append([bottom_vert_arr[i+1][0]])
count = self.append_faces(faces, count) count = self.append_faces(faces, count)
max = self.width - 1 max = self.width - 1
vertices.append([top_vert_arr[i+1][max]]) vertices.append([top_vert_arr[i+1][max]])
vertices.append([top_vert_arr[i][max]]) vertices.append([top_vert_arr[i][max]])
vertices.append([bottom_vert_arr[i+1][max]]) vertices.append([bottom_vert_arr[i+1][max]])
vertices.append([bottom_vert_arr[i][max]]) vertices.append([bottom_vert_arr[i][max]])
count = self.append_faces(faces, count) count = self.append_faces(faces, count)
# Vertical side faces # Vertical side faces
for j in range(self.width - 1): for j in range(self.width - 1):
vertices.append([top_vert_arr[0][j+1]]) vertices.append([top_vert_arr[0][j+1]])
vertices.append([top_vert_arr[0][j]]) vertices.append([top_vert_arr[0][j]])
vertices.append([bottom_vert_arr[0][j+1]]) vertices.append([bottom_vert_arr[0][j+1]])
vertices.append([bottom_vert_arr[0][j]]) vertices.append([bottom_vert_arr[0][j]])
count = self.append_faces(faces, count) count = self.append_faces(faces, count)
max = self.height - 1 max = self.height - 1
vertices.append([top_vert_arr[max][j]]) vertices.append([top_vert_arr[max][j]])
vertices.append([top_vert_arr[max][j+1]]) vertices.append([top_vert_arr[max][j+1]])
vertices.append([bottom_vert_arr[max][j]]) vertices.append([bottom_vert_arr[max][j]])
vertices.append([bottom_vert_arr[max][j+1]]) vertices.append([bottom_vert_arr[max][j+1]])
count = self.append_faces(faces, count) count = self.append_faces(faces, count)
return faces, vertices
def make_stl_planar(self):
'''
Create vertices from meshgrid, add depth values from image.
Create faces from vertices. Add vectors and faces to the model.
'''
# Add the image matrix to the 2D meshgrid and create 1D array of 3D points
tmp_vert_arr = np.vstack(list(map(np.ravel, self.meshgrid))).T
top_vert_arr = np.concatenate(
(tmp_vert_arr, (self.img / 10).reshape(-1, 1)), axis=1)
# Convert 1D array back to matrix of 3D points
top_vert_arr = top_vert_arr.reshape(self.height, self.width, 3)
# Prepare image with plotted text for the backside of the lithophane
bottom_vert_arr = np.copy(top_vert_arr)
# Engrave text on the back of the model
self.engrave_text(bottom_vert_arr, top_vert_arr)
# Create all vertices, faces
faces, vertices = self.create_faces(top_vert_arr, bottom_vert_arr)
# Add the created vertices and faces to a mesh
self.create_stl_mesh(faces, vertices) self.create_stl_mesh(faces, vertices)
def make_stl_curved(self): def make_stl_curved(self):
@ -587,21 +589,20 @@ class fingerprint_app:
Create faces from vertices. Add vectors and faces to the model. Create faces from vertices. Add vectors and faces to the model.
''' '''
# TODO: this might be done in a better way, comment # Calculate the curved surface values
z = np.array([]) x = np.arange(self.width)
for x in range(self.width): y = np.arange(self.height)[:, np.newaxis]
z = np.append(z, np.sqrt(1 - (2*x/self.width - 1)**2) x = (2*x / self.width) - 1
* (self.curv_rate_x**2)) z = np.sqrt(1 - x**2) * self.curv_rate_x**2
z = np.tile(z, (self.height, 1)) z = np.tile(z, (self.height, 1))
for y in range(self.height): z *= np.sqrt((1 - ((self.height - y) / self.height)**2)
new = np.sqrt((1 - ((self.height - y)/self.height)**2) * self.curv_rate_y**2)
* (self.curv_rate_y**2))
z[y] = ((z[y] * new) + (z[y] + new))/2
z = z.reshape(-1, 1) z = z.reshape(-1, 1)
# make a copy of z for the bottom side
z_cpy = np.copy(z) # Make a copy of z for the bottom side
# reshape img and add it to z z_cpy = z.copy()
# Reshape img and add it to height values
self.img = (self.img / 10).reshape(-1, 1) self.img = (self.img / 10).reshape(-1, 1)
z += self.img z += self.img
@ -615,79 +616,13 @@ class fingerprint_app:
bottom_vert_arr = np.concatenate((vert_arr_tmp, z_cpy), axis=1) bottom_vert_arr = np.concatenate((vert_arr_tmp, z_cpy), axis=1)
bottom_vert_arr = bottom_vert_arr.reshape(self.height, self.width, 3) bottom_vert_arr = bottom_vert_arr.reshape(self.height, self.width, 3)
count = 0 # Engrave text on the back of the model
vertices = []
faces = []
self.engrave_text(bottom_vert_arr, top_vert_arr) self.engrave_text(bottom_vert_arr, top_vert_arr)
# TODO: code bellow is duplicate of the code in planar generation # Create all vertices, faces
# if not changed move to a separate function and simplify faces, vertices = self.create_faces(top_vert_arr, bottom_vert_arr)
# Iterate over all vertices, create faces
for i in range(self.height - 1):
for j in range(self.width - 1):
if (top_vert_arr[i][j][2] <= bottom_vert_arr[i][j][2]
or top_vert_arr[i+1][j][2] <= bottom_vert_arr[i+1][j][2]
or top_vert_arr[i][j+1][2] <= bottom_vert_arr[i][j+1][2]
or top_vert_arr[i+1][j+1][2] <= bottom_vert_arr[i+1][j+1][2]):
continue
vertices.append([top_vert_arr[i][j]])
vertices.append([top_vert_arr[i][j+1]])
vertices.append([top_vert_arr[i+1][j]])
vertices.append([top_vert_arr[i+1][j+1]])
count = self.append_faces(faces, count)
# Rotated back side faces
for i in range(self.height - 1):
for j in range(self.width - 1):
if (top_vert_arr[i][j][2] <= bottom_vert_arr[i][j][2]):
continue
vertices.append([bottom_vert_arr[i][j]])
vertices.append([bottom_vert_arr[i+1][j]])
vertices.append([bottom_vert_arr[i][j+1]])
vertices.append([bottom_vert_arr[i+1][j+1]])
count = self.append_faces(faces, count)
# Horizontal side faces
for i in range(self.height - 1): # right
vertices.append([top_vert_arr[i][0]])
vertices.append([top_vert_arr[i+1][0]])
vertices.append([bottom_vert_arr[i][0]])
vertices.append([bottom_vert_arr[i+1][0]])
count = self.append_faces(faces, count)
max = self.width - 1
vertices.append([top_vert_arr[i+1][max]])
vertices.append([top_vert_arr[i][max]])
vertices.append([bottom_vert_arr[i+1][max]])
vertices.append([bottom_vert_arr[i][max]])
count = self.append_faces(faces, count)
# Vertical side faces
for j in range(self.width - 1):
vertices.append([top_vert_arr[0][j+1]])
vertices.append([top_vert_arr[0][j]])
vertices.append([bottom_vert_arr[0][j+1]])
vertices.append([bottom_vert_arr[0][j]])
count = self.append_faces(faces, count)
max = self.height - 1
vertices.append([top_vert_arr[max][j]])
vertices.append([top_vert_arr[max][j+1]])
vertices.append([bottom_vert_arr[max][j]])
vertices.append([bottom_vert_arr[max][j+1]])
count = self.append_faces(faces, count)
# Add the created vertices and faces to a mesh
self.create_stl_mesh(faces, vertices) self.create_stl_mesh(faces, vertices)
def make_stl_map(self): def make_stl_map(self):
@ -771,6 +706,8 @@ class fingerprint_app:
self.stl_model.export(file_obj=self.stl_filename) self.stl_model.export(file_obj=self.stl_filename)
else: else:
self.stl_model.save(self.stl_filename) self.stl_model.save(self.stl_filename)
log.print_message("Saving model to", self.stl_filename)
self.write_stl_header() self.write_stl_header()

Loading…
Cancel
Save