创作不易,如果对您有帮助,帮忙点赞哦!
一. 霍夫变换理解:
二. 霍夫变换简介:
霍夫变换,是将坐标由直角坐标系变换到极坐标系,然后再根据数学表达式检测某些形状(如直线和圆)的方法。当 l1直线 上的某些点变换到极坐标系下时,表现为某些线(和前面点数量一致),这些线交于一点,通过该点的坐标就能表示原先的 l1直线。
三. 霍夫变换用于检测图像直线算法实现:
① 提取图像边缘(可使用Canny算法等)[我也实现了它,前面Canny算法有问题可以参考我的另一篇文章:https://www.cnblogs.com/wojianxin/p/12533526.html]
② 实现二值图像霍夫变换
1. 求出图像对角线长:r_max
2. 在边缘点 (x,y) 处,t 取[0,180),步长设为1,根据下式进行霍夫变换
霍夫变换,(r_ho,t) 表示极坐标,(x,y) 表示直角坐标 ↑
3. 做一个大小为 r_max * 180 的表,变换后一个值落在表内某坐标,就将该坐标表内值 + 1,简言之,就是在进行投票,统计通过哪个点的直线的数量最多(即在原图像上越趋近于一条直线)。
③ 进行非极大值抑制(NMS)操作,使找出的直线落在不同的地点
NMS 的算法如下:
1. 遍历该表,如果遍历到的像素的投票数大于其8近邻的像素投票值,则它不变。
2. 如果遍历到的像素的投票数小于其8近邻的像素投票值,则将其设置为0。
④ 找到20个投票数最多的点(即:直角坐标系下20条直线)准备进行输出
1. np.ravel 将多维数组降为1维
2. np.argsort 将数组元素从小到大排序,返回索引值
3. [::-1] 数组反序 -> 得到从大到小索引值
4. [:20] 前20个最大投票值的索引
5. 根据索引得到坐标(r,t)
⑤ 霍夫反变换后,画出原图中的20条直线,输出图像
霍夫逆变换公式 ↑
四. 纯手工实现 ——> 利用霍夫变换检测图像中的直线
1 importcv22 importnumpy as np3 importmatplotlib.pyplot as plt4
5 #Canny算法:提取图像边缘
6 defCanny(img):7
8 #Gray scale
9 defBGR2GRAY(img):10 b =img[:, :, 0].copy()11 g = img[:, :, 1].copy()12 r = img[:, :, 2].copy()13
14 #Gray scale
15 out = 0.2126 * r + 0.7152 * g + 0.0722 *b16 out =out.astype(np.uint8)17
18 returnout19
20
21 #Gaussian filter for grayscale
22 def gaussian_filter(img, K_size=3, sigma=1.3):23
24 if len(img.shape) == 3:25 H, W, C =img.shape26 gray =False27 else:28 img = np.expand_dims(img, axis=-1)29 H, W, C =img.shape30 gray =True31
32 ## Zero padding
33 pad = K_size // 2
34 out = np.zeros([H + pad * 2, W + pad * 2, C], dtype=np.float)35 out[pad : pad + H, pad : pad + W] =img.copy().astype(np.float)36
37 ## prepare Kernel
38 K = np.zeros((K_size, K_size), dtype=np.float)39 for x in range(-pad, -pad +K_size):40 for y in range(-pad, -pad +K_size):41 K[y + pad, x + pad] = np.exp( - (x ** 2 + y ** 2) / (2 * sigma *sigma))42 #K /= (sigma * np.sqrt(2 * np.pi))
43 K /= (2 * np.pi * sigma *sigma)44 K /=K.sum()45
46 tmp =out.copy()47
48 #filtering
49 for y inrange(H):50 for x inrange(W):51 for c inrange(C):52 out[pad + y, pad + x, c] = np.sum(K * tmp[y : y + K_size, x : x +K_size, c])53
54 out = np.clip(out, 0, 255)55 out = out[pad : pad + H, pad : pad +W]56 out =out.astype(np.uint8)57
58 ifgray:59 out =out[..., 0]60
61 returnout62
63
64 #sobel filter
65 def sobel_filter(img, K_size=3):66 if len(img.shape) == 3:67 H, W, C =img.shape68 else:69 H, W =img.shape70
71 #Zero padding
72 pad = K_size // 2
73 out = np.zeros((H + pad * 2, W + pad * 2), dtype=np.float)74 out[pad : pad + H, pad : pad + W] =img.copy().astype(np.float)75 tmp =out.copy()76
77 out_v =out.copy()78 out_h =out.copy()79
80 ## Sobel vertical
81 Kv = [[1., 2., 1.],[0., 0., 0.], [-1., -2., -1.]]82 ## Sobel horizontal
83 Kh = [[1., 0., -1.],[2., 0., -2.],[1., 0., -1.]]84
85 #filtering
86 for y inrange(H):87 for x inrange(W):88 out_v[pad + y, pad + x] = np.sum(Kv * (tmp[y : y + K_size, x : x +K_size]))89 out_h[pad + y, pad + x] = np.sum(Kh * (tmp[y : y + K_size, x : x +K_size]))90
91 out_v = np.clip(out_v, 0, 255)92 out_h = np.clip(out_h, 0, 255)93
94 out_v = out_v[pad : pad + H, pad : pad +W]95 out_v =out_v.astype(np.uint8)96 out_h = out_h[pad : pad + H, pad : pad +W]97 out_h =out_h.astype(np.uint8)98
99 returnout_v, out_h100
101
102 defget_edge_angle(fx, fy):103 #get edge strength
104 edge = np.sqrt(np.power(fx.astype(np.float32), 2) + np.power(fy.astype(np.float32), 2))105 edge = np.clip(edge, 0, 255)106
107 fx = np.maximum(fx, 1e-10)108 #fx[np.abs(fx) <= 1e-5] = 1e-5
109
110 #get edge angle
111 angle = np.arctan(fy /fx)112
113 returnedge, angle114
115
116 defangle_quantization(angle):117 angle = angle / np.pi * 180
118 angle[angle < -22.5] = 180 + angle[angle < -22.5]119 _angle = np.zeros_like(angle, dtype=np.uint8)120 _angle[np.where(angle <= 22.5)] =0121 _angle[np.where((angle > 22.5) & (angle <= 67.5))] = 45
122 _angle[np.where((angle > 67.5) & (angle <= 112.5))] = 90
123 _angle[np.where((angle > 112.5) & (angle <= 157.5))] = 135
124
125 return_angle126
127
128 defnon_maximum_suppression(angle, edge):129 H, W =angle.shape130 _edge =edge.copy()131
132 for y inrange(H):133 for x inrange(W):134 if angle[y, x] ==0:135 dx1, dy1, dx2, dy2 = -1, 0, 1, 0136 elif angle[y, x] == 45:137 dx1, dy1, dx2, dy2 = -1, 1, 1, -1
138 elif angle[y, x] == 90:139 dx1, dy1, dx2, dy2 = 0, -1, 0, 1
140 elif angle[y, x] == 135:141 dx1, dy1, dx2, dy2 = -1, -1, 1, 1
142 if x ==0:143 dx1 =max(dx1, 0)144 dx2 =max(dx2, 0)145 if x == W-1:146 dx1 =min(dx1, 0)147 dx2 =min(dx2, 0)148 if y ==0:149 dy1 =max(dy1, 0)150 dy2 =max(dy2, 0)151 if y == H-1:152 dy1 =min(dy1, 0)153 dy2 =min(dy2, 0)154 if max(max(edge[y, x], edge[y + dy1, x + dx1]), edge[y + dy2, x + dx2]) !=edge[y, x]:155 _edge[y, x] =0156
157 return_edge158
159 def hysterisis(edge, HT=100, LT=30):160 H, W =edge.shape161
162 #Histeresis threshold
163 edge[edge >= HT] = 255
164 edge[edge <= LT] =0165
166 _edge = np.zeros((H + 2, W + 2), dtype=np.float32)167 _edge[1 : H + 1, 1 : W + 1] =edge168
169 ## 8 - Nearest neighbor
170 nn = np.array(((1., 1., 1.), (1., 0., 1.), (1., 1., 1.)), dtype=np.float32)171
172 for y in range(1, H+2):173 for x in range(1, W+2):174 if _edge[y, x] < LT or _edge[y, x] >HT:175 continue
176 if np.max(_edge[y-1:y+2, x-1:x+2] * nn) >=HT:177 _edge[y, x] = 255
178 else:179 _edge[y, x] =0180
181 edge = _edge[1:H+1, 1:W+1]182
183 returnedge184
185 #grayscale
186 gray =BGR2GRAY(img)187
188 #gaussian filtering
189 gaussian = gaussian_filter(gray, K_size=5, sigma=1.4)190
191 #sobel filtering
192 fy, fx = sobel_filter(gaussian, K_size=3)193
194 #get edge strength, angle
195 edge, angle =get_edge_angle(fx, fy)196
197 #angle quantization
198 angle =angle_quantization(angle)199
200 #non maximum suppression
201 edge =non_maximum_suppression(angle, edge)202
203 #hysterisis threshold
204 out = hysterisis(edge, 100, 30)205
206 returnout207
208 #霍夫变换实现检测图像中的20条直线
209 defHough_Line(edge, img):210 ## Voting
211 defvoting(edge):212 H, W =edge.shape213
214 drho = 1
215 dtheta = 1
216
217 #get rho max length
218 rho_max = np.ceil(np.sqrt(H ** 2 + W ** 2)).astype(np.int)219
220 #hough table
221 hough = np.zeros((rho_max, 180), dtype=np.int)222
223 #get index of edge
224 #ind[0] 是 符合条件的纵坐标,ind[1]是符合条件的横坐标
225 ind = np.where(edge == 255)226
227 ## hough transformation
228 #zip函数返回元组
229 for y, x in zip(ind[0], ind[1]):230 for theta in range(0, 180, dtheta):231 #get polar coordinat4s
232 t = np.pi / 180 *theta233 rho = int(x * np.cos(t) + y *np.sin(t))234
235 #vote
236 hough[rho, theta] += 1
237
238 out =hough.astype(np.uint8)239
240 returnout241
242 #non maximum suppression
243 defnon_maximum_suppression(hough):244 rho_max, _ =hough.shape245
246 ## non maximum suppression
247 for y inrange(rho_max):248 for x in range(180):249 #get 8 nearest neighbor
250 x1 = max(x-1, 0)251 x2 = min(x+2, 180)252 y1 = max(y-1, 0)253 y2 = min(y+2, rho_max-1)254 if np.max(hough[y1:y2, x1:x2]) == hough[y,x] and hough[y, x] !=0:255 pass
256 #hough[y,x] = 255
257 else:258 hough[y,x] =0259
260 returnhough261
262 definverse_hough(hough, img):263 H, W, _=img.shape264 rho_max, _ =hough.shape265
266 out =img.copy()267
268 #get x, y index of hough table
269 #np.ravel 将多维数组降为1维
270 #argsort 将数组元素从小到大排序,返回索引
271 #[::-1] 反序->从大到小
272 #[:20] 前20个
273 ind_x = np.argsort(hough.ravel())[::-1][:20]274 ind_y =ind_x.copy()275 thetas = ind_x % 180
276 rhos = ind_y // 180
277
278 #each theta and rho
279 for theta, rho inzip(thetas, rhos):280 #theta[radian] -> angle[degree]
281 t = np.pi / 180. *theta282
283 #hough -> (x,y)
284 for x inrange(W):285 if np.sin(t) !=0:286 y = - (np.cos(t) / np.sin(t)) * x + (rho) /np.sin(t)287 y =int(y)288 if y >= H or y <0:289 continue
290 out[y, x] = [0,255,255]291 for y inrange(H):292 if np.cos(t) !=0:293 x = - (np.sin(t) / np.cos(t)) * y + (rho) /np.cos(t)294 x =int(x)295 if x >= W or x <0:296 continue
297 out[y, x] = [0,0,255]298
299 out =out.astype(np.uint8)300
301 returnout302
303
304 #voting
305 hough =voting(edge)306
307 #non maximum suppression
308 hough =non_maximum_suppression(hough)309
310 #inverse hough
311 out =inverse_hough(hough, img)312
313 returnout314
315
316 #Read image
317 img = cv2.imread("../paojie.jpg").astype(np.float32)318
319 #Canny
320 edge =Canny(img)321
322 #Hough
323 out =Hough_Line(edge, img)324
325 out =out.astype(np.uint8)326
327 #Save result
328 cv2.imwrite("out.jpg", out)329 cv2.imshow("result", out)330 cv2.waitKey(0)331 cv2.destroyAllWindows()
View Code
五. 实验结果:
原图 ↑
霍夫变换检测到的直线 ↑
六. 参考内容:
七. 版权声明:
未经作者允许,请勿随意转载抄袭,抄袭情节严重者,作者将考虑追究其法律责任,创作不易,感谢您的理解和配合!