回答編集履歴
1
追記
answer
CHANGED
@@ -1,5 +1,94 @@
|
|
1
1
|
> アウトラインの修正
|
2
2
|
|
3
|
+
[Skeletonize](https://scikit-image.org/docs/dev/auto_examples/edges/plot_skeleton.html)という手法が使えそうです。
|
4
|
+
これにて閉領域の中心を通る線を得られます。
|
5
|
+
さらに[Skeleton Network](https://github.com/Image-Py/sknw)にてこれをグラフ化できます。
|
6
|
+
グラフの`Edge`から線、あるいはBezier曲線を得ることができます。
|
7
|
+
以上を組み合わせると以下のようなSVGを出力するコードになります。
|
8
|
+
```Python
|
9
|
+
import numpy as np
|
10
|
+
from numpy import array, linalg, matrix
|
11
|
+
from scipy.special import comb
|
12
|
+
import cv2
|
13
|
+
from skimage.morphology import skeletonize
|
14
|
+
import sknw
|
15
|
+
import svgwrite
|
16
|
+
|
17
|
+
#
|
18
|
+
# Bézier curve fitting with SciPy
|
19
|
+
# https://stackoverflow.com/questions/12643079/b%C3%A9zier-curve-fitting-with-scipy
|
20
|
+
#
|
21
|
+
Mtk = lambda n, t, k: t**(k)*(1-t)**(n-k)*comb(n,k)
|
22
|
+
bezierM = lambda ts: matrix([[Mtk(3,t,k) for k in range(4)] for t in ts])
|
23
|
+
|
24
|
+
def lsqfit(points, M):
|
25
|
+
M_ = linalg.pinv(M)
|
26
|
+
return M_ * points
|
27
|
+
|
28
|
+
def get_bezier(pts):
|
29
|
+
points = array(pts)
|
30
|
+
ts = array(range(points.shape[0]), dtype='float')/(points.shape[0]-1)
|
31
|
+
M = bezierM(ts)
|
32
|
+
control_points = lsqfit(points, M)
|
33
|
+
return control_points.tolist()
|
34
|
+
|
35
|
+
|
36
|
+
if __name__ == '__main__':
|
37
|
+
|
38
|
+
fname = 'test'
|
39
|
+
src = cv2.imread(fname + '.png', cv2.IMREAD_GRAYSCALE)
|
40
|
+
_, src = cv2.threshold(src, 192, 255, cv2.THRESH_BINARY)
|
41
|
+
|
42
|
+
# Skeleton化
|
43
|
+
# https://scikit-image.org/docs/dev/auto_examples/edges/plot_skeleton.html
|
44
|
+
ske = skeletonize(~(src != 0))
|
45
|
+
ske_gray = (ske * 255).astype(np.uint8)
|
46
|
+
ske_rgb = cv2.cvtColor(ske_gray, cv2.COLOR_GRAY2RGB)
|
47
|
+
cv2.imwrite(fname + '_ske.png', ske_rgb)
|
48
|
+
|
49
|
+
# Skeleton Networkを作成
|
50
|
+
# https://github.com/Image-Py/sknw
|
51
|
+
graph = sknw.build_sknw(ske.astype(np.uint16), multi=True)
|
52
|
+
|
53
|
+
dwg = svgwrite.Drawing(fname + '.svg', profile='tiny')
|
54
|
+
|
55
|
+
# Edge
|
56
|
+
for (s,e) in graph.edges():
|
57
|
+
|
58
|
+
pt_s = graph.node[s]['o'].tolist()
|
59
|
+
pt_e = graph.node[e]['o'].tolist()
|
60
|
+
|
61
|
+
for g in graph[s][e].values():
|
62
|
+
|
63
|
+
# 開始 + 中間点 + 終点
|
64
|
+
pts = g['pts'].tolist()
|
65
|
+
pts = [pt_s] + pts + [pt_e]
|
66
|
+
|
67
|
+
# 点群にフィットするBezierのパラメータを取得
|
68
|
+
params = get_bezier(pts)
|
69
|
+
|
70
|
+
# Bezierとして描画
|
71
|
+
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])
|
72
|
+
p = dwg.path( d=d, stroke='#000', fill='none', stroke_width=5)
|
73
|
+
dwg.add(p)
|
74
|
+
|
75
|
+
# 線をそのまま描画
|
76
|
+
#for i in range(len(pts)-1):
|
77
|
+
# dwg.add(dwg.line(pts[i], pts[i+1], stroke='#000', stroke_width=5))
|
78
|
+
|
79
|
+
dwg.save()
|
80
|
+
```
|
81
|
+
結果は、まあそれなりですね。
|
82
|
+
|
83
|
+
元画像
|
84
|
+

|
85
|
+
Skelton化した画像
|
86
|
+

|
87
|
+
SVGをpng化した画像
|
88
|
+

|
89
|
+
修正前
|
90
|
+
-----
|
91
|
+
|
3
92
|
変換結果の画像を見ると、アウトラインのひとつひとつはもともとは2次元ポリゴンで構成されているかと思われます。
|
4
93
|
であれば[Medial axis](https://en.wikipedia.org/wiki/Medial_axis)、限定(直線)的には[Straight skeleton](https://en.wikipedia.org/wiki/Straight_skeleton)が求めたいラインに近いものになるかと思います。
|
5
94
|
`python`でのそのものずばりの実装は見つかりませんでしたが、以下が参考になるかと思います。
|