STM32实现霍夫圆检测

本文要讨论的问题来源于工程实际,摄像头去拍圆形标记点得到一张图像,已知标记圆的半径范围(rmin,rmax),需要识别出圆心坐标和半径。采用霍夫圆变换可以很好的实现这个功能,且具有广泛的适应性(就是指在大多数情况下都能识别出圆,成功率高)。基本思路是先对圆进行边缘检测,然后对于边缘检测图像进行霍夫圆检测。

1 霍夫圆检测算法原理

假设圆的坐标假设为:

(x-x_{_{c}})^{2}+(y-y_{c})^{2}=r^{2}

现在已知圆上的一系列点(xi,yi),则可知圆心(xc,yc)也位于以(xi,yi)为圆心,半径为r的圆上,以这一系列点为圆心画一系列半径为r的圆,则圆心(xc,yc)将位于这些圆的交点上。如下图所示:

 霍夫圆检测的思路就是,对于其中的每一个半径r,做如下处理:

(1)建立一个投票数组SumArr[height][width],其中height为图像高度,width为图像宽度

(2)遍历边缘图像,对其中的每一个边缘点(xi,yi),求出以(xi,yi)为圆心,以r为半径的圆周上的所有像素点,将这些像素点的投票数加1,用伪代码表示为:

for(theta=0;theta<360;theta+=step)
{
    angle = theta*PI/180;
    cx=xi-r*cos(angle);
    cy=xi-r*sin(angle);
    if(cx>=0 && cx<width && cy>=0 && cy<height) 
        sumArr[cy][cx] ++;//坐标点(cx,cy)是潜在的圆心,将其投票数增1
}

(3)统计sumArr数组中的最大值,这个最大值对应的坐标即图中半径为r的圆所在的圆心。

(4)遍历半径范围(rmin,rmax),对于其中每一个半径r,既然可以求出它所对应的投票数candidate,圆心(xc,yc),取其中投票数最大值对应的半径,即为识别出来的圆半径r,其对应的圆心即为识别出来的圆心。

2 canny边沿检测算法

边沿检测算法使用canny边缘检测算法,通过计算图像梯度,

 梯度向量大小

由于开平方和根号计算较慢,可以用|gx|+|gy|来代替

计算梯度用sobel算子,sobel算子有俩个:

 将图中的每个像素和sobel算子1和算子2做卷积,求出gx和gy,进而求出梯度值M(x,y),利用梯度值来进行滤波。代码实现为:

def convolution(img,y,x,width,height,sobel):
    value = [0]*9
    if y-1>=0 and x-1>=0 and y-1<height and x-1<width:
        value[0] = img[y-1][x-1]
    if y-1>=0 and y-1<height:
        value[1] = img[y-1][x]
    if y-1>=0 and y-1<height and x+1<width:
        value[2] = img[y-1][x+1]
    if x-1>=0:
        value[3] = img[y][x-1]
    value[4] = img[y][x]
    if x+1<width:
        value[5] = img[y][x+1]
    if y+1<height and x-1>=0:
        value[6] = img[y+1][x-1]
    if y+1<height:
        value[7] = img[y+1][x]
    if y+1<height and x+1<width:
        value[8] = img[y+1][x+1]
    out = abs(value[0]*sobel[0]+value[1]*sobel[1]+value[2]*sobel[2]+\
        value[3]*sobel[3]+value[4]*sobel[4]+value[5]*sobel[5]+\
        value[6]*sobel[6]+value[7]*sobel[7]+value[8]*sobel[8])
    return int(out)

def mycanny(img,minVal,maxVal):
    sobel1 = [-1,-2,-1,0,0,0,1,2,1]
    sobel2 = [-1,0,1,-2,0,2,-1,0,1]
    width = img.shape[1]
    height = img.shape[0]
    out = numpy.zeros(img.shape,dtype=numpy.uint8)
    for y in range(height):
        for x in range(width):
            gx = convolution(img,y,x,width,height,sobel1)
            gy = convolution(img,y,x,width,height,sobel2)
            temp = gx+gy
            if temp <0:
                temp = 0
            elif temp>255:
                temp = 255
            if temp<200:
                out[y][x] = img[y][x]
            else:
                out[y][x] = temp
    return out

2 程序实现步骤

(1) 对原图像进行灰度化处理

(2) 对灰度图进行canny边缘检测

 (3) 进行圆霍夫检测

def findCir_R(img,r):
    width = img.shape[1]
    height = img.shape[0]
    sumArr = [0]*(width*height)
    for y in range(height):
        for x in range(width):
            if img[y][x] != 255:
                continue
            for a in range(0,360,10):
                theta = a*math.pi/180
                cx = int(x- r*math.cos(theta)) 
                cy = int(y-r*math.sin(theta))
                if cx>0 and cx<width and cy>0 and cy<height:
                    sumArr[cy*width+cx] = sumArr[cy*width+cx]+1
    maxVal = 0
    cirX = 0
    cirY = 0
    for y in range(height):
        for x in range(width):
            if sumArr[y*width+x] > maxVal:
                maxVal = sumArr[y*width+x]
                cirX = x
                cirY = y
    return maxVal,cirX,cirY

def findCir(img,rmin,rmax):
    maxVal = 0
    cirX = 0
    cirY = 0
    radius = 0
    for r in range(rmin,rmax+1,1):
        value,cx,cy = findCir_R(img,r)
        if value > maxVal:
            maxVal = value
            cirX = cx
            cirY = cy
            radius = r
    return cirX,cirY,radius

所有代码:

import cv2
import numpy
import os
import math

def show_img(window,img):
    cv2.namedWindow(window,0)
    cv2.resizeWindow(window,int(img.shape[1]),int(img.shape[0]))
    cv2.imshow(window,img)

def findCir_R(img,r):
    width = img.shape[1]
    height = img.shape[0]
    sumArr = [0]*(width*height)
    for y in range(height):
        for x in range(width):
            if img[y][x] != 255:
                continue
            for a in range(0,360,10):
                theta = a*math.pi/180
                cx = int(x- r*math.cos(theta)) 
                cy = int(y-r*math.sin(theta))
                if cx>0 and cx<width and cy>0 and cy<height:
                    sumArr[cy*width+cx] = sumArr[cy*width+cx]+1
    maxVal = 0
    cirX = 0
    cirY = 0
    for y in range(height):
        for x in range(width):
            if sumArr[y*width+x] > maxVal:
                maxVal = sumArr[y*width+x]
                cirX = x
                cirY = y
    return maxVal,cirX,cirY

def findCir(img,rmin,rmax):
    maxVal = 0
    cirX = 0
    cirY = 0
    radius = 0
    for r in range(rmin,rmax+1,1):
        value,cx,cy = findCir_R(img,r)
        if value > maxVal:
            maxVal = value
            cirX = cx
            cirY = cy
            radius = r
    return cirX,cirY,radius

def convolution(img,y,x,width,height,sobel):
    value = [0]*9
    if y-1>=0 and x-1>=0 and y-1<height and x-1<width:
        value[0] = img[y-1][x-1]
    if y-1>=0 and y-1<height:
        value[1] = img[y-1][x]
    if y-1>=0 and y-1<height and x+1<width:
        value[2] = img[y-1][x+1]
    if x-1>=0:
        value[3] = img[y][x-1]
    value[4] = img[y][x]
    if x+1<width:
        value[5] = img[y][x+1]
    if y+1<height and x-1>=0:
        value[6] = img[y+1][x-1]
    if y+1<height:
        value[7] = img[y+1][x]
    if y+1<height and x+1<width:
        value[8] = img[y+1][x+1]
    out = abs(value[0]*sobel[0]+value[1]*sobel[1]+value[2]*sobel[2]+\
        value[3]*sobel[3]+value[4]*sobel[4]+value[5]*sobel[5]+\
        value[6]*sobel[6]+value[7]*sobel[7]+value[8]*sobel[8])
    return int(out)

def mycanny(img,minVal,maxVal):
    sobel1 = [-1,-2,-1,0,0,0,1,2,1]
    sobel2 = [-1,0,1,-2,0,2,-1,0,1]
    width = img.shape[1]
    height = img.shape[0]
    out = numpy.zeros(img.shape,dtype=numpy.uint8)
    for y in range(height):
        for x in range(width):
            gx = convolution(img,y,x,width,height,sobel1)
            gy = convolution(img,y,x,width,height,sobel2)
            temp = gx+gy
            if temp <0:
                temp = 0
            elif temp>255:
                temp = 255
            if temp<200:
                out[y][x] = img[y][x]
            else:
                out[y][x] = temp
    return out

img = cv2.imread("D:/pic/cir22.jpg")
show_img("source",img)

gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
show_img('gray',gray)

#canny = cv2.Canny(img, 200, 400) 
canny = mycanny(gray, 20, 100) 
show_img('canny',canny)
#cv2.waitKey(0)

cx,cy,r = findCir(canny,15,30)
print(cx,cy,r)

pic = img.copy()
cv2.circle(pic,(cx,cy),r,(0,0,255))
show_img("result",pic)

cv2.waitKey(0)

程序运行效果:

相比前面文章 《STM32识别圆——色块追踪法》,霍夫圆检测算法识别成功率更高。但是这个算法在进行canny边缘检测时需要额外开辟一块和原图一样大小空间的数组用来存储canny结果,在进行霍夫圆检测时需要开辟投票统计数组sumArr。相比色块追踪法需要消耗更多内存,还需要消耗更多的运算时间。色块追踪法的优势是消耗内存少,运算速度快,但是识别成功率低于霍夫法。

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值