霍夫变换在直线和圆检测中的应用。
霍夫线变换是通过转换到极坐标空间去寻找最值直线,通过θ和r 的极坐标点唯一确定一条在x-y坐标下的一条直线。
霍夫圆变换的存在一个三维数组(a,b,r)用来唯一确定原始空间的一个确定的圆,在手动实现的过程中发现非并行计算的情况下计算效率极低。
为了降低计算量,考虑降低图像的尺寸以获取计算上的便利,得到圆的指定位置后在重新放缩到原始的图像大小,以达到快速计算的目的。
霍夫圆变换:
import numpy as np
import math
import cv2
from copy import deepcopy
import sys
def hough_circles(edge_image, edge_thresh, radius_values):
h = edge_image.shape[0]
w = edge_image.shape[1]
# print(h,w)
edgimg = np.zeros((h, w), np.int64)
for i in range(h):
for j in range(w):
if edge_image[i][j] > edge_thresh:
edgimg[i][j] = 255
else:
edgimg[i][j] = 0
accum_array = np.zeros((len(radius_values), h, w))
for i in range(h):
print('Hough Transform进度:', i, '/', h)
for j in range(w):
if edgimg[i][j] != 0:
for r in range(len(radius_values)):
rr = radius_values[r]
hdown = max(0, i - rr)
for a in range(hdown, i):
b = round(j+math.sqrt(rr*rr - (a - i) * (a - i)))
if b>=0 and b<=w-1:
accum_array[r][a][b] += 1
if 2 * i - a >= 0 and 2 * i - a <= h - 1:
accum_array[r][2 * i - a][b] += 1
if 2 * j - b >= 0 and 2 * j - b <= w - 1:
accum_array[r][a][2 * j - b] += 1
if 2 * i - a >= 0 and 2 * i - a <= h - 1 and 2 * j - b >= 0 and 2 * j - b <= w - 1:
accum_array[r][2 * i - a][2 * j - b] += 1
return edgimg, accum_array
def find_circles(image, accum_array, radius_values, hough_thresh, rescale_factor,H,W):
returnlist = []
hlist = []
wlist = []
rlist = []
returnimg = deepcopy(image)
for r in range(accum_array.shape[0]):
print('Find Circles 进度:', r, '/', accum_array.shape[0])
for h in range(accum_array.shape[1]):
for w in range(accum_array.shape[2]):
if accum_array[r][h][w] > hough_thresh:
tmp = 0
for i in range(len(hlist)):
if abs(w-wlist[i])<10 and abs(h-hlist[i])<10:
tmp = 1
break
if tmp == 0:
rr = radius_values[r]
flag = '(h,w,r)is:(' + str(h) + ',' + str(w) + ',' + str(rr) + ')'
returnlist.append(flag)
hlist.append(h)
wlist.append(w)
rlist.append(rr)
print('圆的数量:', len(hlist))
for i in range(len(hlist)):
center = (wlist[i], hlist[i])
rr = rlist[i]
color = (0, 255, 0)
thickness = 2
rescale_center=(center[0]*rescale_factor,center[1]*rescale_factor)
rr = rr * rescale_factor
cv2.circle(returnimg, rescale_center, rr, color, thickness)
return returnlist, returnimg
def main(argv):
img_name = argv[0]
image_read = cv2.imread('test2.jpg', cv2.COLOR_BGR2GRAY)
H,W,C=image_read.shape
#super params
thresh = 254
radius_values = []
for i in range(10):
radius_values.append(45 + i)
rescale_factor = 5
#super params
img = cv2.resize(image_read,(H//rescale_factor,W//rescale_factor))
gray_image = img
img1 = cv2.medianBlur(gray_image,21)
img1 = cv2.Canny(img1,40,100)
cv2.imwrite('output/' + img_name + "_after_edging.png", img1)
edgeimg, accum_array = hough_circles(img1, thresh, radius_values)
cv2.imwrite('output/' + img_name + "_after_circle_fitting.png", edgeimg)
# Findcircle
hough_thresh = 35
resultlist, resultimg = find_circles(image_read, accum_array, radius_values, hough_thresh,rescale_factor,H,W)
print(resultlist)
cv2.imwrite('output/' + img_name + "_circles.png", resultimg)
if __name__ == '__main__':
sys.argv.append("test")
main(sys.argv[1:])
霍夫线变换
import numpy as np
import cv2
import math
def hough_line(edge_image, theta_resolution=1.0, rho_resolution=1.0):
# 得到图像的尺寸
height, width = edge_image.shape
# 计算霍夫空间的最大半径
diag_len = int(math.sqrt(width**2 + height**2))
# 创建霍夫空间
hough_space = np.zeros((int(180/theta_resolution), int(2*diag_len/rho_resolution)), dtype=np.uint64)
# 遍历图像中的每个边缘点
for y in range(height):
for x in range(width):
# 如果该点是边缘点
if edge_image[y, x] != 0:
# 遍历所有可能的角度
for theta in range(0, 180, int(theta_resolution)):
# 计算对应的半径
rho = int(x * math.cos(math.radians(theta)) + y * math.sin(math.radians(theta)))
# 在霍夫空间中累加
hough_space[theta, rho + diag_len] += 1
hough_space = hough_space
return hough_space
# 读取图像并转换为灰度
image = cv2.imread('test2.jpg', cv2.IMREAD_GRAYSCALE)
# 使用Canny边缘检测器
edges = cv2.Canny(image, 40, 100)
# 应用霍夫线变换
hough_space = hough_line(edges)
# 找到霍夫空间中的局部最大值
lines = []
max_lines = 1000 # 检测的最大直线数量
threshold = 75 # 阈值
for rho_idx in range(hough_space.shape[1]):
for theta_idx in range(hough_space.shape[0]):
if hough_space[theta_idx, rho_idx] > threshold:
# 计算直线的参数
rho = (rho_idx - hough_space.shape[1] // 2) * 1.0
theta = theta_idx * 1.0
lines.append((rho, theta))
# 减少阈值以避免检测到相近的直线
hough_space[theta_idx, rho_idx] = 0
max_lines -= 1
if max_lines <= 0:
break
if max_lines <= 0:
break
# 绘制检测到的直线
image_out = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
for rho, theta in lines:
a = np.cos(theta)
b = np.sin(theta)
x0 = a*rho
y0 = b*rho
x1 = int(x0 + 1000*(-b))
y1 = int(y0 + 1000*(a))
x2 = int(x0 - 1000*(-b))
y2 = int(y0 - 1000*(a))
cv2.line(image_out,(x1,y1),(x2,y2),(0,0,255),2)
# 显示结果
def norm(x):
return ( (x-x.min())/(x.max()-x.min()) )*255
hough_space = hough_space.astype(np.uint8).transpose(1,0)
hough_space = cv2.resize(hough_space.astype(np.uint8),(720,720))
cv2.imshow('Original Image', image)
cv2.imshow('Canny Image', edges)
cv2.imshow('Hough Space', (hough_space) )
cv2.imshow('Hough Line Detection', image_out)
cv2.waitKey(0)
cv2.destroyAllWindows()