|
1 |
| -#! /usr/bin/env python3 |
| 1 | +#!/usr/bin/env python3 |
2 | 2 | # -*- coding: utf-8 -*-
|
3 | 3 | # vim:fileencoding=utf-8
|
4 | 4 |
|
5 |
| -import math, argparse |
| 5 | +import math |
| 6 | +import argparse |
| 7 | + |
6 | 8 |
|
7 | 9 | class CGError(Exception):
|
8 |
| - def __init__(self, value): |
9 |
| - self.value = value |
10 |
| - def __str__(self): |
11 |
| - return repr(self.value) |
| 10 | + '''An error in the convolution kernel generator.''' |
| 11 | + def __init__(self, desc): |
| 12 | + super().__init__(desc) |
| 13 | + |
| 14 | + |
| 15 | +class CGBadArg(CGError): |
| 16 | + '''An exception indicating an invalid argument has been passed to the |
| 17 | + convolution kernel generator.''' |
| 18 | + pass |
12 | 19 |
|
13 |
| -class CGBadArg(CGError): pass |
14 |
| -class CGInternal(CGError): pass |
15 | 20 |
|
16 | 21 | def mbuild(width, height):
|
17 |
| - """Build a NxN matrix filled with 0.""" |
18 |
| - result = list() |
19 |
| - for i in range(height): |
20 |
| - result.append(list()) |
21 |
| - for j in range(width): |
22 |
| - result[i].append(0.0) |
23 |
| - return result |
| 22 | + """Build a NxN matrix filled with 0.""" |
| 23 | + result = list() |
| 24 | + for i in range(height): |
| 25 | + result.append(list()) |
| 26 | + for j in range(width): |
| 27 | + result[i].append(0.0) |
| 28 | + return result |
| 29 | + |
24 | 30 |
|
25 | 31 | def mdump(matrix):
|
26 |
| - """Dump a matrix in natural format.""" |
27 |
| - for col in matrix: |
28 |
| - print("[ ", end = ''); |
29 |
| - for ele in col: |
30 |
| - print(format(ele, "13.6g") + ", ", end = " ") |
31 |
| - print("],") |
| 32 | + """Dump a matrix in natural format.""" |
| 33 | + for col in matrix: |
| 34 | + print("[ ", end='') |
| 35 | + for ele in col: |
| 36 | + print(format(ele, "13.6g") + ", ", end=" ") |
| 37 | + print("],") |
| 38 | + |
32 | 39 |
|
33 | 40 | def mdumpcompton(matrix):
|
34 |
| - """Dump a matrix in compton's format.""" |
35 |
| - width = len(matrix[0]) |
36 |
| - height = len(matrix) |
37 |
| - print("{},{},".format(width, height), end = '') |
38 |
| - for i in range(height): |
39 |
| - for j in range(width): |
40 |
| - if int(height / 2) == i and int(width / 2) == j: |
41 |
| - continue; |
42 |
| - print(format(matrix[i][j], ".6f"), end = ",") |
43 |
| - print() |
| 41 | + """Dump a matrix in compton's format.""" |
| 42 | + width = len(matrix[0]) |
| 43 | + height = len(matrix) |
| 44 | + print("{},{},".format(width, height), end='') |
| 45 | + for i in range(height): |
| 46 | + for j in range(width): |
| 47 | + if int(height / 2) == i and int(width / 2) == j: |
| 48 | + continue |
| 49 | + print(format(matrix[i][j], ".6f"), end=",") |
| 50 | + print() |
| 51 | + |
44 | 52 |
|
45 | 53 | def mnormalize(matrix):
|
46 |
| - """Scale a matrix according to the value in the center.""" |
47 |
| - width = len(matrix[0]) |
48 |
| - height = len(matrix) |
49 |
| - factor = 1.0 / matrix[int(height / 2)][int(width / 2)] |
50 |
| - if 1.0 == factor: return matrix |
51 |
| - for i in range(height): |
52 |
| - for j in range(width): |
53 |
| - matrix[i][j] *= factor |
54 |
| - return matrix |
| 54 | + """Scale a matrix according to the value in the center.""" |
| 55 | + width = len(matrix[0]) |
| 56 | + height = len(matrix) |
| 57 | + factor = 1.0 / matrix[int(height / 2)][int(width / 2)] |
| 58 | + if 1.0 == factor: |
| 59 | + return matrix |
| 60 | + for i in range(height): |
| 61 | + for j in range(width): |
| 62 | + matrix[i][j] *= factor |
| 63 | + return matrix |
| 64 | + |
55 | 65 |
|
56 | 66 | def mmirror4(matrix):
|
57 |
| - """Do a 4-way mirroring on a matrix from top-left corner.""" |
58 |
| - width = len(matrix[0]) |
59 |
| - height = len(matrix) |
60 |
| - for i in range(height): |
61 |
| - for j in range(width): |
62 |
| - x = min(i, height - 1 - i) |
63 |
| - y = min(j, width - 1 - j) |
64 |
| - matrix[i][j] = matrix[x][y] |
65 |
| - return matrix |
| 67 | + """Do a 4-way mirroring on a matrix from top-left corner.""" |
| 68 | + width = len(matrix[0]) |
| 69 | + height = len(matrix) |
| 70 | + for i in range(height): |
| 71 | + for j in range(width): |
| 72 | + x = min(i, height - 1 - i) |
| 73 | + y = min(j, width - 1 - j) |
| 74 | + matrix[i][j] = matrix[x][y] |
| 75 | + return matrix |
| 76 | + |
66 | 77 |
|
67 | 78 | def gen_gaussian(width, height, factors):
|
68 |
| - """Build a Gaussian blur kernel.""" |
| 79 | + """Build a Gaussian blur kernel.""" |
| 80 | + |
| 81 | + if width != height: |
| 82 | + raise CGBadArg("Cannot build an uneven Gaussian blur kernel.") |
69 | 83 |
|
70 |
| - if width != height: |
71 |
| - raise CGBadArg("Cannot build an uneven Gaussian blur kernel.") |
| 84 | + size = width |
| 85 | + sigma = float(factors.get('sigma', 0.84089642)) |
72 | 86 |
|
73 |
| - size = width |
74 |
| - sigma = float(factors.get('sigma', 0.84089642)) |
| 87 | + result = mbuild(size, size) |
| 88 | + for i in range(int(size / 2) + 1): |
| 89 | + for j in range(int(size / 2) + 1): |
| 90 | + diffx = i - int(size / 2) |
| 91 | + diffy = j - int(size / 2) |
| 92 | + result[i][j] = 1.0 / (2 * math.pi * sigma) \ |
| 93 | + * pow(math.e, - (diffx * diffx + diffy * diffy) \ |
| 94 | + / (2 * sigma * sigma)) |
| 95 | + mnormalize(result) |
| 96 | + mmirror4(result) |
75 | 97 |
|
76 |
| - result = mbuild(size, size) |
77 |
| - for i in range(int(size / 2) + 1): |
78 |
| - for j in range(int(size / 2) + 1): |
79 |
| - diffx = i - int(size / 2); |
80 |
| - diffy = j - int(size / 2); |
81 |
| - result[i][j] = 1.0 / (2 * math.pi * sigma) * pow(math.e, - (diffx * diffx + diffy * diffy) / (2 * sigma * sigma)) |
82 |
| - mnormalize(result) |
83 |
| - mmirror4(result) |
| 98 | + return result |
84 | 99 |
|
85 |
| - return result |
86 | 100 |
|
87 | 101 | def gen_box(width, height, factors):
|
88 |
| - """Build a box blur kernel.""" |
89 |
| - result = mbuild(width, height) |
90 |
| - for i in range(height): |
91 |
| - for j in range(width): |
92 |
| - result[i][j] = 1.0 |
93 |
| - return result |
94 |
| - |
95 |
| -def gen_invalid(width, height, factors): |
96 |
| - raise CGBadArg("Unknown kernel type.") |
97 |
| - |
98 |
| -def args_readfactors(lst): |
99 |
| - """Parse the factor arguments.""" |
100 |
| - factors = dict() |
101 |
| - if lst: |
102 |
| - for s in lst: |
103 |
| - res = s.partition('=') |
104 |
| - if not res[0]: |
105 |
| - raise CGBadArg("Factor has no key.") |
106 |
| - if not res[2]: |
107 |
| - raise CGBadArg("Factor has no value.") |
108 |
| - factors[res[0]] = float(res[2]) |
109 |
| - return factors |
110 |
| - |
111 |
| -parser = argparse.ArgumentParser(description='Build a convolution kernel.') |
112 |
| -parser.add_argument('type', help='Type of convolution kernel. May be "gaussian" (factor sigma = 0.84089642) or "box".') |
113 |
| -parser.add_argument('width', type=int, help='Width of convolution kernel. Must be an odd number.') |
114 |
| -parser.add_argument('height', nargs='?', type=int, help='Height of convolution kernel. Must be an odd number. Equals to width if omitted.') |
115 |
| -parser.add_argument('-f', '--factor', nargs='+', help='Factors of the convolution kernel, in name=value format.') |
116 |
| -parser.add_argument('--dump-compton', action='store_true', help='Dump in compton format.') |
117 |
| -args = parser.parse_args() |
118 |
| - |
119 |
| -width = args.width |
120 |
| -height = args.height |
121 |
| -if not height: |
122 |
| - height = width |
123 |
| -if not (width > 0 and height > 0): |
124 |
| - raise CGBadArg("Invalid width/height.") |
125 |
| -factors = args_readfactors(args.factor) |
126 |
| - |
127 |
| -funcs = dict(gaussian = gen_gaussian, box = gen_box) |
128 |
| -matrix = (funcs.get(args.type, gen_invalid))(width, height, factors) |
129 |
| -if args.dump_compton: |
130 |
| - mdumpcompton(matrix) |
131 |
| -else: |
132 |
| - mdump(matrix) |
| 102 | + """Build a box blur kernel.""" |
| 103 | + result = mbuild(width, height) |
| 104 | + for i in range(height): |
| 105 | + for j in range(width): |
| 106 | + result[i][j] = 1.0 |
| 107 | + return result |
| 108 | + |
| 109 | + |
| 110 | +def _gen_invalid(width, height, factors): |
| 111 | + '''Handle a convolution kernel generation request of an unrecognized type.''' |
| 112 | + raise CGBadArg("Unknown kernel type.") |
| 113 | + |
| 114 | + |
| 115 | +def _args_readfactors(lst): |
| 116 | + """Parse the factor arguments.""" |
| 117 | + factors = dict() |
| 118 | + if lst: |
| 119 | + for s in lst: |
| 120 | + res = s.partition('=') |
| 121 | + if not res[0]: |
| 122 | + raise CGBadArg("Factor has no key.") |
| 123 | + if not res[2]: |
| 124 | + raise CGBadArg("Factor has no value.") |
| 125 | + factors[res[0]] = float(res[2]) |
| 126 | + return factors |
| 127 | + |
| 128 | + |
| 129 | +def _parse_args(): |
| 130 | + '''Parse the command-line arguments.''' |
| 131 | + |
| 132 | + parser = argparse.ArgumentParser(description='Build a convolution kernel.') |
| 133 | + parser.add_argument('type', help='Type of convolution kernel. May be "gaussian" (factor sigma = 0.84089642) or "box".') |
| 134 | + parser.add_argument('width', type=int, help='Width of convolution kernel. Must be an odd number.') |
| 135 | + parser.add_argument('height', nargs='?', type=int, help='Height of convolution kernel. Must be an odd number. Equals to width if omitted.') |
| 136 | + parser.add_argument('-f', '--factor', nargs='+', help='Factors of the convolution kernel, in name=value format.') |
| 137 | + parser.add_argument('--dump-compton', action='store_true', help='Dump in compton format.') |
| 138 | + return parser.parse_args() |
| 139 | + |
| 140 | + |
| 141 | +def _main(): |
| 142 | + args = _parse_args() |
| 143 | + |
| 144 | + width = args.width |
| 145 | + height = args.height |
| 146 | + if not height: |
| 147 | + height = width |
| 148 | + if not (width > 0 and height > 0): |
| 149 | + raise CGBadArg("Invalid width/height.") |
| 150 | + factors = _args_readfactors(args.factor) |
| 151 | + |
| 152 | + funcs = dict(gaussian=gen_gaussian, box=gen_box) |
| 153 | + matrix = (funcs.get(args.type, _gen_invalid))(width, height, factors) |
| 154 | + if args.dump_compton: |
| 155 | + mdumpcompton(matrix) |
| 156 | + else: |
| 157 | + mdump(matrix) |
| 158 | + |
| 159 | + |
| 160 | +if __name__ == '__main__': |
| 161 | + _main() |
0 commit comments