STM32识别圆——色块追踪法

前言

识别圆采用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上实现霍夫圆算法,将会写另一篇文章来介绍。

 

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值