OPenMV介绍
OpenMV是基于Python的嵌入式机器视觉模块,目标是成为机器视觉界的“Arduino”。它成本低易拓展,开发环境友好,除了用于图像处理外,还可以用Python调用其硬件资源,进行I/O控制,与现实世界进行交互。
一、如何通过串口发送数据—-UART
1、UART用法读写操作
uart.read(10) # read 10 characters, returns a bytes object
# 读入10个字符, 返回一个比特对象
uart.read() # read all available characters
# 读取所有的有效字符
uart.readline() # read a line
# 读入一行
uart.readinto(buf) # read and store into the given buffer
# 读入并且保存在缓存中
uart.write('abc') # write the 3 characters
# 向串口写入3个字符abc
2、单个字符的读取与写入
uart.readchar() # read 1 character and returns it as an integer
# 读入一个字符
uart.writechar(42) # write 1 character
# 写入ASCALL码为42的字符
3、判断串口是否有数据
uart.any() # returns the number of characters waiting
案例:
串口简单用例—》向串口发送数据的代码
# UART Control 串口通信
import time
from pyb import UART
uart = UART(3, 115200)
uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters
while(True):
uart.write("Hello World!\r")
print("Hello World")
# 休眠1s
time.sleep(1000)
串口简单用例—》向串口接收数据
# UART Control 串口通信
import time
from pyb import UART
uart = UART(3, 115200)
uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters
while(True):
if uart.any():
# 判断是否有数据, 有的话就读入
tmp_data = uart.readline()
print(tmp_data)
# 休眠1s
time.sleep(1000)
二、如何通过OPenMV摄像头获取小球坐标
1、矩形绘制
/*
* x: 矩形的左上角 x坐标
* y:矩形的左上角 y坐标
* width:矩形的宽度
* height: 矩形的高度
*/
image.draw_rectangle([x, y, width, height])
2、十字绘制
/*
* x: 十字的x坐标
* y: 十字的y坐标
*/
image.draw_cross(x, y)
3、find_blobs函数
find_blobs(thresholds, invert=False, roi=Auto)
thresholds 颜色阈值 元组数组
invert=1 反转颜色阈值,invert=False默认不反转.
roi 设置颜色识别的视野区域,roi是一个元组, roi = (x, y, w, h),代表从左上顶点(x,y)开始的宽为w高为h的矩形区域,roi不设置的话默认为整个图像视野。
4、Blob 对象
{x:26, y:54, w:83, h:38, pixels:448, cx:65, cy:75, rotation:0.342305, code:1, count:1}
- x 代表矩形区域的左上角的x坐标
- y 代表矩形区域的左上角的y坐标
- w 矩形区域的宽度
- h 矩形区域的高度
- pixels 目标区域满足图像阈值范围的像素点的个数
- cx center x, 矩形区域中心的x坐标
- cy center y, 矩形区域中心的y坐标
- rotation 代表目标颜色区域blob的旋转角度
- code 代表颜色的编号, 它可以用来分辨这个这个矩形区域是用哪个threshold识别出来的
count blob内包含的子blob个数。
注:rotation 单位是弧度值,如果识别的物体是长条状的,例如钢笔, 那其取值范围为0-180度,如果是圆形的画, 其rotaiton值没有任何意义。
code 颜色编号 取值范围 2^n : 1,2, 4, 8
count 如果merge=False count恒等于1, 如果开启色块合并merge=True 则>=1
案例:
# Blob Detection Example
#
# This example shows off how to use the find_blobs function to find color
# blobs in the image. This example in particular looks for dark green objects.
import sensor, image, time
# For color tracking to work really well you should ideally be in a very, very,
# very, controlled enviroment where the lighting is constant...
red_threshold_01 = (0, 35, 0, 50, -10, 40)
#设置红色的阈值,括号里面的数值分别是L A B 的最大值和最小值(minL, maxL, minA,
# maxA, minB, maxB),LAB的值在图像左侧三个坐标图中选取。如果是灰度图,则只需
#设置(min, max)两个数字即可。
# You may need to tweak the above settings for tracking green things...
# Select an area in the Framebuffer to copy the color settings.
sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.RGB565) # use RGB565.
sensor.set_framesize(sensor.QQVGA) # use QQVGA for speed.
sensor.skip_frames(10) # Let new settings take affect.
sensor.set_auto_whitebal(False)
#关闭白平衡。白平衡是默认开启的,在颜色识别中,需要关闭白平衡。
clock = time.clock() # Tracks FPS.
while(True):
clock.tick() # Track elapsed milliseconds between snapshots().
img = sensor.snapshot() # Take a picture and return the image.
# pixels_threshold=100, area_threshold=100
blobs = img.find_blobs([red_threshold_01], area_threshold=150)
if blobs:
#如果找到了目标颜色
print(blobs)
for b in blobs:
#迭代找到的目标颜色区域
# Draw a rect around the blob.
img.draw_rectangle(b[0:4]) # rect
#用矩形标记出目标颜色区域
img.draw_cross(b[5], b[6]) # cx, cy
#在目标颜色区域的中心画十字形标记
print(clock.fps()) # Note: Your OpenMV Cam runs about half as fast while
# connected to your computer. The FPS should increase once disconnected.
三、综合案例
main.py文件如下:
# main.py -- put your code here!
import sensor, image, time
import protocol
import utils
from pyb import UART
import json
sensor.reset() #摄像头初始化
sensor.set_pixformat(sensor.RGB565) #设置为彩色模式
sensor.set_framesize(sensor.QQVGA) #画幅为QQVGA即分辨率为160*120
sensor.skip_frames(time = 2000) #跳过起始画面,获取稳定图像
sensor.set_auto_gain(False) #在色块检测模式下关闭自动补光
sensor.set_auto_whitebal(False) #关闭白平衡
clock = time.clock()
#根据测量设备小球颜色,并把LAB色值存储到集合中
red_threshold = ( 65,75,-60,-40,30,48) #设置监测红色色块阈值
green_threshold = ( 65,75,-60,-40,30,48) #设置检测绿色色块阈值
blue_threshold = ( 65,75,-60,-40,30,48) #设置监测蓝色色块阈值
yellow_threshold = ( 65,75,-60,-40,30,48) #设置监测黄色色块阈值
# 初始化各坐标值
xPositionNow = 0
yPositionNow = 0
xPositionLast = 0
yPositionLast = 0
imageSize = 128
count=0
#存储每个小球的坐标
matrix = [[] for i in range(10)]
index=0 #识别次数
xIndex=protocol.getColor() #获取要识别颜色小球
print('xIndex',xIndex)
#判断是要识别什么颜色的小球
xThreshold=(0,0,0,0,0,0) #默认值
if xIndex==0x01:
xThreshold=red_threshold #红色
print('当前要识别的小球颜色是红色')
elif xIndex==0x02:
xThreshold=green_threshold #绿色
print('当前要识别的小球的颜色是绿色')
elif xIndex==0x03:
xThreshold=blue_threshold #蓝色
print('当前要识别的小球的颜色是蓝色')
elif xIndex==0x04:
xThreshold=yellow_threshold #黄色
print('当前要识别的小球的颜色是黄色')
else:
xThreshold=red_threshold #红色
print('默认颜色是红色')
while(True):
clock.tick()
img = sensor.snapshot() #获取当期所采集到的图像快照
# 设置色块阈值,具体数值情况可以通过OpenMVIDE中的阈值调整功能来得出
# 工具 → Mechine Vision → Threshold Editor
# area_threshold面积阈值设置为100 ,如果色块被面积小于100,则会被过滤掉
# pixels_threshold 像素个数阈值,如果色块像素数量小于这个值,会被过滤掉
# merge 设置为True,合并所有重叠的寻找到的blob为一个色块
#识别
blobs = img.find_blobs([xThreshold], pixels_threshold=10, area_threshold=10, merge=True)
if blobs:
print('sum ==================================', len(blobs),'\n\r')
count=0
for blob in blobs:
# 绘制相应的图形,方便我们测试的时候使用
img.draw_cross(blob.cx(), blob.cy())
#用矩形标记出目标颜色区域
img.draw_rectangle(blob[0:4]) # rect
#在目标颜色区域的中心画十字形标记
img.draw_circle(blob[5], blob[6],10, color = (255, 0, 0))
x = blob.cx() - (imageSize/2)
y = (imageSize/2) - blob.cy()
xPositionLast = xPositionNow
yPositionLast = yPositionNow
# 这个0.7的数值不固定,只是在调试的时候为了使得像素点和坐标单位cm匹配所设置的数值
xPositionNow = x * 0.7
yPositionNow = y * 0.7
# 测试时打印出当前坐标
#print(xPositionNow)
#print(yPositionNow)
#print(xPositionNow, yPositionNow, end = ',')
output_str="[%d,%d]" % (blob[5], blob[6])
print(index,output_str)
#print(',' + str(xPositionNow) + ',' + str((-1)*yPositionNow) + ',')
#识别五次就退出循环
count=count+1
if count<=5:
matrix[index].append(blob[5])
matrix[index].append(blob[6])
time.sleep(1000)
#识别次数
index=index+1
print('第几次识别',index)
if index==10:
index=0
#break
protocol.py(UATR通讯类)
# protocol - By: LW - 周四 4月 26 2018
from pyb import UART
import time
import utils
#串口通讯
uart = UART(3, 115200) #设置为串口3、波特率为115200发送数据
uart.init(115200, bits=8, parity=None, stop=1)
#返回当前指令数据
def getColor():
xColor=receive() #xColor识别小球的颜色
xIndex=0x01 #默认颜色
if xColor: #不为空
print('xColor 不为空')
return readColor(xColor) #xIndex识别小球的角标
else:
print('xColor 为空')
return xIndex
#获取当前指令是要识别什么颜色
#[0x22,0xFF,0X01,0X00,0X00,0X00,0X01,0XBB]
def readColor(instruct=[]):
#判断指令的长度
if len(instruct)<8:
print('读取的协议有问题!')
print(instruct[0])
print(instruct[1])
if instruct[0]!="0x22" or instruct[1]!="0xFF":
#如果不是规定的协议,就从新开启监听获取协议
print('协议有问题!')
return getColor()
return filterColor(instruct[2])
#判断当前是什么颜色#默认为红色
#红色:0x01;绿色:0x02;蓝色:0x03;黄色:0x04;
def filterColor(isColor):
#把十六进制转换为十制数
colorStyle=int(isColor,16)
if colorStyle<0x05:
return colorStyle
else:
return 0x01 #默认为红色
#识别成功
#size:小球个数
def sendSuccessFour(size):
sendFour(0x01,size)
#types:小球颜色 size小球个数
def sendSuccessEight(types,size):
sendEight(types,0x01,size)
#识别失败
def sendFailFour():
sendFour(0x00,0x00)
#识别失败
def sendFailEight(types):
sendEight(types,0x00,0x00)
#当前OPenMV根据给定的颜色识别,返回4个字节指令(协议)
#tag:成功标记【0x01成功、0x00失败】
#size:小球的数量
def sendFour(tag,size):
sendData=[0x55,0xFF]
sendData.append(tag)
sendData.append(size)
sendArray(sendData)
#当前OPenMV根据给定的颜色识别,返回8个字节指令(协议)
#type:识别那种类型的小球
#tag:成功标记【0x01成功、0x00失败】
#size:小球的数量
def sendEight(types,tag,size):
#指令类型
sendData=[0x22,0xFF,0X00,0X00,0X00,0X00,0X01,0XBB]
sendData[2]=types
sendData[3]=tag
sendData[4]=size
sendArray(sendData)
#发送指令(默认发送5次)
#data[] 数组
def sendArray(data=[]):
send(5,data)
#发送指令
#data 字符串
def sendReturn(data):
uart.write(data+'\r\n')
#发送指令
#data[] 数据
#sendSize发送次数
def send(sendSize,data=[]):
#十六进制数组转换为十六进制字符串
buf=utils.arrayIntToStr(data)
#每个一秒发送一次数据
while sendSize>0:
uart.write(buf+'\r\n')
sendSize=sendSize-1
print('准备发送指令!',buf)
time.sleep(1000)
#接收指令(一直接受数据)
def receive():
#一直循环获取数据
while(True):
print('接收中!')
# 休眠1s
time.sleep(1000)
# 判断是否有数据, 有的话就读入
if uart.any():
#读取有效数据
buf=[]
for n in range(uart.any()):
buf.append(uart.readchar())
bufStr=utils.arrayIntToStr(buf)
bufarray=utils.strToArrayHex(bufStr)
print('接收到指令!',bufarray)
#返回数据当前接收到数据
sendReturn(bufStr)
print('返回接收到指令!',bufStr)
return bufarray
utlis.py(工具类)如下:
# Untitled - By: LW - 周六 4月 28 2018
#
# 数据类型转换
#
#十六进制转换为整数
def hexToInt(hexStr):
return int(hexStr,16)
#八进制转换为整数
def octToInt(octStr):
return int(octStr,8)
#整数转换为十六进制
def intToHex(number):
return hexFilter(hex(unmber))
#整数转换为八进制
def intToOct(number):
return oct(number)
#十进制数组转换为十六进制数组
def arrayIntToArrayHex(arrayInt=[]):
for a in range(len(arrayInt)):
arrayInt[a]=intToHex(arrayInt[a])
return arrayInt
#十六进制字符串转换为整形数组
def strToArrayInt(bexStr):
if bexStr.strip()=='':
print('strToArrayInt参数为空!')
return None
#如果不是偶数,直接跳出
if len(bexStr)%2!=0: return None
#创建数组
arrayBex=[]
#转换整形为数组
for b in range(len(bexStr)/2):
ints=bexStr[b*2 :(b*2)+2]
arrayBex.append(int(ints,16))
return arrayBex
#十六进制字符串转换为十六进制数组
def strToArrayHex(bexStr):
if bexStr.strip()=='':
print('strToArrayHex参数为空!')
return None
#创建数组
arrayBex=[]
#如果不是偶数,直接跳出
if (len(bexStr)%2)!=0: return None
#转换十六进制为数组
for b in range(len(bexStr)/2):
hexs=bexStr[b*2:(b*2)+2]
arrayBex.append('0x'+hexs)
return arrayBex
#十六进制数组转换为十六进制字符串
def arrayBexToStr(arrayBex=[]):
if not arrayBex:
print('ArrayBexToStr参数为空!')
return None
#字符串拼接结果
result=''
for b in range(len(arrayBex)):
#十六进制格式转换
print(arrayBex[b])
result+=hexFilter(arrayBex[b])
print('arrayBexToStr Funtion returns to=====>'+result)
return result
#十进制数组转换为十六进制字符串
def arrayIntToStr(arrayInt=[]):
if not arrayInt:
print('ArrayBexToStr参数为空!')
return None
#字符串拼接结果
result=''
for b in range(len(arrayInt)):
#转换为十六进制
hexStr=hex(arrayInt[b])
#十六进制格式转换
result+=hexFilter(hexStr)
print('arrayIntToStr Funtion returns to=====>'+result)
return result
#十六进制格式转换0XAA==>AA
def hexFilter(hexStr):
#转换为大写
hexStr=hexStr.upper()
#字符串中包含OX
if '0X' in hexStr:
result=hexStr[2:len(hexStr)]
if len(result)==1:
result='0'+result
return result
#字符串长度为一的时候
elif len(hexStr)==2:
return hexStr
#字符串长度为二的时候
elif len(hexStr)==1:
return '0'+hexStr
else:
return ''