File: compton-convgen.py

package info (click to toggle)
compton 1-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 776 kB
  • sloc: ansic: 11,631; sh: 295; makefile: 133; python: 114
file content (161 lines) | stat: -rwxr-xr-x 4,636 bytes parent folder | download | duplicates (3)
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()