処理の要点だけ書きます。
- 何とかして曲線部分だけ切り出しましょう。背景にいろいろな色があって分離困難な場合、HSV空間にもっていって、オレンジ色だけ切り出すと良いかもしれません。たくさん曲線があるようであれば、OpenCVのcontour毎に分離させる手法を取ってください。
- 式で近似させるためになんとかして線ののっかる座標を拾いましょう。座標の割り出しにはskeletonが便利です。「オレは近似式なんぞ使わず、画素の並び具合から傾きを計算する!」というマッチョであれば以下無視してください。
- 何らかの形でx,yから傾きを得るために、近似式を出す方法としてsplineを使いました。式が定義できるのであればscipyで最小二乗法をしたらいいと思います(私にはムリでした)。遺伝的アルゴリズムをコメントに書きましたが、計算が遅いのでお勧めできません。splineが現実的かと思います。
- 上記3において、splineの場合あるxに対して1つのyしか存在が許されません。あるx上に複数の解yが存在するのであれば、何らかの方法で曲線を分離してxに対して1つのyしか存在しないようにしてください。私の場合は重複チェックをかませてx,yの値を固有の値にしましました。これだと「初めに出てきた対を機械的に生かす」だけですので少なくとも数pxくらいズレてしまいます。そんなザコい処理は許されないというのであれば、skeletonのskeletonを探したり、gaussianでぼかして濃いところだけ探す、cv2.erodeなりでもっとあたりを探ったら良いと思います。splineで曲線が相当最適化されると思いますので、上記の中心を割り出す処理をした上で、x,yの対の90%くらいを切り捨ててしまってから式を割り出すsplineをしてもいいかもしれません。
- 傾きから直交する線を描くのはもっとスマートな方法があると思います。例えばtiitoiさんの回答みたいなもののほうがカッコいいですよね?
Python3
1import numpy as np
2import cv2
3import sys
4from scipy import interpolate
5from skimage.morphology import skeletonize
6
7def images_to_pos(img_thresh):
8 # Detect skeleton
9 skeleton = (skeletonize(img_th/255)*255).astype(np.uint8)
10 # cv2.imshow("skeleton",skeleton)
11
12 pos = np.asarray(list(zip(*np.where(skeleton > 100))))
13 pos_x,pos_y = pos[:,1], pos[:,0]
14
15 # Remove duplicates
16 x_used = list()
17 pos_x_updated = list()
18 pos_y_updated = list()
19 for x,y in zip(pos_x,pos_y):
20 if x in x_used:
21 pass
22 else:
23 x_used.append(x)
24 pos_x_updated.append(x)
25 pos_y_updated.append(y)
26
27 return pos_x_updated,pos_y_updated
28
29def calc_slope(spline_func,pos_x,target_x,shift_value):
30 # Calc x (low, high)
31 target_x_l = target_x - shift_value
32 target_x_h = target_x + shift_value
33
34 # Limit the range
35 target_x_l = np.clip(target_x_l, np.min(pos_x),np.max(pos_x))
36 target_x_h = np.clip(target_x_h, np.min(pos_x),np.max(pos_x))
37
38 # Calc y (low, high)
39 target_y_l = spline_func(target_x_l)
40 target_y_h = spline_func(target_x_h)
41
42 slope= (target_y_h - target_y_l) / (target_x_h - target_x_l)
43 return slope
44
45def calc_orthogonal(slope,intersection):
46 target_slope = (-1) / slope
47 target_intercept = intersection[1] / (target_slope *intersection[0])
48
49 return target_slope, target_intercept
50
51def draw_line(img,spline_func,pos_x,target_x):
52 # x = --> y =
53 target_x = target_x
54 target_y = spline_func(target_x)
55 center = (int(target_x),int(target_y))
56
57 shift_value_horizontal = 30
58 shift_value_vertical = 999
59 #######################################
60 # Calc slope on the point
61 target_slope = calc_slope(spline_func,pos_x,target_x,shift_value_horizontal)
62
63 # Left
64 img = cv2.line(img,center,(int(target_x - shift_value_horizontal), int(target_y - (target_slope * shift_value_horizontal))),(128,255,192),3)
65
66 # Right
67 img = cv2.line(img,center,(int(target_x + shift_value_horizontal), int(target_y + (target_slope * shift_value_horizontal))),(128,192,255),3)
68
69 #######################################
70 # Calc orthogonal params
71 orth_slope,orth_intercept = calc_orthogonal(target_slope,(target_x,target_y))
72
73 # Top
74 img = cv2.line(img,center,(int(target_x + shift_value_vertical), int(target_y + (orth_slope * shift_value_vertical) + orth_intercept)),(255,0,0),3)
75
76 # Bottom
77 img = cv2.line(img,center,(int(target_x - shift_value_vertical), int(target_y - (orth_slope * shift_value_vertical) + orth_intercept)),(255,192,192),3)
78
79 #######################################
80 # Center
81 img = cv2.circle(img,center, 3, (32,32,32), -1)
82 return img
83
84if __name__ == "__main__":
85 # Read image
86 img = cv2.imread("pic.png")
87
88 # Make grayscale image
89 img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
90
91 # Make binarized image
92 ret,img_th = cv2.threshold(img_gray,200,255,cv2.THRESH_BINARY)
93 img_th = 255-img_th
94
95 # binary image to positional data
96 pos_x, pos_y = images_to_pos(img_th)
97
98 # Calc spline
99 spline_func = interpolate.interp1d(pos_x, pos_y,kind="cubic")
100
101 # # Debug
102 # try:
103 # for x in range(np.min(pos_x),np.max(pos_x),1):
104 # y = spline_func(x)
105 # img = cv2.circle(img,(int(x),int(y)), 15, (128,128,255), 1)
106 #
107 # except Exception as err:
108 # print("x",x)
109 # print("ERR:",err)
110 # sys.exit()
111
112 img = draw_line(img,spline_func,pos_x,100)
113 img = draw_line(img,spline_func,pos_x,150)
114 img = draw_line(img,spline_func,pos_x,200)
115 img = draw_line(img,spline_func,pos_x,300)
116 img = draw_line(img,spline_func,pos_x,500)
117
118 cv2.imshow("img",img)
119 cv2.imwrite("img.png",img)
120 cv2.waitKey(0)