Separated config functionality to a separate file, added filter database and filter saving.
This commit is contained in:
120
src/config_parser.py
Normal file
120
src/config_parser.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
"""! @file config_parser.py
|
||||||
|
@brief Config parser for fingerprint filtering and 3d model generation application
|
||||||
|
@author xlanro00
|
||||||
|
"""
|
||||||
|
|
||||||
|
from os.path import exists
|
||||||
|
import json
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
|
||||||
|
def save_preset(filters, params, preset_name):
|
||||||
|
'''Save filter preset to database.
|
||||||
|
:param filters: list of filters to be saved
|
||||||
|
:param preset_name: name of preset to be saved
|
||||||
|
'''
|
||||||
|
filt = {}
|
||||||
|
# Create dictionary of filter parameters
|
||||||
|
for i, filter in enumerate(filters):
|
||||||
|
|
||||||
|
filt[i] = {}
|
||||||
|
filt[i]["name"] = filter
|
||||||
|
for key, value in params[i+1].items():
|
||||||
|
if value is not None:
|
||||||
|
filt[i][key] = value
|
||||||
|
|
||||||
|
new_filt = []
|
||||||
|
for i, filter in enumerate(filt):
|
||||||
|
new_filt.append(filt[filter])
|
||||||
|
|
||||||
|
# Store preset to database
|
||||||
|
store_to_db(new_filt, preset_name)
|
||||||
|
|
||||||
|
|
||||||
|
def store_to_db(preset, preset_name):
|
||||||
|
'''Store filter preset to database.
|
||||||
|
:param preset: dictionary of filter preset to be stored
|
||||||
|
:param preset_name: name of preset to be stored
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Create unique id for preset, this serves to avoid duplicit preset entries
|
||||||
|
id = str(hashlib.md5(str(preset).encode('utf-8')).hexdigest())[:8]
|
||||||
|
preset_name = preset_name + "_" + id
|
||||||
|
# Create json object from preset
|
||||||
|
preset = json.dumps({preset_name: preset}, indent=4)
|
||||||
|
preset = json.loads(preset)
|
||||||
|
|
||||||
|
# If database doesn't exist, create it
|
||||||
|
if not exists("db.json"):
|
||||||
|
print("Storing preset to database")
|
||||||
|
with open("db.json", 'w') as db:
|
||||||
|
json.dump(preset, db)
|
||||||
|
else:
|
||||||
|
# If database exists, load it and check if preset already exists
|
||||||
|
try:
|
||||||
|
with open("db.json", 'r') as db:
|
||||||
|
db_presets = json.load(db)
|
||||||
|
|
||||||
|
# Empty file is an error, so we don't read it
|
||||||
|
except json.decoder.JSONDecodeError:
|
||||||
|
db_presets = {}
|
||||||
|
|
||||||
|
# If preset already exists, skip it
|
||||||
|
if preset_name in db_presets:
|
||||||
|
print("Preset already exists in database, skipping")
|
||||||
|
else:
|
||||||
|
print("Storing preset to database")
|
||||||
|
db_presets.update(preset)
|
||||||
|
|
||||||
|
# Finally write the updated entries to db file
|
||||||
|
with open("db.json", 'w') as db:
|
||||||
|
json.dump(db_presets, db, indent=4)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_conf(preset_name, filters, params, config_file):
|
||||||
|
'''Parse configuration file if one was given.
|
||||||
|
Store filters and their parameters.
|
||||||
|
'''
|
||||||
|
|
||||||
|
config = json.load(open(config_file))
|
||||||
|
|
||||||
|
# Find preset in config file
|
||||||
|
if preset_name in config:
|
||||||
|
filter_array = config[preset_name]
|
||||||
|
store_to_db(filter_array, preset_name)
|
||||||
|
# Iterate over filters in preset, store them and their parameters
|
||||||
|
for i, filter in enumerate(range(len(filter_array)), start=1):
|
||||||
|
filters.append(filter_array[filter]["name"])
|
||||||
|
params[i] = {}
|
||||||
|
for attribute, value in filter_array[filter].items():
|
||||||
|
# Filter name isn't needed in here
|
||||||
|
if attribute != "name":
|
||||||
|
params[i][attribute] = value
|
||||||
|
parse_params(params[i])
|
||||||
|
print("Loaded preset: " + preset_name +
|
||||||
|
" from file: " + config_file)
|
||||||
|
else:
|
||||||
|
print("Preset not found")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_params(params):
|
||||||
|
'''Parse parameters of filters. Set to None if parameter is not given.
|
||||||
|
They are later set to default values in the filter method apply.
|
||||||
|
:param params: dictionary of filter parameters
|
||||||
|
'''
|
||||||
|
|
||||||
|
# TODO: possibly too bloated, sending all possible params to each filter
|
||||||
|
# TODO: remove unnecessary params
|
||||||
|
possible_params = {"h", "searchWindowSize", "templateWindowSize",
|
||||||
|
"ksize", "kernel", "angle",
|
||||||
|
"sigmaColor", "sigmaSpace", "diameter", "anchor", "iterations",
|
||||||
|
"op", "strength", "amount", "radius", "weight", "channelAxis",
|
||||||
|
"theta", "sigma", "lambd", "gamma", "psi", "shape", "percent",
|
||||||
|
"threshold", "maxval", "type", "margin", "color", "truncate", "patch_size", "patch_distance"}
|
||||||
|
|
||||||
|
for key in possible_params:
|
||||||
|
if params.get(key) is None:
|
||||||
|
params[key] = None
|
||||||
|
else:
|
||||||
|
params[key] = params[key]
|
||||||
|
|
74
src/main.py
74
src/main.py
@ -21,7 +21,7 @@ import trimesh.transformations as tmtra
|
|||||||
|
|
||||||
# Import custom image filter library
|
# Import custom image filter library
|
||||||
import filters as flt
|
import filters as flt
|
||||||
|
import config_parser as cp
|
||||||
|
|
||||||
class app:
|
class app:
|
||||||
'''Main class for the application.
|
'''Main class for the application.
|
||||||
@ -38,29 +38,33 @@ class app:
|
|||||||
# Parse configuration from json config file
|
# Parse configuration from json config file
|
||||||
if self.args.config:
|
if self.args.config:
|
||||||
self.config_file, self.preset_name = self.args.config
|
self.config_file, self.preset_name = self.args.config
|
||||||
self.config = json.load(open(self.config_file))
|
cp.parse_conf(self.preset_name, self.filters, self.params, self.config_file)
|
||||||
self.parse_conf()
|
|
||||||
|
|
||||||
elif self.args.filters:
|
elif self.args.filters:
|
||||||
print("No config file given, using command line arguments")
|
print("No config file given, using command line arguments")
|
||||||
i = 0
|
i = 0
|
||||||
|
|
||||||
# Otherwise expect filters from command line
|
# Otherwise expect filters from command line
|
||||||
for filter in self.args.filters:
|
for filter_part in self.args.filters:
|
||||||
if filter.find('=') == -1:
|
# If no '=' char in filter, it is a new filter name
|
||||||
# if no '=' char in filter, it is a new filter name
|
if filter_part.find('=') == -1:
|
||||||
self.filters.append(filter)
|
self.filters.append(filter_part)
|
||||||
i += 1
|
i += 1
|
||||||
self.params[i] = {} # create empty dict for params
|
self.params[i] = {} # create empty dict for params
|
||||||
|
# Otherwise it's a parameter for current filter
|
||||||
else:
|
else:
|
||||||
# else it's a parameter for current filter
|
key, value = filter_part.split('=')
|
||||||
key, value = filter.split('=')
|
|
||||||
self.params[i][key] = value
|
self.params[i][key] = value
|
||||||
self.parse_params(self.params[i])
|
|
||||||
|
cp.parse_params(self.params[i])
|
||||||
|
# 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])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print("No filters given, saving original image")
|
print("No filters given, saving original image")
|
||||||
|
|
||||||
|
# Set input and output file paths, dpi and mirror flag for easier readability
|
||||||
self.input_file = self.args.input_file
|
self.input_file = self.args.input_file
|
||||||
self.output_file = self.args.output_file
|
self.output_file = self.args.output_file
|
||||||
self.dpi = self.args.dpi
|
self.dpi = self.args.dpi
|
||||||
@ -98,10 +102,6 @@ class app:
|
|||||||
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")
|
||||||
|
|
||||||
# another boolean switch argument, this enables planar mode
|
|
||||||
#parser.add_argument('-p', '--planar', type=bool, action=ap.BooleanOptionalAction,
|
|
||||||
# help="make stl shape planar instead of curved one")
|
|
||||||
|
|
||||||
# configuration file containing presets, preset name
|
# configuration file containing presets, preset name
|
||||||
# pair argument - give both or none
|
# pair argument - give both or none
|
||||||
parser.add_argument('-c', '--config', nargs=2,
|
parser.add_argument('-c', '--config', nargs=2,
|
||||||
@ -111,51 +111,11 @@ class app:
|
|||||||
parser.add_argument('filters', type=str, nargs='*',
|
parser.add_argument('filters', type=str, nargs='*',
|
||||||
help="list of filter names and their parameters in form [filter_name1 param1=value param2=value filter_name2 param1=value...]")
|
help="list of filter names and their parameters in form [filter_name1 param1=value param2=value filter_name2 param1=value...]")
|
||||||
|
|
||||||
|
parser.add_argument('-d', '--database', nargs=1,
|
||||||
|
help='switch to store presets in config database')
|
||||||
|
|
||||||
self.args = parser.parse_args()
|
self.args = parser.parse_args()
|
||||||
|
|
||||||
def parse_params(self, params):
|
|
||||||
'''Parse parameters of filters. Set to None if parameter is not given.
|
|
||||||
They are later set to default values in the filter method apply.
|
|
||||||
:param params: dictionary of filter parameters
|
|
||||||
'''
|
|
||||||
|
|
||||||
# TODO: possibly too bloated, sending all possible params to each filter
|
|
||||||
# TODO: remove unnecessary params
|
|
||||||
possible_params = {"h", "searchWindowSize", "templateWindowSize",
|
|
||||||
"ksize", "kernel", "angle",
|
|
||||||
"sigmaColor", "sigmaSpace", "diameter", "anchor", "iterations",
|
|
||||||
"op", "strength", "amount", "radius", "weight", "channelAxis",
|
|
||||||
"theta", "sigma", "lambd", "gamma", "psi", "shape", "percent",
|
|
||||||
"threshold", "maxval", "type", "margin", "color", "truncate", "patch_size", "patch_distance"}
|
|
||||||
|
|
||||||
for key in possible_params:
|
|
||||||
if params.get(key) is None:
|
|
||||||
params[key] = None
|
|
||||||
else:
|
|
||||||
params[key] = params[key]
|
|
||||||
|
|
||||||
def parse_conf(self):
|
|
||||||
'''Parse configuration file if one was given.
|
|
||||||
Store filters and their parameters.
|
|
||||||
'''
|
|
||||||
|
|
||||||
# Find preset in config file
|
|
||||||
if self.preset_name in self.config:
|
|
||||||
filter_array = self.config[self.preset_name]
|
|
||||||
# Iterate over filters in preset, store them and their parameters
|
|
||||||
for i, filter in enumerate(range(len(filter_array)), start=1):
|
|
||||||
self.filters.append(filter_array[filter]["name"])
|
|
||||||
self.params[i] = {}
|
|
||||||
for attribute, value in filter_array[filter].items():
|
|
||||||
# Filter name isn't needed in here
|
|
||||||
if attribute != "name":
|
|
||||||
self.params[i][attribute] = value
|
|
||||||
self.parse_params(self.params[i])
|
|
||||||
print("Loaded preset: " + self.preset_name +
|
|
||||||
" from file: " + self.config_file)
|
|
||||||
else:
|
|
||||||
self.error_exit("Preset not found")
|
|
||||||
|
|
||||||
def parse_stl(self):
|
def parse_stl(self):
|
||||||
# Get stl filename
|
# Get stl filename
|
||||||
self.stl_path = self.output_file.rsplit('/', 1)[0] + '/'
|
self.stl_path = self.output_file.rsplit('/', 1)[0] + '/'
|
||||||
|
Reference in New Issue
Block a user