創(chuàng)作不易幌绍,如果對您有幫助修械,幫忙點(diǎn)贊哦趾牧!
一. 霍夫變換理解:
? ? 可參考:https://www.cnblogs.com/hellcat/p/9896426.html
二. 霍夫變換簡介:
????霍夫變換,是將坐標(biāo)由直角坐標(biāo)系變換到極坐標(biāo)系肯污,然后再根據(jù)數(shù)學(xué)表達(dá)式檢測某些形狀(如直線和圓)的方法翘单。當(dāng) l1直線 上的某些點(diǎn)變換到極坐標(biāo)系下時,表現(xiàn)為某些線(和前面點(diǎn)數(shù)量一致)蹦渣,這些線交于一點(diǎn)哄芜,通過該點(diǎn)的坐標(biāo)就能表示原先的 l1直線。
三. 霍夫變換用于檢測圖像直線算法實(shí)現(xiàn):
? ? ① 提取圖像邊緣(可使用Canny算法等)[我也實(shí)現(xiàn)了它柬唯,前面Canny算法有問題可以參考我的另一篇文章:https://www.cnblogs.com/wojianxin/p/12533526.html]
? ? ② 實(shí)現(xiàn)二值圖像霍夫變換
? ? ? ? 1. 求出圖像對角線長:r_max
? ? ? ? 2. 在邊緣點(diǎn) (x,y) 處认臊,t 取[0,180),步長設(shè)為1锄奢,根據(jù)下式進(jìn)行霍夫變換
? ? ? ? 3. 做一個大小為 r_max * 180 的表拘央,變換后一個值落在表內(nèi)某坐標(biāo)师坎,就將該坐標(biāo)表內(nèi)值 + 1,簡言之堪滨,就是在進(jìn)行投票胯陋,統(tǒng)計(jì)通過哪個點(diǎn)的直線的數(shù)量最多(即在原圖像上越趨近于一條直線)。
????③ 進(jìn)行非極大值抑制(NMS)操作袱箱,使找出的直線落在不同的地點(diǎn)
????????NMS 的算法如下:
????????1. 遍歷該表遏乔,如果遍歷到的像素的投票數(shù)大于其8近鄰的像素投票值,則它不變发笔。
????????2. 如果遍歷到的像素的投票數(shù)小于其8近鄰的像素投票值盟萨,則將其設(shè)置為0。
? ? ④ 找到20個投票數(shù)最多的點(diǎn)(即:直角坐標(biāo)系下20條直線)準(zhǔn)備進(jìn)行輸出
? ? ? ??1. np.ravel? ?將多維數(shù)組降為1維
? ? ? ? 2. np.argsort? ?將數(shù)組元素從小到大排序了讨,返回索引值
? ? ? ? 3. [::-1]? ?數(shù)組反序 -> 得到從大到小索引值
? ? ? ? 4. [:20]? ?前20個最大投票值的索引
? ? ? ? 5. 根據(jù)索引得到坐標(biāo)(r捻激,t)
????⑤ 霍夫反變換后,畫出原圖中的20條直線前计,輸出圖像
四. 純手工實(shí)現(xiàn) ——> 利用霍夫變換檢測圖像中的直線
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Canny算法:提取圖像邊緣
def Canny(img):
? ? # Gray scale
? ? def BGR2GRAY(img):
? ? ? ? b = img[:, :, 0].copy()
? ? ? ? g = img[:, :, 1].copy()
? ? ? ? r = img[:, :, 2].copy()
? ? ? ? # Gray scale
? ? ? ? out = 0.2126 * r + 0.7152 * g + 0.0722 * b
? ? ? ? out = out.astype(np.uint8)
? ? ? ? return out
? ? # Gaussian filter for grayscale
? ? def gaussian_filter(img, K_size=3, sigma=1.3):
? ? ? ? if len(img.shape) == 3:
? ? ? ? ? ? H, W, C = img.shape
? ? ? ? ? ? gray = False
? ? ? ? else:
? ? ? ? ? ? img = np.expand_dims(img, axis=-1)
? ? ? ? ? ? H, W, C = img.shape
? ? ? ? ? ? gray = True
? ? ? ? ## Zero padding
? ? ? ? pad = K_size // 2
? ? ? ? out = np.zeros([H + pad * 2, W + pad * 2, C], dtype=np.float)
? ? ? ? out[pad : pad + H, pad : pad + W] = img.copy().astype(np.float)
? ? ? ? ## prepare Kernel
? ? ? ? K = np.zeros((K_size, K_size), dtype=np.float)
? ? ? ? for x in range(-pad, -pad + K_size):
? ? ? ? ? ? for y in range(-pad, -pad + K_size):
? ? ? ? ? ? ? ? K[y + pad, x + pad] = np.exp( - (x ** 2 + y ** 2) / (2 * sigma * sigma))
? ? ? ? #K /= (sigma * np.sqrt(2 * np.pi))
? ? ? ? K /= (2 * np.pi * sigma * sigma)
? ? ? ? K /= K.sum()
? ? ? ? tmp = out.copy()
? ? ? ? # filtering
? ? ? ? for y in range(H):
? ? ? ? ? ? for x in range(W):
? ? ? ? ? ? ? ? for c in range(C):
? ? ? ? ? ? ? ? ? ? out[pad + y, pad + x, c] = np.sum(K * tmp[y : y + K_size, x : x + K_size, c])
? ? ? ? out = np.clip(out, 0, 255)
? ? ? ? out = out[pad : pad + H, pad : pad + W]
? ? ? ? out = out.astype(np.uint8)
? ? ? ? if gray:
? ? ? ? ? ? out = out[..., 0]
? ? ? ? return out
? ? # sobel filter
? ? def sobel_filter(img, K_size=3):
? ? ? ? if len(img.shape) == 3:
? ? ? ? ? ? H, W, C = img.shape
? ? ? ? else:
? ? ? ? ? ? H, W = img.shape
? ? ? ? # Zero padding
? ? ? ? pad = K_size // 2
? ? ? ? out = np.zeros((H + pad * 2, W + pad * 2), dtype=np.float)
? ? ? ? out[pad : pad + H, pad : pad + W] = img.copy().astype(np.float)
? ? ? ? tmp = out.copy()
? ? ? ? out_v = out.copy()
? ? ? ? out_h = out.copy()
? ? ? ? ## Sobel vertical
? ? ? ? Kv = [[1., 2., 1.],[0., 0., 0.], [-1., -2., -1.]]
? ? ? ? ## Sobel horizontal
? ? ? ? Kh = [[1., 0., -1.],[2., 0., -2.],[1., 0., -1.]]
? ? ? ? # filtering
? ? ? ? for y in range(H):
? ? ? ? ? ? for x in range(W):
? ? ? ? ? ? ? ? out_v[pad + y, pad + x] = np.sum(Kv * (tmp[y : y + K_size, x : x + K_size]))
? ? ? ? ? ? ? ? out_h[pad + y, pad + x] = np.sum(Kh * (tmp[y : y + K_size, x : x + K_size]))
? ? ? ? out_v = np.clip(out_v, 0, 255)
? ? ? ? out_h = np.clip(out_h, 0, 255)
? ? ? ? out_v = out_v[pad : pad + H, pad : pad + W]
? ? ? ? out_v = out_v.astype(np.uint8)
? ? ? ? out_h = out_h[pad : pad + H, pad : pad + W]
? ? ? ? out_h = out_h.astype(np.uint8)
? ? ? ? return out_v, out_h
? ? def get_edge_angle(fx, fy):
? ? ? ? # get edge strength
? ? ? ? edge = np.sqrt(np.power(fx.astype(np.float32), 2) + np.power(fy.astype(np.float32), 2))
? ? ? ? edge = np.clip(edge, 0, 255)
? ? ? ? fx = np.maximum(fx, 1e-10)
? ? ? ? #fx[np.abs(fx) <= 1e-5] = 1e-5
? ? ? ? # get edge angle
? ? ? ? angle = np.arctan(fy / fx)
? ? ? ? return edge, angle
? ? def angle_quantization(angle):
? ? ? ? angle = angle / np.pi * 180
? ? ? ? angle[angle < -22.5] = 180 + angle[angle < -22.5]
? ? ? ? _angle = np.zeros_like(angle, dtype=np.uint8)
? ? ? ? _angle[np.where(angle <= 22.5)] = 0
? ? ? ? _angle[np.where((angle > 22.5) & (angle <= 67.5))] = 45
? ? ? ? _angle[np.where((angle > 67.5) & (angle <= 112.5))] = 90
? ? ? ? _angle[np.where((angle > 112.5) & (angle <= 157.5))] = 135
? ? ? ? return _angle
? ? def non_maximum_suppression(angle, edge):
? ? ? ? H, W = angle.shape
? ? ? ? _edge = edge.copy()
? ? ? ? for y in range(H):
? ? ? ? ? ? for x in range(W):
? ? ? ? ? ? ? ? ? ? if angle[y, x] == 0:
? ? ? ? ? ? ? ? ? ? ? ? ? ? dx1, dy1, dx2, dy2 = -1, 0, 1, 0
? ? ? ? ? ? ? ? ? ? elif angle[y, x] == 45:
? ? ? ? ? ? ? ? ? ? ? ? ? ? dx1, dy1, dx2, dy2 = -1, 1, 1, -1
? ? ? ? ? ? ? ? ? ? elif angle[y, x] == 90:
? ? ? ? ? ? ? ? ? ? ? ? ? ? dx1, dy1, dx2, dy2 = 0, -1, 0, 1
? ? ? ? ? ? ? ? ? ? elif angle[y, x] == 135:
? ? ? ? ? ? ? ? ? ? ? ? ? ? dx1, dy1, dx2, dy2 = -1, -1, 1, 1
? ? ? ? ? ? ? ? ? ? if x == 0:
? ? ? ? ? ? ? ? ? ? ? ? ? ? dx1 = max(dx1, 0)
? ? ? ? ? ? ? ? ? ? ? ? ? ? dx2 = max(dx2, 0)
? ? ? ? ? ? ? ? ? ? if x == W-1:
? ? ? ? ? ? ? ? ? ? ? ? ? ? dx1 = min(dx1, 0)
? ? ? ? ? ? ? ? ? ? ? ? ? ? dx2 = min(dx2, 0)
? ? ? ? ? ? ? ? ? ? if y == 0:
? ? ? ? ? ? ? ? ? ? ? ? ? ? dy1 = max(dy1, 0)
? ? ? ? ? ? ? ? ? ? ? ? ? ? dy2 = max(dy2, 0)
? ? ? ? ? ? ? ? ? ? if y == H-1:
? ? ? ? ? ? ? ? ? ? ? ? ? ? dy1 = min(dy1, 0)
? ? ? ? ? ? ? ? ? ? ? ? ? ? dy2 = min(dy2, 0)
? ? ? ? ? ? ? ? ? ? if max(max(edge[y, x], edge[y + dy1, x + dx1]), edge[y + dy2, x + dx2]) != edge[y, x]:
? ? ? ? ? ? ? ? ? ? ? ? ? ? _edge[y, x] = 0
? ? ? ? return _edge
? ? def hysterisis(edge, HT=100, LT=30):
? ? ? ? H, W = edge.shape
? ? ? ? # Histeresis threshold
? ? ? ? edge[edge >= HT] = 255
? ? ? ? edge[edge <= LT] = 0
? ? ? ? _edge = np.zeros((H + 2, W + 2), dtype=np.float32)
? ? ? ? _edge[1 : H + 1, 1 : W + 1] = edge
? ? ? ? ## 8 - Nearest neighbor
? ? ? ? nn = np.array(((1., 1., 1.), (1., 0., 1.), (1., 1., 1.)), dtype=np.float32)
? ? ? ? for y in range(1, H+2):
? ? ? ? ? ? ? ? for x in range(1, W+2):
? ? ? ? ? ? ? ? ? ? ? ? if _edge[y, x] < LT or _edge[y, x] > HT:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? continue
? ? ? ? ? ? ? ? ? ? ? ? if np.max(_edge[y-1:y+2, x-1:x+2] * nn) >= HT:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? _edge[y, x] = 255
? ? ? ? ? ? ? ? ? ? ? ? else:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? _edge[y, x] = 0
? ? ? ? edge = _edge[1:H+1, 1:W+1]
? ? ? ? return edge
? ? # grayscale
? ? gray = BGR2GRAY(img)
? ? # gaussian filtering
? ? gaussian = gaussian_filter(gray, K_size=5, sigma=1.4)
? ? # sobel filtering
? ? fy, fx = sobel_filter(gaussian, K_size=3)
? ? # get edge strength, angle
? ? edge, angle = get_edge_angle(fx, fy)
? ? # angle quantization
? ? angle = angle_quantization(angle)
? ? # non maximum suppression
? ? edge = non_maximum_suppression(angle, edge)
? ? # hysterisis threshold
? ? out = hysterisis(edge, 100, 30)
? ? return out
# 霍夫變換實(shí)現(xiàn)檢測圖像中的20條直線
def Hough_Line(edge, img):
? ? ## Voting
? ? def voting(edge):
? ? ? ? H, W = edge.shape
? ? ? ? drho = 1
? ? ? ? dtheta = 1
? ? ? ? # get rho max length
? ? ? ? rho_max = np.ceil(np.sqrt(H ** 2 + W ** 2)).astype(np.int)
? ? ? ? # hough table
? ? ? ? hough = np.zeros((rho_max, 180), dtype=np.int)
? ? ? ? # get index of edge
? ? ? ? # ind[0] 是 符合條件的縱坐標(biāo)胞谭,ind[1]是符合條件的橫坐標(biāo)
? ? ? ? ind = np.where(edge == 255)
? ? ? ? ## hough transformation
? ? ? ? # zip函數(shù)返回元組
? ? ? ? for y, x in zip(ind[0], ind[1]):
? ? ? ? ? ? ? ? for theta in range(0, 180, dtheta):
? ? ? ? ? ? ? ? ? ? ? ? # get polar coordinat4s
? ? ? ? ? ? ? ? ? ? ? ? t = np.pi / 180 * theta
? ? ? ? ? ? ? ? ? ? ? ? rho = int(x * np.cos(t) + y * np.sin(t))
? ? ? ? ? ? ? ? ? ? ? ? # vote
? ? ? ? ? ? ? ? ? ? ? ? hough[rho, theta] += 1
? ? ? ? out = hough.astype(np.uint8)
? ? ? ? return out
? ? # non maximum suppression
? ? def non_maximum_suppression(hough):
? ? ? ? rho_max, _ = hough.shape
? ? ? ? ## non maximum suppression
? ? ? ? for y in range(rho_max):
? ? ? ? ? ? for x in range(180):
? ? ? ? ? ? ? ? # get 8 nearest neighbor
? ? ? ? ? ? ? ? x1 = max(x-1, 0)
? ? ? ? ? ? ? ? x2 = min(x+2, 180)
? ? ? ? ? ? ? ? y1 = max(y-1, 0)
? ? ? ? ? ? ? ? y2 = min(y+2, rho_max-1)
? ? ? ? ? ? ? ? if np.max(hough[y1:y2, x1:x2]) == hough[y,x] and hough[y, x] != 0:
? ? ? ? ? ? ? ? ? ? pass
? ? ? ? ? ? ? ? ? ? #hough[y,x] = 255
? ? ? ? ? ? ? ? else:
? ? ? ? ? ? ? ? ? ? hough[y,x] = 0
? ? ? ? return hough
? ? def inverse_hough(hough, img):
? ? ? ? H, W, _= img.shape
? ? ? ? rho_max, _ = hough.shape
? ? ? ? out = img.copy()
? ? ? ? # get x, y index of hough table
? ? ? ? # np.ravel 將多維數(shù)組降為1維
? ? ? ? # argsort? 將數(shù)組元素從小到大排序,返回索引
? ? ? ? # [::-1]? 反序->從大到小
? ? ? ? # [:20]? ? 前20個
? ? ? ? ind_x = np.argsort(hough.ravel())[::-1][:20]
? ? ? ? ind_y = ind_x.copy()
? ? ? ? thetas = ind_x % 180
? ? ? ? rhos = ind_y // 180
? ? ? ? # each theta and rho
? ? ? ? for theta, rho in zip(thetas, rhos):
? ? ? ? ? ? # theta[radian] -> angle[degree]
? ? ? ? ? ? t = np.pi / 180. * theta
? ? ? ? ? ? # hough -> (x,y)
? ? ? ? ? ? for x in range(W):
? ? ? ? ? ? ? ? if np.sin(t) != 0:
? ? ? ? ? ? ? ? ? ? y = - (np.cos(t) / np.sin(t)) * x + (rho) / np.sin(t)
? ? ? ? ? ? ? ? ? ? y = int(y)
? ? ? ? ? ? ? ? ? ? if y >= H or y < 0:
? ? ? ? ? ? ? ? ? ? ? ? continue
? ? ? ? ? ? ? ? ? ? out[y, x] = [0,255,255]
? ? ? ? ? ? for y in range(H):
? ? ? ? ? ? ? ? if np.cos(t) != 0:
? ? ? ? ? ? ? ? ? ? x = - (np.sin(t) / np.cos(t)) * y + (rho) / np.cos(t)
? ? ? ? ? ? ? ? ? ? x = int(x)
? ? ? ? ? ? ? ? ? ? if x >= W or x < 0:
? ? ? ? ? ? ? ? ? ? ? ? continue
? ? ? ? ? ? ? ? ? ? out[y, x] = [0,0,255]
? ? ? ? out = out.astype(np.uint8)
? ? ? ? return out
? ? # voting
? ? hough = voting(edge)
? ? # non maximum suppression
? ? hough = non_maximum_suppression(hough)
? ? # inverse hough
? ? out = inverse_hough(hough, img)
? ? return out
# Read image
img = cv2.imread("../paojie.jpg").astype(np.float32)
# Canny
edge = Canny(img)
# Hough
out = Hough_Line(edge, img)
out = out.astype(np.uint8)
# Save result
cv2.imwrite("out.jpg", out)
cv2.imshow("result", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
五. 實(shí)驗(yàn)結(jié)果:
六. 參考內(nèi)容:
????①?https://www.cnblogs.com/wojianxin/p/12539886.html
????②?https://blog.csdn.net/Ibelievesunshine/article/details/105011867
七. 版權(quán)聲明:
????未經(jīng)作者允許男杈,請勿隨意轉(zhuǎn)載抄襲丈屹,抄襲情節(jié)嚴(yán)重者,作者將考慮追究其法律責(zé)任伶棒,創(chuàng)作不易旺垒,感謝您的理解和配合彩库!