1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# vim:fileencoding=utf-8
import math
import argparse
class CGError(Exception):
'''An error in the convolution kernel generator.'''
def __init__(self, desc):
super().__init__(desc)
class CGBadArg(CGError):
'''An exception indicating an invalid argument has been passed to the
convolution kernel generator.'''
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):
'''Handle a convolution kernel generation request of an unrecognized type.'''
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
def _parse_args():
'''Parse the command-line arguments.'''
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.')
return parser.parse_args()
def _main():
args = _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)
if __name__ == '__main__':
_main()
|