24电赛E题视觉(OPENMV4)分享
前言
本人与两位队友均为暑假大一升大二,第一次参加电赛与分享经验,坐标江苏,希望大家一起交流进步!八月六号结果出来也是很幸运的获得了省一等奖(开心)!
视觉部分
闲聊
29日上午题目出来后我们本意是选H题小车(因为我们的赛前准备工作是以小车为载体的),然后也忙活了大半个上午搭TI环境。后经过午饭看了一下三子棋题目,发现也能做,便觉得不再受TI鸟气直奔E题!(可惜的是我刚把keil删了准备换新版本就换题目了),饭后认认真真看了下题目,在和队友商讨了方案以后,视觉部分我只需要发送坐标给主控让他可以换算出实际坐标即可,以及我还需发送每一个方格内黑白棋子状况。这么一看,着实简单?
找棋盘算法
方案1
对于找棋盘算法,一开始我选择的是find_rects()函数,可以直接用。再通过调整面积各种限定条件找到合适大小的矩形框即可?可是在实际操作起来,因为机械结构还没有搭建起来,光线等各种原因导致识别效果一直拉跨。(大佬知道有解决办法请多指教)故放弃。
#引入必要模块和方法
import pyb,math,sensor,image,time#必要模块
from pyb import UART#用于串口发送
import json,ustruct#用于打包数据
#摄像头初始化
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QQVGA)
sensor.skip_frames(time = 2000)
sensor.set_auto_whitebal(True) # 自动白平衡功能(颜色识别需用)
sensor.set_auto_gain(True) # 关闭图像传感器的自动增益(颜色识别需用)
sensor.set_brightness(-3)
sensor.set_contrast(-3)
#时钟
clock = time.clock()
#像素长(w)x,高(h)y,QQVGA固定为160*120,此值固定不变
pixel_x=160
pixel_y=120
# ---矩形框部分---
#正摆时像素系数从零到1,可变
factor_x=1.0
factor_y=1.0
#斜摆时像素系数从0到1,可变
factor_left_x=1.0
factor_right_x=1.0
factor_top_y=1.0
factor_bottom_y=1.0
#---矩形框相关函数---
color_thresholds=[18, 83, -20, 18, -18, 20]
#判别最大矩形的大小,并将最大矩形及角点框出,从左上开始顺时针输出角点xy坐标
def find_rect_x_y(choice):
global RT_x,RB_x,LT_x,LB_x,LT_y,RT_y,LB_y,RB_y
img = sensor.snapshot()
# 下面的`threshold`应设置为足够高的值,以滤除在图像中检测到的
# 低边缘幅度的噪声矩形。最适用与背景形成鲜明对比的矩形。
[[LB_x,LB_y],[RB_x,RB_y],[RT_x,RT_y],[LT_x,LT_y]]=find_max_rect(img)
#如果逆时针转
left_dx=RT_x-LT_x
right_dx=RB_x-RT_x
higher_dy=RT_y-LT_y
lower_dy=LB_y-LT_y
#矩形的大小判断,公式factor_x=x/pixel_x,factor_y=y/pixel_y
#如果正着摆放
if choice==1:
factor_x=0.51
factor_y=0.63
if (left_dx+right_dx)>56 and (higher_dy+lower_dy)<80:
#if left_dx>=pixel_x*(factor_x-0.1)and left_dx<=pixel_x*(factor_x+0.1) and lower_dy>=pixel_y*(factor_y-0.1) and lower_dy<=pixel_y*(factor_y+0.1) :
print(LT_x,LT_y,RT_x,RT_y,RB_x,RB_y,LB_x,LB_y)
rects=img.find_rects(threshold = 10000)
max_rect=find_maxr(rects)
img.draw_rectangle(max_rect.rect(), color = (255, 0, 0))
img.draw_circle(LT_x,LT_y,2,color = (0, 0, 255), thickness = 2, fill = False)
#如果顺时针转,顺时针45度左右
elif choice==2:
left_dx=LT_x-LB_x
right_dx=RT_x-LT_x
higher_dy=RT_y-LT_y
lower_dy=RB_y-RT_y
factor_x=0.50
factor_y=0.62
if (left_dx+right_dx)>80 and (left_dx+right_dx)<105:
#if right_dx>=pixel_x*(factor_x-0.1)and right_dx<=pixel_x*(factor_x+0.1) and lower_dy>=pixel_y*(factor_y-0.1) and lower_dy<=pixel_y*(factor_y+0.1) :
print(LT_x,LT_y,RT_x,RT_y,RB_x,RB_y,LB_x,LB_y)
rects=img.find_rects(threshold = 10000)
max_rect=find_maxr(rects)
img.draw_rectangle(max_rect.rect(), color = (255, 0, 0))
#如果逆时针转,逆时针45度左右
elif choice==3:
left_dx=RT_x-LT_x
right_dx=RB_x-RT_x
higher_dy=LT_y-LT_y
lower_dy=LB_y-LT_y
factor_x=0.31
factor_y=0.40
if (left_dx+right_dx)>80 and (left_dx+right_dx)<105:
#if left_dx>=pixel_x*(factor_x-0.2)and left_dx<=pixel_x*(factor_x+0.2) and higher_dy>=pixel_y*(factor_y-0.25) and higher_dy<=pixel_y*(factor_y+0.25) :
print(LT_x,LT_y,RT_x,RT_y,RB_x,RB_y,LB_x,LB_y)
rects=img.find_rects(threshold = 10000)
max_rect=find_maxr(rects)
img.draw_rectangle(max_rect.rect(), color = (255, 0, 0))
k = [LT_x,LT_y,RT_x,RT_y,RB_x,RB_y,LB_x,LB_y]
return k
#描点函数,传入x1,y1,x2...,不加括号
def draw_crosses(k):
index=0
while index < len(k):
img.draw_cross(k[index],k[index+1])
index=index+2
#返回最大矩形角点
def find_max_rect(img):
rects=img.find_rects(threshold = 10000)
if rects:
max_rect=find_maxr(rects)
# img.draw_rectangle(max_rect.rect(), color = (255, 0, 0))
point=max_rect.corners()
return point[0],point[1],point[2],point[3]
else:
return (-1,-1),(-1,-1),(-1,-1),(-1,-1)
#判断最大矩形
def find_maxr(rects):
global max_size
max_size=0
global max_rect
for rect in rects:
x,y,w,h=rect.rect()
if w*h > max_size:
max_rect=rect
max_size = w*h
return max_rect
# ---串口部分---
uart=UART(3,9600)
uart.init(9600,bits=8,parity=None,stop=1)
#串口发送一个字节
def sending_data(num):
global uart;#?
data=ustruct.pack("<b",int(num))#帧头+数据+帧尾
uart.write(data);
#串口接收一个字节
def get_data():
global pattern
getrx = uart.readchar()#获取char类型数据
if getrx == 0 or getrx==1 or getrx==2 or getrx==3:
pattern = getrx
return pattern
#发送外框坐标
def sending_rectangle():
k=find_rect_x_y(3)
sending_data(0xff)
sending_data(k[0])
sending_data(k[1])
sending_data(k[2])
sending_data(k[3])
sending_data(k[4])
sending_data(k[5])
sending_data(k[6])
sending_data(k[7])
sending_data(0xfe)
sending_data(0xfe)
# ---主循环---
while True:
clock.tick()
find_rect_x_y(2)
方案2
之后我想到了find_blobs()函数,因为黑线分割出来多个棋盘就可以了,分别找色块即可。可实施起来依旧困难,光线什么的也不确定最后也就不了了之了。(其实是因为懒得调)
方案3
image.find_line_segments()函数,最后我也确实是用的这个,找多条线段求斜率换算得到矩形框坐标。
识别棋子算法
方案1
找圆形。
方案2
找黑白色块
方案3
统计区域内lab信息