opencv-python霍夫变换
1 霍夫线检测原理
霍夫变换常用来提取图像中的直线和圆等几何形状。
在笛卡尔坐标系中,直线可以表示为 y=kx+q 也就是说通过变量k,q可以确定一条直线,把直线写成关于k,q的函数,进行空间转换,转换后的空间称为霍夫空间。
也就是说:笛卡尔坐标系中的一条线对应了霍夫空间的一个点。反过来,霍夫空间的一条线对应了笛卡尔坐标系的一个点。
如果笛卡尔坐标系中的点共线,那么这些点在霍夫空间中对应的直线应该交于一点。
如果是判断多个点是否共线的话,就尽可能找霍夫空间中更多直线相交的点。即霍夫坐标空间中选择尽可能多的直线汇成的点,将其对应回笛卡尔坐标系中的直线,基本上可以完成直线检测。
但是如果是垂直于y轴的直线,比如x=2,那么k,q就不好确定了。为了解决这种情况,把笛卡尔坐标系转换到极坐标系是比较好的选择。
因此,只需要指知道霍夫空间中交点的位置,就可以得到原坐标系下的直线。
实现流程如下:
2 霍夫线检测
opencv中霍夫线检测使用的api是 HoughLines(img,rho,theta,threshold)。检测的图像需要二值化或者进行canny边缘检测。threshold:阈值,只有高于阈值的累加器的值才是直线。
import cv2 import numpy as np img = cv2.imread('./contours.png') img_copy = img.copy() img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) edges =cv2.Canny(img_gray,50,150) # canny边缘检测 cv2.imshow('edges',edges) lines = cv2.HoughLines(edges,1,np.pi/180,100) #霍夫直线检测,threshold越小,越容易检测到更多的直线 print(lines) for line in lines: # 将检测的直线绘制在图像上,注意是极坐标。 rho,theta = line[0] 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(img_copy,(x1,y1),(x2,y2),[0,0,255]) #绘制直线 cv2.imshow('img_hough',img_copy) cv2.imshow('img',img) cv2.waitKey(0) cv2.destroyAllWindows()
检测结果如下:
3 霍夫圆检测
# 霍夫圆检测 import cv2 import numpy as np img = cv2.imread('./circle.jpg') img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) img_gray = cv2.medianBlur(img_gray,3) #中值滤波 res_img = np.zeros(img.shape[:],np.uint8) res_img[:] = 255 print(img.shape) #edges = cv2.Canny(img_gray,50,150) #不需要进行canny边缘检测,否则霍夫检测不出来圆,返回none circles = cv2.HoughCircles(img_gray,cv2.HOUGH_GRADIENT,1,100,param2=50) #霍夫圆检测,返回圆心和半径的list circles = np.int0(np.around(circles)) # 把结果转换为int整数 for i in circles[0,:]: cv2.circle(res_img,(i[0],i[1]),i[2],[0,0,255],2) #根据检测的结果绘制圆 print(circles) cv2.imshow('img',img) cv2.imshow('res_img',res_img) #cv2.imshow('edges',edges) cv2.waitKey(0) cv2.destroyAllWindows()
检测结果如下: