#! /usr/bin/env python3 # -*- coding: utf-8 -*- # vim:fileencoding=utf-8 import math, argparse class CGError(Exception): def __init__(self, value): self.value = value def __str__(self): return repr(self.value) class CGBadArg(CGError): pass class CGInternal(CGError): pass def mbuild(width, height): """Build a NxN matrix filled with 0.""" result = list() for i in range(height): result.append(list()) for j in range(width): result[i].append(0.0) return result def mdump(matrix): """Dump a matrix in natural format.""" for col in matrix: print("[ ", end = ''); for ele in col: print(format(ele, "13.6g") + ", ", end = " ") print("],") def mdumpcompton(matrix): """Dump a matrix in compton's format.""" width = len(matrix[0]) height = len(matrix) print("{},{},".format(width, height), end = '') for i in range(height): for j in range(width): if int(height / 2) == i and int(width / 2) == j: continue; print(format(matrix[i][j], ".6f"), end = ",") print() def mnormalize(matrix): """Scale a matrix according to the value in the center.""" width = len(matrix[0]) height = len(matrix) factor = 1.0 / matrix[int(height / 2)][int(width / 2)] if 1.0 == factor: return matrix for i in range(height): for j in range(width): matrix[i][j] *= factor return matrix def mmirror4(matrix): """Do a 4-way mirroring on a matrix from top-left corner.""" width = len(matrix[0]) height = len(matrix) for i in range(height): for j in range(width): x = min(i, height - 1 - i) y = min(j, width - 1 - j) matrix[i][j] = matrix[x][y] return matrix def gen_gaussian(width, height, factors): """Build a Gaussian blur kernel.""" if width != height: raise CGBadArg("Cannot build an uneven Gaussian blur kernel.") size = width sigma = float(factors.get('sigma', 0.84089642)) result = mbuild(size, size) for i in range(int(size / 2) + 1): for j in range(int(size / 2) + 1): diffx = i - int(size / 2); diffy = j - int(size / 2); result[i][j] = 1.0 / (2 * math.pi * sigma) * pow(math.e, - (diffx * diffx + diffy * diffy) / (2 * sigma * sigma)) mnormalize(result) mmirror4(result) return result def gen_box(width, height, factors): """Build a box blur kernel.""" result = mbuild(width, height) for i in range(height): for j in range(width): result[i][j] = 1.0 return result def gen_invalid(width, height, factors): raise CGBadArg("Unknown kernel type.") def args_readfactors(lst): """Parse the factor arguments.""" factors = dict() if lst: for s in lst: res = s.partition('=') if not res[0]: raise CGBadArg("Factor has no key.") if not res[2]: raise CGBadArg("Factor has no value.") factors[res[0]] = float(res[2]) return factors parser = argparse.ArgumentParser(description='Build a convolution kernel.') parser.add_argument('type', help='Type of convolution kernel. May be "gaussian" (factor sigma = 0.84089642) or "box".') parser.add_argument('width', type=int, help='Width of convolution kernel. Must be an odd number.') parser.add_argument('height', nargs='?', type=int, help='Height of convolution kernel. Must be an odd number. Equals to width if omitted.') parser.add_argument('-f', '--factor', nargs='+', help='Factors of the convolution kernel, in name=value format.') parser.add_argument('--dump-compton', action='store_true', help='Dump in compton format.') args = parser.parse_args() width = args.width height = args.height if not height: height = width if not (width > 0 and height > 0): raise CGBadArg("Invalid width/height.") factors = args_readfactors(args.factor) funcs = dict(gaussian = gen_gaussian, box = gen_box) matrix = (funcs.get(args.type, gen_invalid))(width, height, factors) if args.dump_compton: mdumpcompton(matrix) else: mdump(matrix)