aboalaa147 commited on
Commit
712d0ef
·
verified ·
1 Parent(s): da2258e

Upload 2 files

Browse files
Files changed (2) hide show
  1. bresenham.py +109 -0
  2. strings.py +182 -0
bresenham.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # Bresenham line algorithm
3
+ # https://gist.github.com/flags/1132363
4
+ class bresenham:
5
+ def __init__(self, start, end):
6
+ self.start = list(start)
7
+ self.end = list(end)
8
+ self.path = []
9
+
10
+ self.steep = abs(self.end[1]-self.start[1]) > abs(self.end[0]-self.start[0])
11
+
12
+ if self.steep:
13
+ self.start = self.swap(self.start[0],self.start[1])
14
+ self.end = self.swap(self.end[0],self.end[1])
15
+
16
+ if self.start[0] > self.end[0]:
17
+ _x0 = int(self.start[0])
18
+ _x1 = int(self.end[0])
19
+ self.start[0] = _x1
20
+ self.end[0] = _x0
21
+
22
+ _y0 = int(self.start[1])
23
+ _y1 = int(self.end[1])
24
+ self.start[1] = _y1
25
+ self.end[1] = _y0
26
+
27
+ dx = self.end[0] - self.start[0]
28
+ dy = abs(self.end[1] - self.start[1])
29
+ error = 0
30
+ derr = dy/float(dx)
31
+
32
+ ystep = 0
33
+ y = self.start[1]
34
+
35
+ if self.start[1] < self.end[1]: ystep = 1
36
+ else: ystep = -1
37
+
38
+ for x in range(self.start[0],self.end[0]+1):
39
+ if self.steep:
40
+ self.path.append((y,x))
41
+ else:
42
+ self.path.append((x,y))
43
+
44
+ error += derr
45
+
46
+ if error >= 0.5:
47
+ y += ystep
48
+ error -= 1.0
49
+
50
+ def swap(self,n1,n2):
51
+ return [n2,n1]
52
+
53
+ def test():
54
+ l = bresenham([8,1],[6,4])
55
+ print(l.path)
56
+
57
+ map = []
58
+ for x in range(0,15):
59
+ yc = []
60
+ for y in range(0,15):
61
+ yc.append('#')
62
+ map.append(yc)
63
+
64
+ for pos in l.path:
65
+ map[pos[0]][pos[1]] = '.'
66
+
67
+ for y in range(0,15):
68
+ for x in range(0,15):
69
+ print(map[x][y], end=' ')
70
+ print()
71
+
72
+
73
+ # Bresenham circle algorithm
74
+ # https://www.daniweb.com/programming/software-development/threads/321181/python-bresenham-circle-arc-algorithm
75
+ def circle(radius):
76
+ # init vars
77
+ switch = 3 - (2 * radius)
78
+ points = set()
79
+ x = 0
80
+ y = radius
81
+ # first quarter/octant starts clockwise at 12 o'clock
82
+ while x <= y:
83
+ # first quarter first octant
84
+ points.add((x,-y))
85
+ # first quarter 2nd octant
86
+ points.add((y,-x))
87
+ # second quarter 3rd octant
88
+ points.add((y,x))
89
+ # second quarter 4.octant
90
+ points.add((x,y))
91
+ # third quarter 5.octant
92
+ points.add((-x,y))
93
+ # third quarter 6.octant
94
+ points.add((-y,x))
95
+ # fourth quarter 7.octant
96
+ points.add((-y,-x))
97
+ # fourth quarter 8.octant
98
+ points.add((-x,-y))
99
+ if switch < 0:
100
+ switch = switch + (4 * x) + 6
101
+ else:
102
+ switch = switch + (4 * (x - y)) + 10
103
+ y = y - 1
104
+ x = x + 1
105
+ return points
106
+
107
+
108
+ if __name__ == "__main__":
109
+ test()
strings.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import numpy as np
3
+ import scipy
4
+ import scipy.sparse
5
+ import scipy.sparse.linalg
6
+ from imageio import imread, imsave
7
+ from skimage.transform import resize as imresize
8
+ from skimage.color import rgb2gray
9
+
10
+ import math
11
+ from collections import defaultdict
12
+
13
+ from bresenham import *
14
+
15
+
16
+ def image(filename, size):
17
+ img = imresize(rgb2gray(imread(filename)), (size, size))
18
+ return img
19
+
20
+
21
+ def build_arc_adjecency_matrix(n, radius):
22
+ print("building sparse adjecency matrix")
23
+ hooks = np.array([[math.cos(np.pi*2*i/n), math.sin(np.pi*2*i/n)] for i in range(n)])
24
+ hooks = (radius * hooks).astype(int)
25
+ edge_codes = []
26
+ row_ind = []
27
+ col_ind = []
28
+ for i, ni in enumerate(hooks):
29
+ for j, nj in enumerate(hooks[i+1:], start=i+1):
30
+ edge_codes.append((i, j))
31
+ pixels = bresenham(ni, nj).path
32
+ edge = []
33
+ for pixel in pixels:
34
+ pixel_code = (pixel[1]+radius)*(radius*2+1) + (pixel[0]+radius)
35
+ edge.append(pixel_code)
36
+ row_ind += edge
37
+ col_ind += [len(edge_codes)-1] * len(edge)
38
+ # creating the edge-pixel adjecency matrix:
39
+ # rows are indexed with pixel codes, columns are indexed with edge codes.
40
+ sparse = scipy.sparse.csr_matrix(([1.0]*len(row_ind), (row_ind, col_ind)), shape=((2*radius+1)*(2*radius+1), len(edge_codes)))
41
+ return sparse, hooks, edge_codes
42
+
43
+
44
+ def build_circle_adjecency_matrix(radius, small_radius):
45
+ print("building sparse adjecency matrix")
46
+ edge_codes = []
47
+ row_ind = []
48
+ col_ind = []
49
+ pixels = circle(small_radius)
50
+ for i, cx in enumerate(range(-radius+small_radius+1, radius-small_radius-1, 1)):
51
+ for j, cy in enumerate(range(-radius+small_radius+1, radius-small_radius-1, 1)):
52
+ edge_codes.append((i, j))
53
+ edge = []
54
+ for pixel in pixels:
55
+ px, py = cx+pixel[0], cy+pixel[1]
56
+ pixel_code = (py+radius)*(radius*2+1) + (px+radius)
57
+ edge.append(pixel_code)
58
+ row_ind += edge
59
+ col_ind += [len(edge_codes)-1] * len(edge)
60
+ # creating the edge-pixel adjecency matrix:
61
+ # rows are indexed with pixel codes, columns are indexed with edge codes.
62
+ sparse = scipy.sparse.csr_matrix(([1.0]*len(row_ind), (row_ind, col_ind)), shape=((2*radius+1)*(2*radius+1), len(edge_codes)))
63
+ hooks = []
64
+ return sparse, hooks, edge_codes
65
+
66
+
67
+ def build_image_vector(img, radius):
68
+ # representing the input image as a sparse column vector of pixels:
69
+ assert img.shape[0] == img.shape[1]
70
+ img_size = img.shape[0]
71
+ row_ind = []
72
+ col_ind = []
73
+ data = []
74
+ for y, line in enumerate(img):
75
+ for x, pixel_value in enumerate(line):
76
+ global_x = x - img_size//2
77
+ global_y = y - img_size//2
78
+ pixel_code = (global_y+radius)*(radius*2+1) + (global_x+radius)
79
+ data.append(float(pixel_value))
80
+ row_ind.append(pixel_code)
81
+ col_ind.append(0)
82
+ sparse_b = scipy.sparse.csr_matrix((data, (row_ind, col_ind)), shape=((2*radius+1)*(2*radius+1), 1))
83
+ return sparse_b
84
+
85
+
86
+ def reconstruct(x, sparse, radius):
87
+ b_approx = sparse.dot(x)
88
+ b_image = b_approx.reshape((2*radius+1, 2*radius+1))
89
+ b_image = np.clip(b_image, 0, 255)
90
+ return b_image
91
+
92
+
93
+ def reconstruct_and_save(x, sparse, radius, filename):
94
+ brightness_correction = 1.2
95
+ b_image = reconstruct(x * brightness_correction, sparse, radius)
96
+ imsave(filename, b_image)
97
+
98
+
99
+ def dump_arcs(solution, hooks, edge_codes, filename):
100
+ f = open(filename, "w")
101
+ n = len(hooks)
102
+ print(n, file=f)
103
+ for i, (x, y) in enumerate(hooks):
104
+ print("%d\t%f\t%f" % (i, x, y), file=f)
105
+ print(file=f)
106
+ assert len(edge_codes) == len(solution)
107
+ for (i, j), value in zip(edge_codes, solution):
108
+ if value==0:
109
+ continue
110
+ # int values are shown as ints.
111
+ if value==int(value):
112
+ value = int(value)
113
+ print("%d\t%d\t%s" % (i, j, str(value)), file=f)
114
+ f.close()
115
+
116
+
117
+ def main():
118
+ filename, output_prefix = sys.argv[1:]
119
+
120
+ n = 180
121
+ radius = 250
122
+
123
+ sparse, hooks, edge_codes = build_arc_adjecency_matrix(n, radius)
124
+ # sparse, hooks, edge_codes = build_circle_adjecency_matrix(radius, 10)
125
+
126
+ # square image with same center as the circle, sides are 75% of circle diameter.
127
+ shrinkage = 0.75
128
+ img = image(filename, int(radius * 2 * shrinkage))
129
+ sparse_b = build_image_vector(img, radius)
130
+ # imsave(output_prefix+"-original.png", sparse_b.todense().reshape((2*radius+1, 2*radius+1)))
131
+
132
+ # finding the solution, a weighting of edges:
133
+ print("solving linear system")
134
+ # note the .todense(). for some reason the sparse version did not work.
135
+ result = scipy.sparse.linalg.lsqr(sparse, np.array(sparse_b.todense()).flatten())
136
+ print("done")
137
+ # x, istop, itn, r1norm, r2norm, anorm, acond, arnorm = result
138
+ x = result[0]
139
+
140
+ reconstruct_and_save(x, sparse, radius, output_prefix+"-allow-negative.png")
141
+
142
+ # negative values are clipped, they are physically unrealistic.
143
+ x = np.clip(x, 0, 1e6)
144
+
145
+ reconstruct_and_save(x, sparse, radius, output_prefix+"-unquantized.png")
146
+ dump_arcs(x, hooks, edge_codes, output_prefix+"-unquantized.txt")
147
+
148
+ # quantizing:
149
+ quantization_level = 30 # 50 is already quite good. None means no quantization.
150
+ # clip values larger than clip_factor times maximum.
151
+ # (The long tail does not add too much to percieved quality.)
152
+ clip_factor = 0.3
153
+ if quantization_level is not None:
154
+ max_edge_weight_orig = np.max(x)
155
+ x_quantized = (x / np.max(x) * quantization_level).round()
156
+ x_quantized = np.clip(x_quantized, 0, int(np.max(x_quantized) * clip_factor))
157
+ # scale it back:
158
+ x = x_quantized / quantization_level * max_edge_weight_orig
159
+ dump_arcs(x_quantized, hooks, edge_codes, output_prefix+".txt")
160
+
161
+ reconstruct_and_save(x, sparse, radius, output_prefix+".png")
162
+
163
+
164
+ if quantization_level is not None:
165
+ arc_count = 0
166
+ total_distance = 0.0
167
+ hist = defaultdict(int)
168
+ for edge_code, multiplicity in enumerate(x_quantized):
169
+ multiplicity = int(multiplicity)
170
+ hist[multiplicity] += 1
171
+ arc_count += multiplicity
172
+ hook_index1, hook_index2 = edge_codes[edge_code]
173
+ hook1, hook2 = hooks[hook_index1], hooks[hook_index2]
174
+ distance = np.linalg.norm(hook1.astype(float) - hook2.astype(float)) / radius
175
+ total_distance += distance * multiplicity
176
+ for multiplicity in range(max(hist.keys())+1):
177
+ print(multiplicity, hist[multiplicity])
178
+ print("total arc count", arc_count)
179
+ print("number of different arcs used", len(x_quantized[x_quantized>0]))
180
+ print("total distance (assuming a unit diameter circle)", total_distance / 2) # unit diameter, not unit radius.
181
+
182
+ main()