一种基于openmv和触摸屏的动态读取颜色的解决方案

前言:

​ 作为大学生电子设计竞赛控制题选手,常常需要与视觉上位机打交道,openmv作为当前一种开源方案,能够以较低的成本制作,并且官方文档和各种教程丰富,但是苦于光照的影响,程序中预定的阈值往往会出现误差,导致完美运行的工程就此崩塌,故博主以2023年电赛E题为背景,基于B站up主:程欢欢的智能控制集 的开源屏幕方案,设计了一套能够基于触摸屏动态读取阈值的解决方案,该方案成本低廉(屏幕成本大概在25元左右),支持阈值读取,保存,修改,删除等,并可以DIY扩展多阈值

原理:

  1. 通过阅读星瞳的官方手册 使用统计信息 · OpenMV中文入门教程 ,我们可以使用统计方法来读取某一RIO区域的各种阈值及其平均数,中位数,众数等,基于此,不难想到我们可以通过这个api来设计一个读取RIO阈值的函数:Get_threshold
  2. 基于B站up主开源的触摸屏方案,我们可以利用其显示摄像头的实时图像,并使用触摸屏操控,设计控件。
  • 至此,我们外部控制设备和设计原理都已经掌握,可以开始设计解决方案了

文件结构:

calibration.py :该脚本记录了电阻屏的按压阈值

get_threshold_v3.py(main.py) :该脚本主要实现了动态阈值的读取,保存,修改,删除等操作

screen.py :该脚本实现了SPI总线与屏幕控制IC和触摸IC的初始化

my_threshold.py :该脚本保存了读取的阈值,包括LAB格式,和灰度格式

get_threshold_v3.py的部分函数实现

1. 串口收发函数:

'''
串口发送函数 请根据实际情况更改发送的数据
功能: 以16进制发送一条位置帧
帧结构: 帧头(0x12,0x13),数据段(data1,data2), 帧尾(0x11)|
参数: data1 : 颜色目标X轴量化坐标 范围:0x00 ~ 0x250
data2 : 颜色目标Y轴量化坐标 范围:0x00 ~ 0x250
异常处理: 当找不到色块时, 输出X == 255, Y == 255,表示色块没有找到
'''
def send_data(data1,data2):
uart.write(bytes([12,13])) # 将整数转换为字节类型(16位)并发送
# print([data1,data2])
uart.write(bytes([data1,data2])) # 将整数转换为字节类型(16位)并发送
uart.write(bytes([11])) # 将整数转换为字节类型(16位)并发送 '''
串口接收函数 请根据实际情况更改
'''
def rece_data():
rece_dat = 0
if uart.any():
rece_dat = int.from_bytes(uart.read(1),'little')
print("已接收到数据{0}".format(rece_dat))#打印接收到的数字
while(uart.any() != 0):
print("out")
uart.read(1)
'''

2. 寻找目标色块函数:

'''
寻找最大色块辅助函数
参数: blobs类型
'''
def find_max(blobs):
max_size = 0
max_blob = 0
for blob in blobs:
if blob[2]*blob[3] > max_size:
max_blob=blob
max_size = blob[2]*blob[3]
return max_blob '''
寻找最大色块函数
参数: img: 图像
threshold: 颜色阈值
'''
def find_maxblobs(img,threshold):
global cnt_find_fail
print(threshold)
blobs = img.find_blobs(threshold,roi = (40,0,240,240))
if blobs:
#如果找到了目标颜色
cnt_find_fail = 0
max_blob = find_max(blobs)
img.draw_rectangle(max_blob.rect(), color = (255,255,255))
#img.draw_edges(b.min_corners(), color=(0,255,0))
img.draw_cross(max_blob.cx(),max_blob.cy(), color = (255,255,255))
# img.draw_string(10,30,str(max_blob.cx()*max_blob.cy()),color=(255,0,0),mono_space=False)
send_data(max_blob.cx() - 40,max_blob.cy())
else:
cnt_find_fail = cnt_find_fail+1
if cnt_find_fail >= 10:
print("find_out")
send_data(255,255)

3. 文件操作函数:

'''
保存阈值函数
注意: !!! 保存后需要重启器件
会在当前目录下生成一个my_threshold.py文件保存get获取的阈值
'''
def save_threshold():
global text
my_threshold = threshold[0] #读取阈值默认保存到列表第一个
my_grayscale_threshold = (my_threshold[0],my_threshold[1])#灰度值等于LAB中的L即亮度值
file_path = "my_threshold.py" #文件地址
f = open(file_path,mode='w')
f.write('my_threshold=' + str(my_threshold))
f.write('\n')
f.write('my_grayscale_threshold=' + str(my_grayscale_threshold))
f.write(text+' linjiejie and bilibili up: 程欢欢')
#写入LAB和灰度阈值
#延时确保文件被保存
sleep(0.5)
f.close()
print("save sccess")
sleep(0.5)
#重置器件
hard_reset() '''
删除阈值函数
注意: !!! 删除后需要重启器件
删除当前目录下的my_threshold.py文件, 当前版本未使用该函数
'''
def del_threshold():
sleep(0.5)
os.remove('my_threshold.py')
hard_reset() '''
读取阈值函数
返回值: 若文件存在,返回文件的LAB阈值(默认),若没有找到则返回(0)元组
如果需要读取的是灰度阈值请自行修改
'''
def read_threshold():
try:
import my_threshold
return my_threshold.my_threshold
except:
print("can't open threshold")
return (0,0,0,0,0,0) '''

4. 控件绘制函数:

'''
控件绘制函数
绘制clear, get, save, size等控件, 可以自定义
参数: img: image类型
'''
def draw_button(img):
#绘制clear
img.draw_circle(mode_btn_x,mode_btn_y,mode_btn_radius,color=(0,255,0),thickness = 3)
img.draw_string(mode_btn_x-10,mode_btn_y-5,'mode',color=(0,255,0),mono_space=False)
#绘制get
img.draw_circle(get_btn_x,get_btn_y,get_btn_radius,color=(255,255,0),thickness = 3)
img.draw_string(get_btn_x-5,get_btn_y-6,'get',color=(255,255,0),mono_space=False)
#绘制save
img.draw_circle(save_btn_x,save_btn_y,save_btn_radius,color=(0,255,255),thickness = 3)
img.draw_string(save_btn_x-7,save_btn_y-6,'save',color=(0,255,255),mono_space=False)
#绘制size
img.draw_circle(size_btn_x,size_btn_y,size_btn_radius,color=(255,180,255),thickness = 3)
img.draw_string(size_btn_x-6,size_btn_y-6,'size',color=(255,180,255),mono_space=False)

5. 阈值读取函数(核心):

'''
采集阈值函数
采集roi方框中的统计数据, 使用均值滤波
参数 n: 需要连续采集n次
img: 摄像头读取的图像image类型
img_drawing_board: 画板image类型, 用于和img做叠加操作, 在屏幕上显示
返回值: get_thresholds 采集到的阈值元组
'''
def Get_threshold(n,img,img_drawing_board):#采集n次
if(last_x == 0 and last_y == 0):
print('error')
return None
#阈值保存变量初始化
get_thresholds = ()
Lmin = 0
Lmax = 0
Amin = 0
Amax = 0
Bmin = 0
Bmax = 0
for i in range(n):
img = sensor.snapshot()
Statistics = img.get_statistics(roi = (last_x,last_y,get_roi_size,get_roi_size))
img.draw_rectangle(last_x,last_y,get_roi_size,get_roi_size,color=(255,255,255))
img.draw_string(10,10,'getting threshold:',color=(255,0,0),mono_space=False)#打印正在获取阈值
print("{0}...".format(i+1))
Lmin += Statistics.l_min()
Lmax += Statistics.l_max()
Amin += Statistics.a_min()
Amax += Statistics.a_max()
Bmin += Statistics.b_min()
Bmax += Statistics.b_max()
img.b_nor(img_drawing_board) #将画板画布与感光器图像叠加仅支持二值图像(黑白图像)
screen.display(img)
#均值滤波
Lmin //= n
Lmax //= n
Amin //= n
Amax //= n
Bmin //= n
Bmax //= n
get_thresholds = (Lmin,Lmax,Amin,Amax,Bmin,Bmax)
print(get_thresholds)
return get_thresholds
  • !!!注意,博主这里仅使用最简单的均值滤波,请根据自己需要修改滤波函数

6. 主函数:

def get_main():
global last_x
global last_y
global rectangle_flag
global threshold
global state
global get_roi_size
highlight_threshold = [(74, 100, -4, 6, -17, 2)] #高光阈值
screen.init()
threshold[0] = read_threshold()#读取文件中的阈值
print(threshold)
img_drawing_board=sensor.alloc_extra_fb(320,240,sensor.RGB565)
img_drawing_board.draw_rectangle(0,0,320,240,fill=True,color=(255,255,255))
#初始化画布 while(True):
#这里state 为0和1都覆盖高光是因为电阻屏四周灵敏度差,容易误触
#这里的状态可以根据自己的需要选择删除, 设置这里的黑色覆盖是为了更加直观的在屏幕中显示目标色块与实际检测结果在图像中的分布情况, 以帮助我们来调试和选择设置方框大小
if state == 0:
img = sensor.snapshot().binary([highlight_threshold[0]], invert=False, zero=True)
elif state == 1:
img = sensor.snapshot().binary([highlight_threshold[0]], invert=False, zero=True)
elif state == 2:
img = sensor.snapshot().binary([threshold[0]], invert=False, zero=True)
# img = sensor.snapshot()#.binary([(86, 254)])二值化操作
# img = img.gaussian(3)
if screen.press:
#判断mode按键
if (mode_btn_x-mode_btn_radius<screen.x<mode_btn_x+mode_btn_radius) and (mode_btn_y-mode_btn_radius<screen.y<mode_btn_y+mode_btn_radius):
img_drawing_board.draw_rectangle(0,0,320,240,fill=True,color=(255,255,255))#将img_drawing_board设置为全黑
rectangle_flag = 0#方框数清零
state = (state + 1)%3
#判断get按键
if (get_btn_x-get_btn_radius<screen.x<get_btn_x+get_btn_radius) and (get_btn_y-get_btn_radius<screen.y<get_btn_y+get_btn_radius):
threshold[0] = Get_threshold(100,img,img_drawing_board)#将读取的阈值保存读阈值列表[0]中, 请根据自己的需要修改逻辑和列表大小
print(threshold[0])
highlight_threshold[0] = Get_threshold(100,img,img_drawing_board)#将读取的阈值保存读高光阈值列表[0]中, 请根据自己的需要修改逻辑和列表大小
# print(highlight_threshold) #判断save按键
if (save_btn_x-save_btn_radius<screen.x<save_btn_x+save_btn_radius) and (save_btn_y-save_btn_radius<screen.y<save_btn_y+save_btn_radius):
save_threshold() #判断size按键
if (size_btn_x-save_btn_radius<screen.x<size_btn_x+save_btn_radius) and (size_btn_y-save_btn_radius<screen.y<size_btn_y+save_btn_radius):
get_roi_size = 10 + (get_roi_size + 5) % 35
#长按处理
cnt_press = cnt_press + 1
img.draw_cross(screen.x,screen.y)
if cnt_press > press_limit :
if rectangle_flag > 1:
img_drawing_board.draw_rectangle(last_x,last_y,get_roi_size,get_roi_size,color=(255,255,255))
rectangle_flag = 1
# pass img_drawing_board.draw_rectangle(screen.x,screen.y,get_roi_size,get_roi_size,color=(0,0,0))
rectangle_flag = rectangle_flag + 1
last_x = screen.x
last_y = screen.y
cnt_press = 0
else:
cnt_press = 0
find_maxblobs(img,threshold) #匹配最大色块
img.b_nor(img_drawing_board) #将画板画布与感光器图像叠加仅支持二值图像(黑白图像)
fps=clock.fps() #获取帧速
img.draw_string(10,10,'FPS:'+str(fps),color=(255,0,0),mono_space=False) #绘制帧速
img.draw_string(10,30,'now_size:'+str(get_roi_size),color=(255,180,255),mono_space=False) #当前采样矩形大小
img.draw_string(10,20,'now_mode:'+str(state+1),color=(255,180,255),mono_space=False) #当前采样矩形大小
draw_button(img)
screen.display(img) #显示图像到屏幕,并获取触摸信息。不运行此函数,不会更新触屏信息。 get_main()

博主这里提供一个简单参考mian函数,请根据自己的需要修改或重写该函数

实现效果

如何使用

1.初始化:

当我们拿到代码时,只有get_threshold_v3.py(main.py)和screen.py,我们缺少calibration.py和calibration.py

  • 缺少calibration.py :我们会进入下面的触摸IC初始化界面,根据提示按压初始化IC压力参数(强行重启openmv)即可

  • 缺少my_threshold.py :没有影响,按照下面的控件使用教程读取阈值即可

2.控件:

  • size控件单击生效,用来修改采集阈值RIO区域的大小
  • save控件单击生效,保存已经采集的阈值(openmv会强行重启)
  • mode控件:单击切换,上机默认mode1(使用黑色覆盖高光区域),可以单击切换mode2(将目标颜色和实际颜色相同的像素点用黑色覆盖)
  • get 控件单击生效,在使用前需要在想要采集的屏幕位置上长按一段时间,出现一个小矩形白框,白框就是采集阈值RIO区域,然后单击get即可,值得注意的是博主这里实现的长按是基于openmv帧率的(mcpy在openmv上的定时器太难用了),如果觉得长按时间太长可以修改 press_limit 变量来修改长按时间
  • Q&A:为什么有方框指示了还要用黑色覆盖呢?
  • 为了能够更直观的看见目标颜色与实际情况,方便我们进行调试,方框显示会有偏差

触摸屏硬件获取

程欢欢OpenMV触屏扩展板(复刻) - 立创开源硬件平台

版本限制

该脚本是在博主大二(2024)时编写的,受限与当时的知识储备,和当时程欢欢屏幕固件的版本限制,该屏幕固件仅支持openmv固件4.3.3之前版本,如果想要使用,可以下载旧版的openmvIDE为手上的openmv烧录旧版本固件(IDE版本大概为3.3左右),或者下载程欢欢的新版屏幕固件,并根据代码修改即可使用

程欢欢 gitee仓库地址

2023电赛E题: 2023电赛E题,关注B站‘程欢欢的智能控制集’

程欢欢/OpenMV-resistive_touch_screen - 码云 - 开源中国

【开源】OpenMV触屏扩展板,只要26块!额外预留三路舵机+一路UART

源码获取

通过网盘分享的文件:openmv动态读取脚本

链接: https://pan.baidu.com/s/1rgaN4LVZOnnrtR-iTfycAw 提取码: mcpy

如果有疑问可以通过邮箱与我交流

1309909195@qq.com

结尾

一种基于openmv和触摸屏的动态读取颜色的解决方案的更多相关文章

  1. 一种基于RBAC模型的动态访问控制改进方法

    本发明涉及一种基于RBAC模型的动态访问控制改进方法,属于访问控制领域.对原有RBAC模型进行了权限的改进和约束条件的改进,具体为将权限分为静态权限和动态权限,其中静态权限是非工作流的权限,动态权限是 ...

  2. tmpfs:一种基于内存的文件系统

    tmpfs是一种基于内存的文件系统, tmpfs有时候使用rm(物理内存),有时候使用swap(磁盘一块区域).根据实际情况进行分配. rm:物理内存.real memery的简称? 真实内存就是电脑 ...

  3. 一种基于Qt的可伸缩的全异步C/S架构服务器实现(流浪小狗,六篇,附下载地址)

    本文向大家介绍一种基于Qt的伸缩TCP服务实现.该实现针对C/S客户端-服务集群应用需求而搭建.连接监听.数据传输.数据处理均在独立的线程池中进行,根据特定任务不同,可安排负责监听.传输.处理的线程数 ...

  4. 一种基于Qt的可伸缩的全异步C/S架构server实现(一) 综述

    本文向大家介绍一种基于Qt的伸缩TCP服务实现.该实现针对C/Sclient-服务集群应用需求而搭建. 连接监听.传输数据.数据处理均在独立的线程池中进行,依据特定任务不同,可安排负责监听.传输.处理 ...

  5. "《算法导论》之‘队列’":队列的三种实现(静态数组、动态数组及指针)

    本文有关栈的介绍部分参考自网站数据结构. 1. 队列  1.1 队列的定义 队列(Queue)是只允许在一端进行插入,而在另一端进行删除的运算受限的线性表. (1)允许删除的一端称为队头(Front) ...

  6. 腾讯优图&港科大提出一种基于深度学习的非光流 HDR 成像方法

    目前最好的高动态范围(HDR)成像方法通常是先利用光流将输入图像对齐,随后再合成 HDR 图像.然而由于输入图像存在遮挡和较大运动,这种方法生成的图像仍然有很多缺陷.最近,腾讯优图和香港科技大学的研究 ...

  7. 26种基于PHP的开源博客系统

    26种基于PHP的开源博客系统 来源:本站原创 PHP学习笔记 以下列举的PHP开源Blog系统中,除了我们熟知的WordPress之外,大多都没有使用过,其中一些已经被淘汰,或者有人还在使用.除了做 ...

  8. Hive数据分析——Spark是一种基于rdd(弹性数据集)的内存分布式并行处理框架,比于Hadoop将大量的中间结果写入HDFS,Spark避免了中间结果的持久化

    转自:http://blog.csdn.net/wh_springer/article/details/51842496 近十年来,随着Hadoop生态系统的不断完善,Hadoop早已成为大数据事实上 ...

  9. 一种基于Qt的可伸缩的全异步C/S架构服务器实现(一) 综述

    本文向大家介绍一种基于Qt的伸缩TCP服务实现.该实现针对C/S客户端-服务集群应用需求而搭建.连接监听.数据传输.数据处理均在独立的线程池中进行,根据特定任务不同,可安排负责监听.传输.处理的线程数 ...

  10. AMAP-TECH算法大赛开赛!基于车载视频图像的动态路况分析

    阿里巴巴高德地图AMAP-TECH算法大赛于7月8日开启初赛,赛题为「基于车载视频图像的动态路况分析」,活动邀请了业界权威专家担任评委,优秀选手不仅可以瓜分丰厚的奖金,领取荣誉证书,还有机会进入高德地 ...

随机推荐

  1. 2025dsfz集训Day11:数位DP、状态压缩DP、单调队列优化DP

    Day11:数位DP.状压DP.单调队列优化DP 经典题目:AccodersP2195 |[一本通提高数位动态规划]Amount of Degrees 题意: 求出区间 \([x,y]\) 中满足下面 ...

  2. Nim 语言新的性能测试

    今天将 性能测试网站: benchmarks game 上一个关于 n-body 的题目改成 nim 1.6.4 语言来编写. 注意,我是基于 java 的版本来写的,没有像  c++ 那样的版本使用 ...

  3. 银河麒麟v10 sysctl内核参数加载顺序的思考

    背景 最近很多伙伴想使用银河麒麟高级服务器系统v10来部署最新版本的k8s集群,可能遇到了各式各样的问题,于是准备使用kylinOS v10重温一遍kubeadm部署最新版本k8s的流程,也是替大家踩 ...

  4. Oracle 使用UTL_HTTP发送http请求--转载

    参考:https://blog.csdn.net/tmaczt/article/details/82665885 GET方式 CREATE OR REPLACE FUNCTION FN_HTTP_GE ...

  5. java springboot图片上传和访问

    上传 @RequestMapping("/uploadImg") public Result uploadImg(HttpServletRequest request, Multi ...

  6. [译]MIT6.824_1.1分布式系统介绍——驱动力与挑战

    这是6.824分布式系统课程,我会开始用简明的介绍我所认为的分布式系统. 分布式系统的核心是通过网络以完成一致任务的一组协作计算机. 因此我们将在本课程中重点介绍各种实例,例如大型网站的存储或MapR ...

  7. Prompt 生产及提示词案例(含完整提示词)

    场景一:格式文本提取提示词 # 角色 Role - 你是一个高度精确的文本提取代理. # 背景 Background - 你将接收到两段不同的文本: 1. 一份**按序号排列的列表**,详细说明了需要 ...

  8. 【中英】【吴恩达课后测验】Course 1 - 神经网络和深度学习 - 第四周测验

    [中英][吴恩达课后测验]Course 1 - 神经网络和深度学习 - 第四周测验 上一篇:[课程1 - 第三周编程作业]※※※※※ [回到目录]※※※※※下一篇:[课程1 - 第四周编程作业] 第4 ...

  9. 杂七杂八系列----C#代码如何影响CPU缓存速度?

    CPU与RAM的隔阂 CPU与RAM是两个独立的硬件,并非集成在一起.所以他们两个之间一定会存在一个连接的桥梁,这个桥梁的名字叫做内存总线. 内存总线由三部分组成: 地址总线(Address Bus) ...

  10. 使用RandomAccessFile监听日志文件,并实时一行行读取出来

    public static void main(String[] args) { String filePath = "E:\\codes\\work\\product-parent\\lo ...