前言
识别圆采用OpenCV来做比较简单,可以用HoughCircles函数,但是如何在内存和资源都很紧张的STM32上面实现圆识别算法,是本文的写作目的。本文的算法采用Python实现,不采用库函数,所以可以很方便的改写成C语言移植到STM32上。
1 色块追踪法识别圆的算法原理
对于上面这个黑色标记的图像,要识别出黑色圆。色块追踪实现圆的思路是,先对圆进行灰度化,然后再进行二值化,将图中的所有像素点都变成只有黑或白俩种颜色。然后查找黑色色块的中心点,当找到一个中心点后,再从该中心点在Y轴和X轴方向进行搜索,当找到不是黑色像素的点就停止,这样可以找到色块的上下左右边界,该圆的圆心和半径也就确定了。
2 算法实现步骤
预处理,灰度化和二值化
def gray(img):
h = img.shape[0]
w = img.shape[1]
for i in range(h):
for j in range(w):
rgb = img[i][j]
gray = rgb[0]*0.11+rgb[1]*0.59+rgb[2]*0.3
rgb[0] = gray
rgb[1] = gray
rgb[2] = gray
def binary(img):
THRESHOLD = 90
BLACK = 0
WHITE = 255
width = img.shape[1]
height = img.shape[0]
for i in range(height):
for j in range(width):
rgb = img[i][j]
if rgb[0] < THRESHOLD:
rgb[0] = BLACK
rgb[1] = BLACK
rgb[2] = BLACK
else:
rgb[0] = WHITE
rgb[1] = WHITE
rgb[2] = WHITE
查找圆心
def search_center(img,xstart,xend,ystart,yend):
spaceX = 10
spaceY = 10
width = img.shape[1]
height = img.shape[0]
for i in range(ystart,yend,spaceY):
for j in range(xstart,xend,spaceX):
failCnt = 0
for k in range(spaceX+spaceY):
if k < spaceX:
if i+int(spaceY/2) >= IMG_HEIGHT or j+k>=IMG_WIDTH:
continue
rgb = img[i+int(spaceY/2)][j+k]
else:
if i+(k-spaceX) >= IMG_HEIGHT or j+int(spaceX/2) >= IMG_WIDTH:
continue
rgb = img[i+(k-spaceX)][j+int(spaceX/2)]
#hsl = RGB2HSL(rgb)
if ColorMatch(rgb) == 0:
failCnt = failCnt + 1
if failCnt > int(spaceX+spaceY)>>ALLOW_FAIL_PER:
break
if k == spaceX+spaceY-1:
if j+int(spaceX/2)<IMG_WIDTH and i+int(spaceY/2)<IMG_HEIGHT:
return 1,j+int(spaceX/2),i+int(spaceY/2)
return 0,0,0
迭代查找圆边界
找到圆心以后,就开始迭代查找圆边界,迭代个俩三次就能比较准确的查找到圆了。
def ColorMatch(hls):
if hls[0] == 255:
return 0
return 1
def corrode(img,oldX,oldY):
Xmin = 0
Xmax = 0
Ymin = 0
Ymax = 0
failCnt = 0
for i in range(oldX,0,-1):
rgb = img[oldY][i]
#hsl = RGB2HSL(rgb)
if ColorMatch(rgb) == 0:
failCnt = failCnt + 1
if failCnt > int((WIDTH_MIN+WIDTH_MAX)>>2>>ALLOW_FAIL_PER):
break
Xmin = i
failCnt = 0
for i in range(oldX,IMG_WIDTH,1):
rgb = img[oldY][i]
#hsl = RGB2HSL(rgb)
if ColorMatch(rgb) == 0:
failCnt = failCnt + 1
if failCnt > int((WIDTH_MIN+WIDTH_MAX)>>2>>ALLOW_FAIL_PER):
break
Xmax = i
failCnt = 0
for i in range(oldY,0,-1):
rgb = img[i][oldX]
#hsl = RGB2HSL(rgb)
if ColorMatch(rgb) == 0:
failCnt = failCnt + 1
if failCnt > int((HEIGHT_MIN+HEIGHT_MAX)>>2>>ALLOW_FAIL_PER):
break
Ymin = i
failCnt = 0
for i in range(oldY,IMG_HEIGHT,1):
rgb = img[i][oldX]
#hsl = RGB2HSL(rgb)
if ColorMatch(rgb) == 0:
failCnt = failCnt + 1
if failCnt > int((HEIGHT_MIN+HEIGHT_MAX)>>2>>ALLOW_FAIL_PER):
break
Ymax = i
xc = int((Xmin+Xmax)/2)
yc = int((Ymin+Ymax)/2)
width = int(Xmax-Xmin)
height = int(Ymax-Ymin)
ret = 0
if width > WIDTH_MIN and width<WIDTH_MAX and height>HEIGHT_MIN and height<HEIGHT_MAX:
ret = 1
return ret,xc,yc,width,height
算法代码汇总
import cv2
import numpy
import os
import math
#使用颜色跟踪算法来识别圆
IMG_WIDTH = 160
IMG_HEIGHT = 120
WIDTH_MIN = 30
HEIGHT_MIN = 30
WIDTH_MAX = 70
HEIGHT_MAX = 70
ALLOW_FAIL_PER = 3
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 ColorMatch(hls):
if hls[0] == 255:
return 0
return 1
def search_center(img,xstart,xend,ystart,yend):
spaceX = 10
spaceY = 10
width = img.shape[1]
height = img.shape[0]
for i in range(ystart,yend,spaceY):
for j in range(xstart,xend,spaceX):
if i == 30 and j == 60:
print('test')
failCnt = 0
for k in range(spaceX+spaceY):
if k < spaceX:
if i+int(spaceY/2) >= IMG_HEIGHT or j+k>=IMG_WIDTH:
continue
rgb = img[i+int(spaceY/2)][j+k]
else:
if i+(k-spaceX) >= IMG_HEIGHT or j+int(spaceX/2) >= IMG_WIDTH:
continue
rgb = img[i+(k-spaceX)][j+int(spaceX/2)]
if ColorMatch(rgb) == 0:
failCnt = failCnt + 1
if failCnt > int(spaceX+spaceY)>>ALLOW_FAIL_PER:
break
if k == spaceX+spaceY-1:
if j+int(spaceX/2)<IMG_WIDTH and i+int(spaceY/2)<IMG_HEIGHT:
return 1,j+int(spaceX/2),i+int(spaceY/2)
return 0,0,0
def corrode(img,oldX,oldY):
Xmin = 0
Xmax = 0
Ymin = 0
Ymax = 0
failCnt = 0
for i in range(oldX,0,-1):
rgb = img[oldY][i]
if ColorMatch(rgb) == 0:
failCnt = failCnt + 1
if failCnt > int((WIDTH_MIN+WIDTH_MAX)>>2>>ALLOW_FAIL_PER):
break
Xmin = i
failCnt = 0
for i in range(oldX,IMG_WIDTH,1):
rgb = img[oldY][i]
if ColorMatch(rgb) == 0:
failCnt = failCnt + 1
if failCnt > int((WIDTH_MIN+WIDTH_MAX)>>2>>ALLOW_FAIL_PER):
break
Xmax = i
failCnt = 0
for i in range(oldY,0,-1):
rgb = img[i][oldX]
if ColorMatch(rgb) == 0:
failCnt = failCnt + 1
if failCnt > int((HEIGHT_MIN+HEIGHT_MAX)>>2>>ALLOW_FAIL_PER):
break
Ymin = i
failCnt = 0
for i in range(oldY,IMG_HEIGHT,1):
rgb = img[i][oldX]
if ColorMatch(rgb) == 0:
failCnt = failCnt + 1
if failCnt > int((HEIGHT_MIN+HEIGHT_MAX)>>2>>ALLOW_FAIL_PER):
break
Ymax = i
xc = int((Xmin+Xmax)/2)
yc = int((Ymin+Ymax)/2)
width = int(Xmax-Xmin)
height = int(Ymax-Ymin)
ret = 0
if width > WIDTH_MIN and width<WIDTH_MAX and height>HEIGHT_MIN and height<HEIGHT_MAX:
ret = 1
return ret,xc,yc,width,height
def trace(img):
xstart = 0
xend = IMG_WIDTH
ystart = 0
yend = IMG_HEIGHT
count = 0
#查找第1个圆
ret,xc,yc = search_center(img,xstart,xend,ystart,yend)
if ret == 0:
return
for i in range(3):
ret,xc,yc,width,height= corrode(img,xc,yc)
ret,xc,yc,width,height = corrode(img,xc,yc)
print(xc,yc,width,height)
pic = img.copy()
cv2.rectangle(pic,(xc-int(width/2),yc-int(height/2)),(xc+int(width/2),yc+int(height/2)),(255,0,0))
count = count+1
show_img("pic"+str(count),pic)
#更新ystart,查找第2个圆
ystart = yc+int(height/2)
ret,xc,yc = search_center(img,xstart,xend,ystart,yend)
if ret == 0:
return
for i in range(3):
ret,xc,yc,width,height= corrode(img,xc,yc)
ret,xc,yc,width,height = corrode(img,xc,yc)
print(xc,yc,width,height)
pic = img.copy()
cv2.rectangle(pic,(xc-int(width/2),yc-int(height/2)),(xc+int(width/2),yc+int(height/2)),(255,0,0))
count = count+1
show_img("pic"+str(count),pic)
#更新ystart,查找第3个圆
ystart = yc+int(height/2)
ret,xc,yc = search_center(img,0,xend,ystart,yend)
if ret == 0:
return
for i in range(3):
ret,xc,yc,width,height= corrode(img,xc,yc)
ret,xc,yc,width,height = corrode(img,xc,yc)
print(xc,yc,width,height)
pic = img.copy()
cv2.rectangle(pic,(xc-int(width/2),yc-int(height/2)), (xc+int(width/2),yc+int(height/2)),(255,0,0))
count = count+1
show_img("pic"+str(count),pic)
def gray(img):
h = img.shape[0]
w = img.shape[1]
for i in range(h):
for j in range(w):
rgb = img[i][j]
gray = rgb[0]*0.11+rgb[1]*0.59+rgb[2]*0.3
rgb[0] = gray
rgb[1] = gray
rgb[2] = gray
def binary(img):
THRESHOLD = 90
BLACK = 0
WHITE = 255
width = img.shape[1]
height = img.shape[0]
for i in range(height):
for j in range(width):
rgb = img[i][j]
if rgb[0] < THRESHOLD:
rgb[0] = BLACK
rgb[1] = BLACK
rgb[2] = BLACK
else:
rgb[0] = WHITE
rgb[1] = WHITE
rgb[2] = WHITE
img = cv2.imread("D:/pic/cir2.jpg")
kernel = numpy.ones((3, 3), numpy.uint8)
show_img("source",img)
gray(img)
binary(img)
show_img('binary',img)
trace(img)
cv2.waitKey(0)
3 运行结果
识别单圆
识别多圆
识别带干扰点的圆
算法局限性:
1 该算法运行预处理后产生的二值化图是一个比较完整的圆,所以对于光照和二值化阈值都有要求。
2 对于二值化后有其它不规则色块的图形,该算法会失败。入下图所示。要克服这个问题,需要使用霍夫圆算法,在STM32上实现霍夫圆算法,将会写另一篇文章来介绍。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)