【千律】OpenCV基础:Hough圆检测

环境:Python3.8 和 OpenCV

内容:Hough圆检测

将直角坐标系中的一个圆映射为新坐标系中的一个点,对于原直角坐标系中的每一个圆,可以对应(a, b, r) 这样一个点,这个点即为新三维中的点。

标准法实现步骤:
1.获取原图像的边缘检测图像;

2.设置最小半径、最大半径和半径分辨率等超参数;

3.根据转化后空间的圆心分辨率等信息,设置计数器N(a, b, r);

4.对边缘检测图像的每个白色边缘像素点,其坐标记为(x, y),对于指定的半径r,根据公式 (a-x)^2 + (b-y)^2 = r^2 得到点(a, b)形成的轨迹圆,将轨迹圆经过的计数器N(a, b, r) 加1;

5.如果某个计数器的点数超过设定阈值,则认为存在对应的圆,取出(a, b, r),求出对应的圆方程。

梯度法实现步骤:

1.获取原图像的边缘检测图像;

2.设置最小半径、最大半径和半径分辨率等超参数;

3.计算原图像各点x和y方向的梯度;

4.设置计数器N(a, b);

5.对边缘图像中任意一点p,其梯度为(px, py),由该点p和其梯度决定一条直线,将直线经过的计数器N(a, b)加1。

6.如果某计数器点数超过阈值,则认为存在圆,取出的(a, b)记为圆心,通过不断变换半径,若与边缘图的交集点数超过设定阈值,则认为存在圆,从而获得圆方程。

实现方式1(梯度法):

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


# 封装图片显示函数
def image_show(image):
    if image.ndim == 2:
        plt.imshow(image, cmap='gray')
    else:
        image = cv.cvtColor(image, cv.COLOR_BGR2RGB)
        plt.imshow(image)
    plt.show()

# Hough圆检测


if __name__ == '__main__':

    # 读取灰度图
    img_desk = cv.imread('desk.png')

    # 转换为灰度图
    img_gray = cv.cvtColor(img_desk, cv.COLOR_BGR2GRAY)

    # 参数设置
    img_dp = 2           # 图像分辨率与累加器分辨率之比,建议范围为1-2
    MinDist = 50         # 两个不同圆圆心之间的距离
    Param1 = 400         # Canny边缘阈值的上限,其下限为上限的1/2
    Param2 = 80          # cv.HOUGH_GRADIENT 方法的累加器阈值,阈值越小,检测的圆越多
                         # cv.HOUGH_GRADIENT_ALT 方法中,该参数用于衡量圆的完美度,范围为0-1
    MinRadius = 10       # 最小圆半径
    MaxRadius = 100      # 最大圆半径

    # 霍夫圆检测
    circles = cv.HoughCircles(img_gray, cv.HOUGH_GRADIENT, dp=img_dp, minDist=MinDist,
                              param1=Param1, param2=Param2, minRadius=MinRadius, maxRadius=MaxRadius)

    # 绘制结果
    for (x, y, r) in circles.squeeze():
        cv.circle(img_desk, (int(x), int(y)), int(r), (0, 0, 255), 2)

    image_show(img_desk)

实现方式2(标准法): 

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


# 封装图片显示函数
def image_show(image):
    if image.ndim == 2:
        plt.imshow(image, cmap='gray')
    else:
        image = cv.cvtColor(image, cv.COLOR_BGR2RGB)
        plt.imshow(image)
    plt.show()



if __name__ == '__main__':

    # 读取灰度图
    img_desk = cv.imread('desk.png')

    # 转换为灰度图
    img_gray = cv.cvtColor(img_desk, cv.COLOR_BGR2GRAY)

    # 边缘检测
    img_canny = cv.Canny(img_gray, 100, 400)

    # 参数设置
    img_dr = 1            # 半径分辨率
    MinRadius = 10        # 最小圆半径
    MaxRadius = 100       # 最大圆半径
    thresh = 80           # 累加的阈值

    # 初始化计数器
    height, width = img_gray.shape
    radius = int((MaxRadius - MinRadius) / img_dr) + 1
    N = np.zeros((width, height, radius))

    # 获取边缘像素位置
    edge_y, edge_x = np.where(img_canny == 255)

    # 循环绘制不同圆
    for (x, y) in zip(edge_x, edge_y):
        for r in range(MinRadius, MaxRadius, img_dr):

            # 根据个点绘制不同圆
            circle = np.zeros((height, width), np.uint8)
            cv.circle(circle, (x, y), r, 255, 1)

            # 获取不同圆的轨迹位置
            bsi, asi = np.where(circle == 255)

            # 将轨迹经过位置计数器累加
            N[asi, bsi, r - MinRadius] += 1

    # 显示累加图
    plt.imshow(N[:, :, 50], cmap='jet')
    plt.show()

    # 获取圆的位置和半径
    xs, ys, rs = np.where(N > thresh)

    # 绘制图上检测的圆
    for (x, y, r) in zip(xs, ys, rs):
        cv.circle(img_desk, (x, y), r, (0, 0, 255), 2)

    # 显示结果
    image_show(img_desk)

实现方式3(梯度法):

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


# 封装图片显示函数
def image_show(image):
    if image.ndim == 2:
        plt.imshow(image, cmap='gray')
    else:
        image = cv.cvtColor(image, cv.COLOR_BGR2RGB)
        plt.imshow(image)
    plt.show()


if __name__ == '__main__':

    # 读取灰度图
    img_desk = cv.imread('desk.png')

    # 转换为灰度图
    img_gray = cv.cvtColor(img_desk, cv.COLOR_BGR2GRAY)

    # 边缘检测
    img_canny = cv.Canny(img_gray, 100, 400)

    # 参数设置
    img_dr = 1            # 半径分辨率
    MinRadius = 10        # 最小圆半径
    MaxRadius = 100       # 最大圆半径
    thresh1 = 40          # 累加的阈值
    thresh2 = 90          # 交集的阈值

    # 设置直线长度
    height, width = img_canny.shape
    lmax = height + width

    # 计算梯度
    Ix = cv.Sobel(img_gray, cv.CV_64F, 1, 0)
    Iy = cv.Sobel(img_gray, cv.CV_64F, 0, 1)
    Ix = Ix / (np.sqrt(Ix**2 + Iy**2) + 1e-5)
    Iy = Iy / (np.sqrt(Ix ** 2 + Iy ** 2) + 1e-5)

    # 设置计数器
    N = np.zeros((height, width), np.int32)

    # 获取边缘像素位置
    edge_y, edge_x = np.where(img_canny == 255)

    # 循环绘制不同直线
    for (x, y) in zip(edge_x, edge_y):

        # 获取不同点梯度
        grad_x = Ix[y, x]
        grad_y = Iy[y, x]

        # 根据点和梯度绘制直线
        line_sx = int(x + grad_x * lmax)
        line_sy = int(y + grad_y * lmax)

        line_ex = int(x - grad_x * lmax)
        line_ey = int(y - grad_y * lmax)

        line = np.zeros((height, width), np.uint8)
        cv.line(line, (line_sx, line_sy), (line_ex, line_ey), 255, 1)

        # 获取不同圆的轨迹位置
        bsi, asi = np.where(line == 255)

        # 将轨迹经过位置计数器累加
        N[bsi, asi] += 1

    # 显示累加图
    plt.imshow(N, cmap='jet')
    plt.show()

    # 获取圆的位置和半径
    bsi, asi = np.where(N > thresh1)

    # 根据已知的圆心绘制不同半径的圆
    cir_abr = []
    for (a, b) in zip(asi, bsi):
        for r in range(MinRadius, MaxRadius, img_dr):

            # 绘制不同半径的圆
            circles = np.zeros((height, width), np.uint8)
            cv.circle(circles, (a, b), r, 255, 1)

            # 获取绘制圆和边缘图的交集
            intersection = cv.bitwise_and(circles, img_canny)

            # 累计交集的点数
            num_sum = (intersection == 255).sum()

            # 获取超过阈值的圆心和半径
            if num_sum > thresh2:

                cir_abr.append((a, b, r))

    # 绘制检测的圆
    for (a, b, r) in cir_abr:
        cv.circle(img_desk, (a, b), r, (0, 0, 255), 2)

    # 显示结果
    image_show(img_desk)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿飞_Y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值