diff --git a/src/config_parser.py b/src/config_parser.py new file mode 100644 index 0000000..35c09e5 --- /dev/null +++ b/src/config_parser.py @@ -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] + diff --git a/src/main.py b/src/main.py index 891f13a..cb97d34 100644 --- a/src/main.py +++ b/src/main.py @@ -21,7 +21,7 @@ import trimesh.transformations as tmtra # Import custom image filter library import filters as flt - +import config_parser as cp class app: '''Main class for the application. @@ -38,29 +38,33 @@ class app: # Parse configuration from json config file if self.args.config: self.config_file, self.preset_name = self.args.config - self.config = json.load(open(self.config_file)) - self.parse_conf() + cp.parse_conf(self.preset_name, self.filters, self.params, self.config_file) elif self.args.filters: print("No config file given, using command line arguments") i = 0 # Otherwise expect filters from command line - for filter in self.args.filters: - if filter.find('=') == -1: - # if no '=' char in filter, it is a new filter name - self.filters.append(filter) + for filter_part in self.args.filters: + # If no '=' char in filter, it is a new filter name + if filter_part.find('=') == -1: + self.filters.append(filter_part) i += 1 self.params[i] = {} # create empty dict for params + # Otherwise it's a parameter for current filter else: - # else it's a parameter for current filter - key, value = filter.split('=') + key, value = filter_part.split('=') 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: 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.output_file = self.args.output_file self.dpi = self.args.dpi @@ -98,10 +102,6 @@ class app: parser.add_argument('-s', "--stl", type=str, nargs='*', 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 # pair argument - give both or none parser.add_argument('-c', '--config', nargs=2, @@ -111,50 +111,10 @@ class app: 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...]") - 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. - ''' + parser.add_argument('-d', '--database', nargs=1, + help='switch to store presets in config database') - # 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") + self.args = parser.parse_args() def parse_stl(self): # Get stl filename