Added functionality for finger mapping, adjusted mapping mode parameters.

master
Rostislav Lán 2 years ago
parent 2302428e05
commit 8af2e88b35

@ -7,7 +7,6 @@
import argparse as ap
from os.path import exists
import hashlib
import math
# Libraries for image processing
import numpy as np
@ -16,11 +15,14 @@ import cv2 as cv
from stl import mesh
import trimesh
import trimesh.transformations as tmtra
import trimesh.remesh as tmrem
# Import custom image filter library
import filters as flt
import config_parser as cp
import log
import math
class fingerprint_app:
'''Main class for the application.
@ -37,7 +39,8 @@ class fingerprint_app:
# Parse configuration from json config file
if self.args.config:
self.config_file, self.preset_name = self.args.config
cp.parse_conf(self.preset_name, self.filters, self.params, self.config_file)
cp.parse_conf(self.preset_name, self.filters,
self.params, self.config_file)
elif self.args.filters:
filter_index = 0
@ -58,10 +61,11 @@ class fingerprint_app:
self.params[filter_index][key] = value
cp.parse_params(self.params[filter_index])
# If database flag is set, save filters to database as a new preset
if self.args.database:
cp.save_preset(self.filters, self.params, self.args.database[0])
cp.save_preset(self.filters, self.params,
self.args.database[0])
else:
log.print_message("No filters given, saving original image")
@ -76,18 +80,18 @@ class fingerprint_app:
self.run_filtering()
else:
log.error_exit("Input file " + self.input_file +
" does not exist")
" does not exist")
if self.args.stl:
self.parse_stl()
def parse_arguments(self):
'''Parse arguments from command line using argparse library.
'''
parser = ap.ArgumentParser(prog='main.py',
description='Program for transforming a 2D image into 3D fingerprint.',
usage='%(prog)s [-h] [-m | --mirror | --no-mirror] [-p] input_file output_file dpi ([-c | --config config_file preset] | [filters ...]) [-s | --stl p height_line height_base | --stl c height_line curv_rate_x curv_rate_y | --stl m height_line]')
usage='%(prog)s [-h] [-m | --mirror | --no-mirror] input_file output_file dpi ([-c | --config config_file preset] | [filters ...]) [-s | --stl p height_line height_base | --stl c height_line curv_rate_x curv_rate_y | --stl m height_line]')
# positional arguments
parser.add_argument("input_file", type=str, help="input file path")
@ -99,7 +103,7 @@ class fingerprint_app:
help="switch to mirror input image")
# another boolean switch argument, this time with value, name of the new file and dimensions
# TODO: behaves absolutely randomly for some reason
# TODO: behaves absolutely randomly for some reason, fix it
parser.add_argument('-s', "--stl", type=str, nargs='*',
help="create stl model from processed image")
@ -135,7 +139,7 @@ class fingerprint_app:
self.height_line = float(self.args.stl[1])
self.height_base = float(self.args.stl[2])
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.args.stl[0] == 'c':
self.mode = "curved"
@ -146,24 +150,28 @@ class fingerprint_app:
self.height_base = 10
self.curv_rate_x = 2
self.curv_rate_y = 6
log.print_message("Warning: Too few arguments, using default values (2mm lines, curvature 0.5 on x, 0.5 on y)")
log.print_message(
"Warning: Too few arguments, using default values (2mm lines, curvature 0.5 on x, 0.5 on y)")
else:
self.height_line = float(self.args.stl[1])
self.height_base = float(self.args.stl[2])
self.curv_rate_x = float(self.args.stl[3])
self.curv_rate_y = float(self.args.stl[4])
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.args.stl[0] == 'm':
self.mode = "mapped"
# TODO: add default values for mapped mode, add finger model?
if len(self.args.stl) < 2:
if len(self.args.stl) < 6:
log.print_message(
"Warning: Too few arguments, using default values")
else:
self.height_line = float(self.args.stl[1])
self.finger_model = self.args.stl[2]
self.iter = int(self.args.stl[2])
self.finger_x = float(self.args.stl[3])
self.finger_y = float(self.args.stl[4])
self.finger_z = float(self.args.stl[5])
else:
log.error_exit("Unrecognized generation mode")
@ -219,13 +227,16 @@ class fingerprint_app:
if len(self.filters) != 0:
for i, filter_name in enumerate(self.filters):
# Get filter class from filter.py, use the apply method
filter = getattr(flt, filter_name)
try:
filter = getattr(flt, filter_name)
except AttributeError:
log.error_exit("Filter " + filter_name + " not found")
log.print_message("Applying filter:", filter_name)
for param in self.params[i+1]:
if self.params[i+1][param] is not None:
log.print_message("\twith parameter", param,
"=", str(self.params[i+1][param]))
"=", str(self.params[i+1][param]))
filter.apply(self, self.params[i+1])
else:
pass
@ -260,12 +271,8 @@ class fingerprint_app:
self.make_stl_curved()
elif self.mode == "mapped":
# TODO: find a more suitable finger model
self.make_stl_map()
else:
log.error_exit("Incorrect stl generation mode")
plt.show()
self.save_stl()
log.print_message("Saving model to", self.stl_filename)
@ -297,8 +304,8 @@ class fingerprint_app:
if self.mode == "mapped":
if self.height_line <= 0:
log.error_exit("Line height must be positive")
if not exists(self.finger_model):
log.error_exit("Finger model file does not exist")
if self.iter < 0:
log.error_exit("Number of iterations must be positive orr zero")
self.height_base = 0
# TODO: curved height base could be done here?
@ -367,8 +374,11 @@ class fingerprint_app:
param_list.append(str(self.curv_rate_y))
if self.mode == "mapped":
#TODO
pass
param_list.append(str(self.height_line))
param_list.append(str(self.iter))
param_list.append(str(self.finger_x))
param_list.append(str(self.finger_y))
param_list.append(str(self.finger_z))
if self.mode == "planar":
param_list.append("P")
@ -384,7 +394,8 @@ class fingerprint_app:
# fill the rest with the ending char to rewrite any leftover header
# this is done for easier parsing of the header
self.param_string = "\\".join(param_list)
self.param_string = self.param_string + "\n" * (80 - len(self.param_string))
self.param_string = self.param_string + \
"\n" * (80 - len(self.param_string))
# hash the param string to get unique ID, this will be put in filename and on the back of the model
# not using built-in hash function because it's seed cannot be set to constant number
@ -435,7 +446,6 @@ class fingerprint_app:
plt.close()
# TODO: maybe don't use nested for loops, use numpy?
# TODO: this is very badly written, fix it
# TODO: this does not always work, fix it
# add the bottom array
@ -498,7 +508,6 @@ class fingerprint_app:
vertices = []
faces = []
# TODO: don't like this, could be done using numpy vectorisation?
# Iterate over all vertices, create faces
for i in range(self.height - 1):
for j in range(self.width - 1):
@ -564,7 +573,10 @@ class fingerprint_app:
self.create_stl_mesh(faces, vertices)
def make_stl_curved(self):
'''Map fingerprint to finger model.
'''Compute curved surface.
Create mesh from meshgrid.
Create vertices from meshgrid, add depth values from image.
Create faces from vertices. Add vectors and faces to the model.
'''
# TODO: this might be done in a better way, comment
@ -672,50 +684,82 @@ class fingerprint_app:
def make_stl_map(self):
'''Map fingerprint to a given finger model.
Experimental, does not work very well...
'''
# TODO: maybe use trimesh.update_vertices
log.print_message("Mapping to finger")
# TODO: try to merge meshes? or stl files?
# trimesh library?
finger = trimesh.load(self.finger_model)
# TODO: connect with curved generation
# manually tried to allign two models and concatenated
fingerprint = trimesh.load('res/0-norm1_e5f52c0fe1.stl')
angle = math.pi
dir = [0, 0, 1]
center = [0, 0, 0]
mat = tmtra.rotation_matrix(angle, dir, center)
finger.apply_transform(mat)
angle = -3 / 4 * math.pi
dir = [1, 0, 0]
center = [0, 0, 0]
mat = tmtra.rotation_matrix(angle, dir, center)
finger.apply_transform(mat)
# TODO: random values that works for one finger model...
# TODO: this can later be modified to map finger to the core of the finger.
x = 2 + (self.width * 25.4 / self.dpi / 2)
y = 5 + (self.height * 25.4 / self.dpi / 2)
z = 20
mat = tmtra.translation_matrix(
[x, y, z])
finger.apply_transform(mat)
self.stl_model = trimesh.util.concatenate([finger, fingerprint])
# Conversion constants for mm and pixels
mm2px = self.dpi/25.4
px2mm = 25.4/self.dpi
# Finds the image pixel closest to finger vertice in 2D plane
def find_nearest(ver1, ver2, img):
searched_point = np.array([ver1, ver2])
min1 = math.floor(ver1*mm2px)
max1 = math.ceil(ver1*mm2px)
min2 = math.floor(ver2*mm2px)
max2 = math.ceil(ver2*mm2px)
min_dist_point = img[min2][min1]
for i in range(min2, max2 - 1):
for j in range(min1, max1 - 1):
if np.linalg.norm(img[i][j] - searched_point) < min_dist_point:
min_dist_point = img[i][j]
return min_dist_point
# Load the finger model
finger = trimesh.load("res/finger-mod.stl")
# Implicitly translate it to match middle of the fingerprint
# Later this can be modified
x = (self.width * px2mm / 2) + self.finger_x
y = (self.height * px2mm / 2) + self.finger_y
z = self.finger_z
matrix = tmtra.translation_matrix([x, y, z])
finger.apply_transform(matrix)
# Subdivide the finger mesh to allow for more precision
vertices, faces = tmrem.subdivide_loop(
finger.vertices, finger.faces, iterations=self.iter)
# For logging progress
c = 0
u = 0
for k, vertice in enumerate(vertices):
# Skip vertices under plane xy
# also skip vertices under the fingerprint image,
# they are all unused
if vertice[2] < 0 or vertice[1] > self.height * px2mm:
u += 1
continue
# This is the easiest way to avoid indexing errors
# Those errors are caused by vertices outside of the image
# When this occurs, input parameters need to be adjusted
try:
# Find the closest point in the image
# To the 2D image projection of vertice, add its value
point = find_nearest(vertice[0], vertice[1], self.img)
except IndexError:
log.error_exit("Fingerprint image is outside of the finger model")
vertices[k][2] += point
# Prints out generation progress
if k % 1000 == 0:
percentage = round(k/len(vertices) * 100, 2)
if percentage > c:
log.print_message("Carving finger: " + str(c) + "%")
c += 10
self.stl_model = trimesh.Trimesh(vertices, faces)
log.print_message("Carving finger finished")
print("Unused vertices: " + str(u))
def save_stl(self):
'''Save final mesh to stl file.
'''
# create output file name, save it and write header with file info
# Create output file name, save it and write header with file info
self.stl_filename = self.output_file.split(
".")[0] + "_" + self.id + ".stl"
if (self.mode == "mapped"):

Loading…
Cancel
Save