霍夫变换

霍夫直线变换

霍夫直线变换(Hough Line Transform)用来做直线检测

霍夫直线变换官网文档

coordinate system

Hough Transform Demo

为了加升大家对霍夫直线的理解,我在左图左上角大了一个点,然后在右图中绘制出来经过这点可能的所有直线

绘制经过某点的所有直线的示例代码如下,这个代码可以直接拷贝运行

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np

def draw_line():
    # 绘制一张黑图
    img = np.zeros((500, 500, 1), np.uint8)
    # 绘制一个点
    cv.line(img, (10, 10), (10, 10), (255), 1)
    cv.imshow("line",img)
    return img

def hough_lines(img):
    rho = 1;
    theta = np.pi/180
    threshold=0
    lines = cv.HoughLines(img,rho, theta, threshold)

    dst_img = img.copy()

    for line in lines[:,0]:
        rho,theta = line
        a = np.cos(theta)
        b = np.sin(theta)
        x = a*rho
        y=b*rho

        x1 = int(np.round(x + 1000*(-b)))
        y1 = int(np.round(y + 1000*a))

        x2 = int(np.round(x - 1000*(-b)))
        y2 = int(np.round(y - 1000*a))

        cv.line(dst_img,(x1,y1),(x2,y2),(255,0,0),1)

    cv.imshow("li",dst_img)

img = draw_line()
hough_lines(img)

cv.waitKey(0)
cv.destroyAllWindows()

练习:寻找棋盘中的直线

示例代码如下:

import cv2 as cv
import matplotlib.pyplot as plt
import numpy as np

# 1. 将图片以灰度的方式读取进来
img = cv.imread("assets/weiqi.jpg", cv.IMREAD_COLOR)
cv.imshow("src",img)

gray_img = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# cv.imshow("gray",gray_img)
# 
flag,thresh_img = cv.threshold(gray_img,100,255,cv.THRESH_BINARY_INV)
cv.imshow("thresh_img",thresh_img)


# 3. 霍夫变换
#  线段以像素为单位的距离精度,double类型的,推荐用1.0
rho = 1
# 线段以弧度为单位的角度精度,推荐用numpy.pi/180
theta = np.pi/180
# 累加平面的阈值参数,int类型,超过设定阈值才被检测出线段,值越大,基本上意味着检出的线段越长,检出的线段个数越少。
threshold=10
# 线段以像素为单位的最小长度
min_line_length=25
# 同一方向上两条线段判定为一条线段的最大允许间隔(断裂),超过了设定值,则把两条线段当成一条线段,值越大,允许线段上的断裂越大,越有可能检出潜在的直线段
max_line_gap = 3

lines = cv.HoughLinesP(thresh_img,rho,theta,threshold,minLineLength=min_line_length,maxLineGap=max_line_gap)

dst_img = img.copy()

for line in lines:
    x1,y1,x2,y2 = line[0]
    cv.line(dst_img,(x1,y1),(x2,y2),(0,0,255),2)

cv.imshow("dst img",dst_img)

cv.waitKey(0)
cv.destroyAllWindows()

霍夫圆

一个圆可以由以下公式表示

(x - x_0)^2 + (y - y_0)^2=r^2

,其中

(x_0, y_0)

是圆心,r是半径。圆环需要3个参数来确定,所以进行圆环检测的累加器必须是三维的,这样效率就会很低,因此OpenCV使用了霍夫梯度法这个巧妙的方法,来使用边界的梯度信息,从而提升计算的效率。

使用步骤:

  1. 霍夫圆检测对噪声敏感,先对对象做中值滤波
  2. 检测边缘,先把可能的圆边缘过滤出来
  3. 根据边缘得到最大概率的圆心,进而得到最佳半径

hough_circle.jpg

OpenCV的Logo检测结果:

Hough Circles

  • 参数及代码
def hough_circle(img):
    img_copy = img.copy()
    # 中值滤波降噪
    img_copy = cv2.GaussianBlur(img_copy, (3,3), 0)
    img_copy = cv2.medianBlur(img_copy, 5)


    gray = cv2.cvtColor(img_copy, cv2.COLOR_BGR2GRAY)
    cv2.imshow("gray", gray)

    """
    @param 8-bit 单通道图片
    @param method 检测方法, 当前只有cv2.HOUGH_GRADIENT
    @param dp 累加器分辨率和图像分辨率的反比例, 例如:
        如果 dp=1 累加器和输入图像分辨率相同. 
        如果 dp=2 累加器宽高各为输入图像宽高的一半相同. 
    @param minDist 检测到圆的圆心之间的最小距离。
        如果参数太小,除了真实的一个之外,可能错误地检测到多个相邻的圆圈。
        如果参数太大,可能会遗漏一些圆
    @param param1 参数1。它是两个传递给Canny边缘检测器的较高阈值(较低的阈值是此值的一半)
    @param param2 参数2, 它是检测阶段圆心的累加器阈值。
        它越小,会检测到更多的假圆圈。较大累加器值对应的圆圈将首先返回。
    @param minRadius 最小圆半径.
    @param maxRadius 最大圆半径. 
        如果<=0, 会使用图片最大像素值
        如果< 0, 直接返回圆心, 不计算半径
    """
    # ../images/coins.jpg
    circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT,
                               dp = 1,
                               minDist = 50,
                               param1=160,
                               param2=50,
                               minRadius=0,
                               maxRadius=100)

    circles = np.uint16(np.around(circles))
    for i in circles[0, :]:
        # draw the outer circle
        cv2.circle(img_copy, (i[0], i[1]), i[2], (0, 255, 0), 2)
        # draw the center of the circle
        cv2.circle(img_copy, (i[0], i[1]), 2, (0, 0, 255), 3)

    cv2.imshow("detected circles", img_copy)

寻找棋盘中的棋子

import cv2 as cv
import numpy as np


img = cv.imread("assets/weiqi.jpg", cv.IMREAD_COLOR)
cv.imshow("src",img)
# 将图片转成灰色图片
gray_img = cv.cvtColor(img,cv.COLOR_BGR2GRAY)


# 霍夫圆形检测
def hough_circle(gray_img):
    # 定义检测图像中圆的方法。目前唯一实现的方法是cv2.HOUGH_GRADIENT
    method = cv.HOUGH_GRADIENT
    # 累加器分辨率与图像分辨率的反比。例如,如果dp = 1,则累加器具有与输入图像相同的分辨率。如果dp = 2,则累加器的宽度和高度都是一半。
    dp = 1
    # 检测到的圆的圆心之间最小距离。如果minDist太小,则可能导致检测到多个相邻的圆。如果minDist太大,则可能导致很多圆检测不到。
    minDist = 20
    # param1 Canny算法阈值上线
    # param2 cv2.HOUGH_GRADIENT方法的累加器阈值。阈值越小,检测到的圈子越多。
    # minRadius : 最小的半径,如果不确定,则不指定
    # maxRadius : 最大的半径,若不确定,则不指定
    circles = cv.HoughCircles(gray_img,method,dp,minDist=minDist,param1=70,param2=30,minRadius=0,maxRadius=20)

    for circle in circles[0,:]:
        # 圆心坐标,半径
        x,y,r = circle
        # 绘制圆心
        cv.circle(img,(x,y),2,(0,255,0),1)
        # 绘制圆形
        cv.circle(img,(x,y),r,(0,0,255),2)

    cv.imshow("result",img)

# 调用函数,寻找霍夫圆
hough_circle(gray_img)

cv.waitKey(0)
cv.destroyAllWindows()