STM32识别圆——色块追踪法

2023-05-16

前言

识别圆采用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(使用前将#替换为@)

STM32识别圆——色块追踪法 的相关文章

  • 初始化 ST-Link 设备时出错 - 无法连接到设备

    我目前正在使用 ST Link 调试器对我的 STM32F3 Discovery 板进行编程 我使用的IDE是Atollic TrueStudio 5 5 2 现在我面临一个非常奇怪的问题 那就是我不断收到消息 初始化 ST Link 设备
  • STM32 F072上的软件如何跳转到bootloader(DFU模式)?

    STM32应用笔记2606对此进行了讨论 但没有简单的代码示例 该答案已使用 IAR EWARM 在 STM32F072 Nucleo 板上进行了测试 这个答案使用 STM32标准外设库 仅此而已 请注意 验证您是否成功进入引导加载程序模式
  • 优化 ARM Cortex M3 代码

    我有一个 C 函数 它尝试将帧缓冲区复制到 FSMC RAM 这些函数将游戏循环的帧速率降低至 10FPS 我想知道如何分析反汇编的函数 我应该计算每个指令周期吗 我想知道CPU把时间花在哪里 在哪个部分 我确信该算法也是一个问题 因为它的
  • 136-基于stm32单片机家庭温湿度防漏水系统设计Proteus仿真+源程序

    资料编号 136 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 DHT11传感器 蜂鸣器 制作一个基于stm32单片机家庭温湿度防漏水系统设计Proteus仿真 2 通过DHT11传感器检测当前温湿度 并且显示到L
  • HAL库STM32常用外设教程(二)—— GPIO输入\输出

    HAL库STM32常用外设教程 二 GPIO输入 输出 文章目录 HAL库STM32常用外设教程 二 GPIO输入 输出 前言 一 GPIO功能概述 二 GPIO的HAl库驱动 三 GPIO使用示例 1 示例功能 四 代码讲解 五 总结
  • rt-thread studio中新建5.0不能用

    文章目录 一 版本对比 二 文件和文件夹打斜杠 在使用RT Thread studio创建新工程5 0版本的时候 结果发现新建完成之后程序不能正常运行 但是创建4 10版本的时候却能运行 那肯定是新版本出现了BUG 一 版本对比 首先对比了
  • STM32用一个定时器执行多任务写法

    文章目录 main c include stm32f4xx h uint32 t Power check times 电量检测周期 uint32 t RFID Init Check times RFID检测周期 int main Timer
  • 毕业设计 江科大STM32的智能温室控制蓝牙声光报警APP系统设计

    基于STM32的智能温室控制蓝牙声光报警APP系统设计 1 项目简介 1 1 系统构成 1 2 系统功能 2 部分电路设计 2 1 stm32f103c8t6单片机最小系统电路设计 2 2 LCD1602液晶显示电路设计 2 2 风
  • STM32 暂停调试器时冻结外设

    当到达断点或用户暂停代码执行时 调试器可以停止 Cortex 中代码的执行 但是 当皮质停止在暂停状态下执行代码时 调试器是否会冻结其他外设 例如 DMA UART 和定时器 您只能保留时间 r 取决于外围设备 我在进入主函数时调用以下代码
  • Freertos低功耗管理

    空闲任务中的低功耗Tickless处理 在整个系统运行得过程中 其中大部分时间都是在执行空闲任务的 空闲任务之所以执行 因为在系统中的其他任务处于阻塞或者被挂起时才会执行 因此可以将空闲任务的执行时间转换成低功耗模式 在其他任务解除阻塞而准
  • STM32F4XX的12位ADC采集数值超过4096&右对齐模式设置失败

    文章目录 一 前言 二 问题1 数值超过4096 三 问题1的排错过程 四 问题2 右对齐模式设置失败 五 问题2的解决方法 5 1 将ADC ExternalTrigConv设置为0 5 2 使用ADC StructInit 函数 一 前
  • 嵌入式开发--STM32G4系列片上FLASH的读写

    这个玩意吧 说起来很简单 就是几行代码的事 但楞是折腾了我大半天时间才搞定 原因后面说 先看代码吧 读操作 读操作很简单 以32位方式读取的时候是这样的 data IO uint32 t 0x0800F000 需要注意的是 当以32位方式读
  • 库函数点亮Led

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 pandas是什么 二 使用步骤 1 引入库 2 读入数据 总结 前言 提示 这里可以添加本文要记录的大概内容 例如 随着人工智能的不断发展 机器学习这门
  • STM32 Nucleo 上的上升沿中断多次触发

    我正在使用 STM32 NUCLEO F401RE 微控制器板 我有一个扬声器 经过编程 当向上 向下推操纵杆时 可以按设定的量改变频率 我的问题是 有时 通常 当向上 向下推动操纵杆时 频率会增加 减少多次 这意味着 ISR 正在执行多次
  • STM32 上的 ADC 单次转换

    我正在研究 STM32 F103x 上的 ADC 编程 并从最简单的情况 单次转换开始 测量内部温度传感器 连接到 ADC1 的值 并使用 USART 将其发送到 COM 端口 目标似乎很明确 但是当我尝试将源代码下载到闪存时 它不会向 C
  • PWM DMA 到整个 GPIO

    我有一个 STM32F4 我想对一个已与掩码进行 或 运算的 GPIO 端口进行 PWM 处理 所以 也许我们想要 PWM0b00100010一段时间为 200khz 但随后 10khz 后 我们现在想要 PWM0b00010001 然后
  • 在 Contiki 程序中使用 malloc

    考虑以下 Contiki 程序 include
  • HAL_Delay() 陷入无限循环

    我被 HAL Delay 函数困住了 当我调用此函数 HAL Delay 时 控制陷入无限循环 在寻找问题的过程中 我发现了这个 http www openstm32 org forumthread2145 threadId2146 htt
  • 使用 STM32F0 ADC 单独读取不同的输入

    STM32F072CBU 微控制器 我有多个 ADC 输入 并且希望单独读取它们 STMcubeMX 生成样板代码 假设我希望按顺序读取所有输入 但我无法弄清楚如何纠正这个问题 这篇博文 http blog koepi info 2015
  • 移动数组中的元素

    我需要一点帮助 我想将数组中的元素向上移动一个元素 以便新位置 1 包含位置 1 中的旧值 new 2 包含 old 1 依此类推 旧的最后一个值被丢弃 第一个位置的新值是我每秒给出的新值 我使用大小为 10 的数组 uint32 t TE

随机推荐