回答編集履歴
1
追記
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
|
|