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