测试场景布置图
目标板和背景板放置示意图
题目上有三种颜色三种形状的物体, 可以先判断物体的颜色二值化后判断物体形状, 这样可以有效降低误差, 但是后来测试的时候经常会发现有误判的现象, 想到可能是因为背景嘈杂并且二值化后的图像信息会有损耗, 我开始思考为什么人类识别这些物体的成功率如此之高, 自我认为是因为我们会聚焦到一个物体并且不断找出这个物体的特征然后根据经验判断出物体是什么, 因此我设计了一个几率模型,或者说是一种擂台法则, 把在一段时间内检测到的数据进行整合得到一个几率表, 根据擂台法则得出物体的准确信息
大概可分为以下几个要点:
根据题目要求, 程序分为两种运行模式:
flag A:
确认物体颜色,获得距离算出物体大小
flag B:
先确认物体颜色, 然后再跟踪物体, 跟踪物体之后测量出距离, 随后进行尺寸计算
所谓聚焦,就是让当前画面中只出现自己所关心的内容, 我使用OpenMV提供的图像二值化方法来实现这一点
根据像素是否在阈值列表 thresholds
中的阈值内,将图像中的所有像素设置为黑色或白色。
thresholds
必须是元组列表。 [(lo, hi), (lo, hi), ..., (lo, hi)]
定义你想追踪的颜色范围。 对于灰度图像,每个元组需要包含两个值 - 最小灰度值和最大灰度值。 仅考虑落在这些阈值之间的像素区域。 对于RGB565图像,每个元组需要有六个值(l_lo,l_hi,a_lo,a_hi,b_lo,b_hi) - 分别是LAB L,A和B通道的最小值和最大值。 为方便使用,此功能将自动修复交换的最小值和最大值。 此外,如果元组大于六个值,则忽略其余值。相反,如果元组太短,则假定其余阈值处于最大范围。
注解:
获取所跟踪对象的阈值,只需在IDE帧缓冲区中选择(单击并拖动)跟踪对象。 直方图会相应地更新到所在区域。然后只需写下颜色分布在每个直方图通道中起始与下降位置。 这些将是
thresholds
的低值和高值。 由于上下四分位数据相差微小,故手动确定阈值为佳。
您还可以通过进入OpenMV IDE中的
工具 ->机器视觉 ->阈值编辑器
并从GUI窗口中拖动滑块来确定颜色阈值。
invert
反转阈值操作,像素在已知颜色范围之外进行匹配,而非在已知颜色范围内。
设置 zero
为True来使阈值像素为零,并使不在阈值列表中的像素保持不变。
mask
是另一个用作绘图操作的像素级掩码的图像。掩码应该是一个只有黑色或白色像素的图像,并且应该与你正在绘制的 image
大小相同。 仅掩码中设置的像素被修改。
to_bitmap
将图像数据转换为二进制位图图像,每个像素以1位的形式存储。 对于非常小的图像,新的位图图像可能无法放入原始图像中,因此需要使用 copy
进行就地操作。
copy
如果为True,则在堆上创建二进制图像的副本,而不是修改源图像。
注解:
位图图像就像只有两个像素值(0和1)的灰度图像一样。 此外,位图图像被打包,这样它们每个像素仅存储1位,因此非常小。 OpenMV图像库允许位图图像在所有位置的
sensor.GRAYSCALE
和sensor.RGB565
中使用。 但是,将许多操作应用于位图图像时没有任何意义,因为位图图像只有2个值。 OpenMV建议在操作中对mask
值使用位图图像,因为它们很容易适合MicroPython堆。 最后,将位图图像像素值0和1应用于sensor.GRAYSCALE
或sensor.RGB565
图像时, 将被解释为黑白。该库自动处理转换。
返回图像对象,以便您可以使用 .
表示法调用另一个方法。
不支持压缩图像和bayer图像。
封装一个工具方法方便下面的程序编写:
# binary filter
def testBinary(img, thresholdValue):
return img.binary([thresholdValue])
使用MicroPython.img对象自带的Hough变换查找圆形
def isCircle(img):
circles = img.find_circles(
threshold=5000, x_margin=10, y_margin=10,
r_margin=10, r_min=8, r_max=100, r_step=2)
print("circles: ", circles)
if (len(circles) < 1):
return False
return circles[0].r() # 像素直径
三角形使用占空比判断, density函数可以自动返回色块面积/外接矩形面积 的值
def isTriangle(img):
sideLength = 0 # 存放测量到的像素块的边长
blobs = img.find_blobs([gray_threshold])
# print(blobs)
blob = find_max(blobs)
if (blob == None):
return False
print('该形状占空比为', blob.density())
# density函数可以自动返回 色块面积/外接矩形面积 的值
if 0.56 > blob.density() > 0.50:
print("检测为三角型 ", end='')
# img.draw_cross(blob.cx(), blob.cy(), color=(255,0,0), size=5)
print('三角型边长', blob.w())
sideLength = blob.w()
print("triangle sideLength: ", sideLength)
return sideLength
return False
矩形同样使用占空比判断, density函数可以自动返回色块面积/外接矩形面积 的值
def isRect(img):
blobs = img.find_blobs([gray_threshold])
print(blobs)
blob = find_max(blobs)
if (blob == None):
return False
# for blob in blobs:
print('该形状占空比为', blob.density())
if 0.99 > blob.density() > 0.87:
print("检测为矩形 ", end='')
print('矩形边长', blob.w())
return blob.w()
return False
最终决定采用激光测距
关于尺寸计算公式实在想不起来当时怎么计算的了, 实验表明只能在200cm~300cm之间并且只能测量圆形的尺寸
这种方式参考OpenMV官方例程, 倒也可以, 但是误差较大, 像素测距以及计算尺寸的部分代码:
'距离的K值 L=k/像素'
k = 2400
def isCircle(img):
print(img)
res = {
'L': 0, 'R': 0} # 距离 直径
circles = img.find_circles(
threshold=5000, x_margin=10, y_margin=10,
r_margin=10, r_min=8, r_max=100, r_step=2)
print("circles: ", circles)
if (len(circles) < 1):
return False
for circle in circles:
# img.draw_circle(circle.x(), circle.y(), circle.r(), color=(255, 0, 0))
L = k / circle.r()
res['L'] = L # 距离
k1 = (L - 200) * 0.04
R = (k1 + circle.r()) * 3.04
res['R'] = R # 直径
print("isCircle:res: ", res)
return res
采用这种方式好处就是激光测距本身就带有激光灯, 并且极其准确, 缺点就是需要占用串口
PLS-K-60 激光测距模块:
接线图:
用串口给激光测距发送特定字节命令可以操作它, 部分指令:
详细数据手册稍后上传至CSDN资源: PLS-K-60激光测距模块产品手册.pdf-CSDN下载
激光测距程序实现:
def getDistance():
# uart.write(b'D') # 测距离
while(True): # 因为我需要调用一次这个方法就返回给我准确的数据, 所以这里需要一直等待着获取到有用的数据
uart.write(b'D') # 测距离 0x44
v = uart.read(1024)
# print(v)
if(v != None):
# print(v)
s = v.decode('utf-8')
if(len(s) == 15):
s = s[1:7]
return float(s.strip())*100 # 将距离(cm)反馈出去
else:
continue
# 获得距离&尺寸 Distance & Size
# 根据物体的距离算出物体大小
def getDS(res):
ds = {
'd': 0, 's': 0}
L = getDistance()
k1 = (L - 200) * 0.04
S = (k1 + int(res[2])) * 3.04 # size
ds['d'] = L
ds['s'] = S
return ds
最最最重要的一步就在这里, 首先从threshold
中取出一种颜色对图像进行二值化处理, 然后用二值化后的图像去判断是什么形状, 将会对每个颜色的二值化图片检测5遍, 将每次检测到的图形和颜色都暂存到一个py字典中, 来增加可能是这种物体的几率. 这样做的好处就是既可以获取到当前物体的颜色形状顺便还可以将检测到的物体的边长记录下来.
def areba(img):
# a=形状 c=color s=shape
a = {
'c': 0, 'r': 0, 't': 0} # circle rect triangle
c = {
'0': 0, '1': 0, '2': 0} # red grenn blue
s = {
} # pix_size
count = 0
# 形状字典擂台,规定次数内谁的命中次数最多就获胜
for i in range(3): # 三种颜色
img = sensor.snapshot()
print(threshold[i])
img = testBinary(img, threshold[i])
for x in range(5): # 五次确认形状
count = count + 1
print("count: ", count)
# 纠结到底要不要再次拍照
img = sensor.snapshot()
img = testBinary(img, threshold[i])
# 暂存结果
cir = isCircle(img)
rect = isRect(img)
tria = isTriangle(img)
if (cir != False):
a['c'] = a['c'] + 1 # 增加形状几率
c[str(i)] = c[str(i)] + 1 # 增加颜色几率
if (s.get(str(cir)) != None): # 已经包含这个Key
s[str(cir)] = s[str(cir)] + 1 # 增加像素尺寸机率 k=数据,v=命中次数
else: # 不包含就创建
s[str(cir)] = 0
s[str(cir)] = s[str(cir)]+1
if (rect != False): # -------- 矩形开始 ---------
a['r'] = a['r'] + 1
c[str(i)] = c[str(i)] + 1
if (s.get(str(rect)) != None): # 已经包含这个Key
s[str(rect)] = s[str(rect)] + 1 # 增加像素尺寸机率 k=数据,v=命中次数
else: # 不包含就创建
s[str(rect)] = 0
if (tria != False): # -------- 三角形开始 ---------
a['t'] = a['t'] + 1
c[str(i)] = c[str(i)] + 1
if (s.get(str(tria)) != None): # 已经包含这个Key
s[str(tria)] = s[str(tria)] + 1 # 增加像素尺寸机率 k=数据,v=命中次数
else: # 不包含就创建
s[str(tria)] = 0
# print("颜色: ", c)
# print("形状: ", a)
# dict(a, **c)
return [a, c, s] # 合并字典 https://www.cnblogs.com/lmh001/p/9888156.html
处理结果集, 判断谁获胜 将获胜方的数据返回出去
重要思想: 颜色形状擂台可以和尺寸距离擂台分别对战,最后两方面的获胜者一定说的是同一个物体
def handle_data(data):
shapes = {
'r': 'Rect', 'c': 'Circle', 't': 'Tirangle'}
colors = ['Red', 'Green', 'Blue']
shape = ''
color = ''
size = ''
max_value = 0
for (k, v) in data[0].items():
if (v > max_value): # update max_value
max_value = v
shape = shapes[k]
max_value = 0
for (k, v) in data[1].items():
if (v > max_value):
max_value = v
color = colors[int(k)]
max_value = 0
for (k, v) in data[2].items():
if (v > max_value):
max_value = v
size = k
return [shape, color, size]
查看我的Gitee:
GFinal.py · Missper/Race_G - 码云 - 开源中国 (gitee.com)
后续可能会使用机器学习,或者小型神经网络来改善这个项目
G题_辽宁赛区_大连理工大学——非接触物体尺寸形态测量 - 2020年TI杯省赛作品交流区 - 全国大学生电子设计竞赛培训网 (nuedc-training.com.cn)
Jupyter Notebook介绍、安装及使用教程 - 简书 (jianshu.com)
关于TensorFlow | TensorFlow中文官网 (google.cn)
OpenMV Cam H7 Plus (edgeimpulse.com)
让数以百万计的Arduino开发者可以在微控制器中可以轻松使用机器学习,只因为有了它 - 知乎 (zhihu.com)
mask-tflite/README_CN.md at main · SingTown/mask-tflite (github.com)
关于TensorFlow | TensorFlow中文官网 (google.cn)
OpenMV Cam H7 Plus (edgeimpulse.com)
让数以百万计的Arduino开发者可以在微控制器中可以轻松使用机器学习,只因为有了它 - 知乎 (zhihu.com)
mask-tflite/README_CN.md at main · SingTown/mask-tflite (github.com)
文章浏览阅读425次。BeautifulSoup学习笔记
文章浏览阅读1.1k次。解决方式:进行序列化在实体类中加上public class User implements Serializable { private static final long serialVersionUID = -2867071904568397169L;}再次启动:可以看到返回了正确的查询结果。
文章浏览阅读1w次,点赞4次,收藏69次。简介使用STM32中TIMER的输入捕获功能可以测量PWM方波的频率和占空比。测量通常有两种方法:中断法和DMA传输法。根据我的经验,中断法最多能测量到150KHz的方波,而DMA方法最多可达1MHz(注:在这种极限状态下,信号占空比太大也容易测量失败)。下面就分别介绍这两种方法。1. 使用STM32CubeMX生成代码首先是选用TIMER,TIMER必须要选用两个相邻通道,并且一个是Input Capture Direct Mode,另一个是Input Capture Indirect Mode。在_stm32方波频率
文章浏览阅读1k次。使用阿里云搭建自己的网站并用阿里云RDS实现站库分离,本文章将从注册域名到部署网站详细地讲解。原文:https://blog.zeruns.tech/archives/513.html什么是站库分离:站库分离就是网站和数据库不在同一个服务器上,数据库用的是内网网络;这样的操作模式更快,更安全;很多大型的企业都采用站库分离的模式。推荐几个网站程序,自己根据需要选择:论坛:DiscuzX博客..._什么是站库分离
文章浏览阅读641次。觉得写得不错,是个方法,转到自己的博客里留着看作者:何新宇链接:https://www.zhihu.com/question/21440067/answer/24522844来源:知乎著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 不同的代码段采用不同的命名长度。通常来说,循环计数器(loop counters)采用1位的单字符来命名,循环判断变量(condi..._写代码的时候函数或者变量怎么命名
文章浏览阅读1.1k次。<template> <view class="page"> <view class="content flex" > <!-- D --> <view class="left" > <view class="left_title" @click="linkA()"> A标题 </view> <view class="left_txt" v-if="type_uni app tablist 多页面
文章浏览阅读1w次,点赞8次,收藏8次。昨天电脑忘关机了,今天登陆本地项目就报这错org.apache.shiro.cache.CacheException: net.sf.ehcache.CacheException: java.io.StreamCorruptedException: invalid stream header: 1B000000大概意思shiro验证ehcache出问题了 ,我清掉 t..._org.apache.shiro.cache.cacheexception: net.sf.ehcache.cacheexception: java.i
文章浏览阅读1.5w次,点赞15次,收藏57次。首先要申明,本人双非研究生,对于我的水平来说。IEEE Access已经很不错了。如果有什么清华北大中科院的大神请绕道。研一的一年中趁着上课的空闲基本弄完了课题。算法结构或者说网络的完成是在2020年疫情7月份的时候。然后就穿插着写论文做实验。之后老师又提出意见,修改然后继续做实验。一直到2021年2月23号才投稿,大半年的时间主要是因为导师在华旧金山大学访学了一年,沟通交流主要靠腾讯会议,日夜颠倒也不是很方便,而且我们一个老师研一研二研三同时带了8个学生,好在我比较积极,一直在催老师。如果你只_ieee access审稿周期
文章浏览阅读348次。Android自定义半圆进度条 半圆渐变色进度条带指示 半圆开口大小可自由修改首先我们来看下效果图不同的开口大小只需要修改一个参数即可半圆1:半圆2:半圆3:如果是你想要的效果,就直接滑动到文章底部,下载源码,然后结合本文章操作说明集成到自己的项目中吧如果集成第一步:下载项目解压,然后用as打开。第二步:复制SemicircleProgressBar这个类到自己的项目中第三步:在需要显示的布局中,..._android 半圆进度条
文章浏览阅读200次。Spring Boot 配置profile多环境支持**前言:**在我们进行项目部署的时候,肯定很多配咋和开发环境是有很多不同的,那么我们第一种方法就是去修改application.properties文件,但是这显然太麻烦了,所以springboot也是支持多环境配置的。1.多profile文件我们在主配置文件编写的时候,文件名可以是application-{proffile}.prope..._sping 3 profile支持
文章浏览阅读1.7k次,点赞2次,收藏4次。参考:https://blog.csdn.net/liuyukuan/article/details/77888169这是一款免费的、Windows平台下开放源代码的热键脚本语言。一、安装使用步骤1:官网下载https://www.autohotkey.com/打开后:直接下载。步骤2:安装。没有特别之处,一路安装即可。步骤3:notepad++或者ed..._autokeypress
文章浏览阅读2.1w次,点赞5次,收藏12次。项目组里有功能需要,当前页面获取url传递过来的参数值,动态赋值给iframe的src,从而实现动态加载iframe的需求,代码如下:<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><html> <head> <title>打印</title> <meta http-equiv="Content-Type" content="text/html; cha_js设置iframe的src