回答編集履歴

1

追記

2020/03/01 12:35

投稿

can110
can110

スコア38266

test CHANGED
@@ -1,4 +1,182 @@
1
1
  > アウトラインの修正
2
+
3
+
4
+
5
+ [Skeletonize](https://scikit-image.org/docs/dev/auto_examples/edges/plot_skeleton.html)という手法が使えそうです。
6
+
7
+ これにて閉領域の中心を通る線を得られます。
8
+
9
+ さらに[Skeleton Network](https://github.com/Image-Py/sknw)にてこれをグラフ化できます。
10
+
11
+ グラフの`Edge`から線、あるいはBezier曲線を得ることができます。
12
+
13
+ 以上を組み合わせると以下のようなSVGを出力するコードになります。
14
+
15
+ ```Python
16
+
17
+ import numpy as np
18
+
19
+ from numpy import array, linalg, matrix
20
+
21
+ from scipy.special import comb
22
+
23
+ import cv2
24
+
25
+ from skimage.morphology import skeletonize
26
+
27
+ import sknw
28
+
29
+ import svgwrite
30
+
31
+
32
+
33
+ #
34
+
35
+ # Bézier curve fitting with SciPy
36
+
37
+ # https://stackoverflow.com/questions/12643079/b%C3%A9zier-curve-fitting-with-scipy
38
+
39
+ #
40
+
41
+ Mtk = lambda n, t, k: t**(k)*(1-t)**(n-k)*comb(n,k)
42
+
43
+ bezierM = lambda ts: matrix([[Mtk(3,t,k) for k in range(4)] for t in ts])
44
+
45
+
46
+
47
+ def lsqfit(points, M):
48
+
49
+ M_ = linalg.pinv(M)
50
+
51
+ return M_ * points
52
+
53
+
54
+
55
+ def get_bezier(pts):
56
+
57
+ points = array(pts)
58
+
59
+ ts = array(range(points.shape[0]), dtype='float')/(points.shape[0]-1)
60
+
61
+ M = bezierM(ts)
62
+
63
+ control_points = lsqfit(points, M)
64
+
65
+ return control_points.tolist()
66
+
67
+
68
+
69
+
70
+
71
+ if __name__ == '__main__':
72
+
73
+
74
+
75
+ fname = 'test'
76
+
77
+ src = cv2.imread(fname + '.png', cv2.IMREAD_GRAYSCALE)
78
+
79
+ _, src = cv2.threshold(src, 192, 255, cv2.THRESH_BINARY)
80
+
81
+
82
+
83
+ # Skeleton化
84
+
85
+ # https://scikit-image.org/docs/dev/auto_examples/edges/plot_skeleton.html
86
+
87
+ ske = skeletonize(~(src != 0))
88
+
89
+ ske_gray = (ske * 255).astype(np.uint8)
90
+
91
+ ske_rgb = cv2.cvtColor(ske_gray, cv2.COLOR_GRAY2RGB)
92
+
93
+ cv2.imwrite(fname + '_ske.png', ske_rgb)
94
+
95
+
96
+
97
+ # Skeleton Networkを作成
98
+
99
+ # https://github.com/Image-Py/sknw
100
+
101
+ graph = sknw.build_sknw(ske.astype(np.uint16), multi=True)
102
+
103
+
104
+
105
+ dwg = svgwrite.Drawing(fname + '.svg', profile='tiny')
106
+
107
+
108
+
109
+ # Edge
110
+
111
+ for (s,e) in graph.edges():
112
+
113
+
114
+
115
+ pt_s = graph.node[s]['o'].tolist()
116
+
117
+ pt_e = graph.node[e]['o'].tolist()
118
+
119
+
120
+
121
+ for g in graph[s][e].values():
122
+
123
+
124
+
125
+ # 開始 + 中間点 + 終点
126
+
127
+ pts = g['pts'].tolist()
128
+
129
+ pts = [pt_s] + pts + [pt_e]
130
+
131
+
132
+
133
+ # 点群にフィットするBezierのパラメータを取得
134
+
135
+ params = get_bezier(pts)
136
+
137
+
138
+
139
+ # Bezierとして描画
140
+
141
+ d = 'M{},{} C{},{} {},{}, {},{}'.format( params[0][0], params[0][1], params[1][0], params[1][1], params[2][0], params[2][1], params[3][0], params[3][1])
142
+
143
+ p = dwg.path( d=d, stroke='#000', fill='none', stroke_width=5)
144
+
145
+ dwg.add(p)
146
+
147
+
148
+
149
+ # 線をそのまま描画
150
+
151
+ #for i in range(len(pts)-1):
152
+
153
+ # dwg.add(dwg.line(pts[i], pts[i+1], stroke='#000', stroke_width=5))
154
+
155
+
156
+
157
+ dwg.save()
158
+
159
+ ```
160
+
161
+ 結果は、まあそれなりですね。
162
+
163
+
164
+
165
+ 元画像
166
+
167
+ ![イメージ説明](1ef77af0f205797387a95ee73115510c.png)
168
+
169
+ Skelton化した画像
170
+
171
+ ![イメージ説明](3b66ff8aaebd52c86dc0ae7272b91094.png)
172
+
173
+ SVGをpng化した画像
174
+
175
+ ![イメージ説明](e423449246bf0d7a794c45dddb98bf42.png)
176
+
177
+ 修正前
178
+
179
+ -----
2
180
 
3
181
 
4
182