OpenMV物品识别(以网球为例、模型训练)

OpenMV物品识别(以网球为例、模型训练) 目录-----接单片机设计DPJSJ0X78一、识别目标二、可供采取的识别方式1. 阈值识别2. 多阈值识别3. 多阈值加最多像素点识别4. 训练神经网络目标检测重点 三、单阈值识别1. 网球阈值获取2. 代码编写3. 识别效果四、多阈值识别加最多像素点识别1. 阈值获取2. 代码编写3. 效果演示五、训练神经网络目标检测1. 获取数据集数据集采集使用方法2. 训练神经网络目标检测数据标注标记错误解决方法训练模型选择模型导入模型3. 代码编写注释版无注释版六、串口发送七、总结优缺点分析单阈值识别多阈值识别加最多像素点识别训练神经网络目标检测参考资料一、识别目标使用OpenMV摄像头模块来识别网球并将识别到的网球在图像中的坐标通过串口通信传输出去以便其他设备接收并利用这些坐标信息进行后续处理或操作。二、可供采取的识别方式1. 阈值识别阈值识别是一种基于颜色、亮度或其他图像特征设定固定阈值来进行物体识别的方法。在OpenMV IDE集成开发环境中可以通过分析网球在图像中的颜色特征手动设定或自动获取颜色阈值然后利用这些阈值对图像进行二值化处理从而识别出网球。2. 多阈值识别多阈值识别是对阈值识别方法的扩展它使用多个阈值来处理更复杂的颜色或亮度变化。在网球识别中由于光照条件、网球材质或表面磨损等因素网球的颜色可能会有所变化。因此可以设定多个颜色阈值范围以覆盖网球在不同条件下的颜色变化。然后对每个阈值范围分别进行阈值识别并将结果合并以得到更准确的网球区域。3. 多阈值加最多像素点识别在多阈值识别的基础上增加像素量作为识别条件可以提高识别的准确性。具体做法是在确定了网球可能的颜色范围后不仅检查像素颜色是否满足阈值条件还统计满足条件的像素数量。如果某个连通区域中的像素数量超过预设的阈值即最小网球像素量则认为该区域是一个网球。这种方法有助于减少误识别例如将图像中的噪点或类似颜色的其他物体误认为是网球。4.训练神经网络目标检测重点 训练模型识别是一种基于机器学习或深度学习的方法。OpenMV官方提供了训练神经网络目标检测的教程训练神经网络目标检测可学习这个教程进行改造实现自己想要的物品识别。三、单阈值识别1. 网球阈值获取这里要保证摄像头处有网球2. 代码编写# 色块监测 例子 # # 这个例子展示了如何通过find_blobs()函数来查找图像中的色块 # 这个例子查找的颜色是深绿色 import sensor, image, time import ustruct # 颜色追踪的例子一定要控制环境的光保持光线是稳定的。 green_threshold_num 2 green_threshold {} green_threshold[0] (48, 100, -128, -8, 20, 127) green_threshold[1] (15, 100, -46, -16, 12, 61) # green_threshold (15, 100, -46, -16, 12, 61) #设置绿色的阈值括号里面的数值分别是L A B 的最大值和最小值minL, maxL, minA, # maxA, minB, maxBLAB的值在图像左侧三个坐标图中选取。如果是灰度图则只需 #设置min, max两个数字即可。 import display import time from pyb import UART # UART 3, and baudrate. uart UART(3, 115200) uart.init(115200, bits8, parityNone, stop1) #RX P5 TX P4 # 初始化串口 sensor.reset() # 初始化摄像头 sensor.set_pixformat(sensor.RGB565) # 格式为 RGB565. sensor.set_framesize(sensor.QQVGA2) # 使用 QQVGA 速度快一些 sensor.skip_frames(time 2000) # 跳过2000s使新设置生效,并自动调节白平衡 sensor.set_auto_gain(False) # 关闭自动自动增益。默认开启的在颜色识别中一定要关闭白平衡。 sensor.set_auto_whitebal(False) #关闭白平衡。白平衡是默认开启的在颜色识别中一定要关闭白平衡。 clock time.clock() # 追踪帧率 lcd display.SPIDisplay() while(True): #lcd.write(sensor.snapshot()) # Take a picture and display the image. clock.tick() # Track elapsed milliseconds between snapshots(). img sensor.snapshot() # 从感光芯片获得一张图像 宽128 高160 lcd.write(img) # Take a picture and display the image. length 160 width 120 # 水平方向翻转 sensor.set_hmirror(True) # 垂直翻转图像 sensor.set_vflip(True) blobs img.find_blobs([green_threshold[0]], pixels_threshold 50)#invert 0 #find_blobs(thresholds, invertFalse, roiAuto),thresholds为颜色阈值 #是一个元组需要用括号 括起来。invert1,反转颜色阈值invertFalse默认 false没有反转 #不反转。roi设置颜色识别的视野区域roi是一个元组 roi (x, y, w, h)代表 #从左上顶点(x,y)开始的宽为w高为h的矩形区域roi不设置的话默认为整个图像视野。 #这个函数返回一个列表[0]代表识别到的目标颜色区域左上顶点的x坐标1代表 #左上顶点y坐标2代表目标区域的宽3代表目标区域的高4代表目标 #区域像素点的个数5代表目标区域的中心点x坐标6代表目标区域中心点y坐标 #7代表目标颜色区域的旋转角度是弧度值浮点型列表其他元素是整型 #8代表与此目标区域交叉的目标个数9代表颜色的编号它可以用来分辨这个 #区域是用哪个颜色阈值threshold识别出来的。 #img.draw_circle(10, 10, 10, color(0,255,0)) # img.draw_rectangle(70, 0, 20, 60, color(255,0,0)) # rect # img.draw_rectangle(70, 20, 20, 20, color(255,0,0)) # rect Target 0 if blobs: #如果找到了目标颜色 for b in blobs: #迭代找到的目标颜色区域 # Draw a rect around the blob. if abs(b[3] - b[2]) 10 : img.draw_rectangle(b[0:4], color(0,255,0)) # rect #用矩形标记出目标颜色区域 img.draw_cross(b[5], b[6], color(0,255,0)) # cx, cy #在目标颜色区域的中心画十字形标记 #print(b[5], b[6]) img.draw_cross(64, 80, color(255,0,0)) # cx, cy Target 1 uart.write(Target: str(Target) ;X: str(b[5]) ;Y: str(img.height() - b[6]) ;TargetX: str(img.width()) ;TargetY: str(img.height()) ;) # 如果没有找到目标颜色 if Target 0: uart.write(Target: str(Target) ;X: 0 ;Y: 0 ;TargetX: str(img.width()) ;TargetY: str(img.height()) ;) img.draw_cross(64, 80, color(255,0,0)) # cx, cy img.draw_rectangle(32,120,128-32*2,40, color(0,0,255)) lcd.write(img) # Take a picture and display the image. #print(clock.fps()) # 注意: 你的OpenMV连到电脑后帧率大概为原来的一半 #如果断开电脑帧率会增加3. 识别效果这种识别方式对环境光照要求极高环境变化后容易识别不准确。四、多阈值识别加最多像素点识别1. 阈值获取使用三、1中描述的阈值获取方法在不同的环境和光照条件下分别获取相应的阈值并将这些阈值保存到一个数组中。2. 代码编写# 色块监测 例子 # # 这个例子展示了如何通过find_blobs()函数来查找图像中的色块 # 这个例子查找的颜色是深绿色 import sensor, image, time import ustruct # 颜色追踪的例子一定要控制环境的光保持光线是稳定的。 green_threshold_num 4 green_threshold {} green_threshold[0] (48, 100, -128, -8, 20, 127) green_threshold[1] (15, 100, -46, -16, 12, 61) green_threshold[2] (37, 64, -39, -12, 28, 127) green_threshold[3] (46, 98, -31, -16, 14, 127) # green_threshold (15, 100, -46, -16, 12, 61) #设置绿色的阈值括号里面的数值分别是L A B 的最大值和最小值minL, maxL, minA, # maxA, minB, maxBLAB的值在图像左侧三个坐标图中选取。如果是灰度图则只需 #设置min, max两个数字即可。 import display import time from pyb import UART # UART 3, and baudrate. uart UART(3, 115200) uart.init(115200, bits8, parityNone, stop1) #RX P5 TX P4 # 初始化串口 sensor.reset() # 初始化摄像头 sensor.set_pixformat(sensor.RGB565) # 格式为 RGB565. sensor.set_framesize(sensor.QQVGA2) # 使用 QQVGA 速度快一些 sensor.skip_frames(time 2000) # 跳过2000s使新设置生效,并自动调节白平衡 sensor.set_auto_gain(False) # 关闭自动自动增益。默认开启的在颜色识别中一定要关闭白平衡。 sensor.set_auto_whitebal(False) #关闭白平衡。白平衡是默认开启的在颜色识别中一定要关闭白平衡。 clock time.clock() # 追踪帧率 lcd display.SPIDisplay() # 记录网球不同阈值的数组下标 green_threshold_i 0 Target 0 Xmax 128 Ymax 160 XSD 0 uart.write(start) while(True): #lcd.write(sensor.snapshot()) # Take a picture and display the image. clock.tick() # Track elapsed milliseconds between snapshots(). img sensor.snapshot() # 从感光芯片获得一张图像 宽128 高160 lcd.write(img) # Take a picture and display the image. if green_threshold_i green_threshold_num: # 此处发送数据 uart.write(Target: str(Target) ;X: str(Xmax) ;Y: str(Ymax) ;TargetX: str(img.width()) ;TargetY: str(img.height()) ;) if Target 1: img.draw_circle(Xmax, img.height()-Ymax, 15, color (255, 0, 0)) # 实际发送坐标有效坐标 # 将发送完的数据清零重新计算 green_threshold_i 0 Target 0 Xmax 128 Ymax 160 XSD 0 # 水平方向翻转 sensor.set_hmirror(True) # 垂直翻转图像 sensor.set_vflip(True) blobs img.find_blobs([green_threshold[green_threshold_i]], pixels_threshold 50)#invert 0 green_threshold_i green_threshold_i 1 #find_blobs(thresholds, invertFalse, roiAuto),thresholds为颜色阈值 #是一个元组需要用括号 括起来。invert1,反转颜色阈值invertFalse默认 false没有反转 #不反转。roi设置颜色识别的视野区域roi是一个元组 roi (x, y, w, h)代表 #从左上顶点(x,y)开始的宽为w高为h的矩形区域roi不设置的话默认为整个图像视野。 #这个函数返回一个列表 # [0]代表识别到的目标颜色区域左上顶点的x坐标 # [1]代表左上顶点y坐标 # [2]代表目标区域的宽 # [3]代表目标区域的高 # [4]代表目标区域像素点的个数5代表目标区域的中心点x坐标6代表目标区域中心点y坐标 #7代表目标颜色区域的旋转角度是弧度值浮点型列表其他元素是整型 #8代表与此目标区域交叉的目标个数9代表颜色的编号它可以用来分辨这个 #区域是用哪个颜色阈值threshold识别出来的。 #img.draw_circle(10, 10, 10, color(0,255,0)) # img.draw_rectangle(70, 0, 20, 60, color(255,0,0)) # rect # img.draw_rectangle(70, 20, 20, 20, color(255,0,0)) # rect if blobs: #如果找到了目标颜色 for b in blobs: #迭代找到的目标颜色区域 # Draw a rect around the blob. if abs(b[3] - b[2]) 10 : # img.draw_rectangle(b[0:4], color(0,255,0)) # rect #用矩形标记出目标颜色区域 # img.draw_cross(b[5], b[6], color(0,255,0)) # cx, cy #在目标颜色区域的中心画十字形标记 #print(b[5], b[6]) # img.draw_cross(64, 80, color(255,0,0)) # cx, cy Target 1 X b[5] Y img.height() - b[6] if b[4] XSD: # 记录最大且有效的坐标点 Xmax X Ymax Y XSD b[4] # if green_threshold_i green_threshold_num - 1: # uart.write(Target: str(Target) ;X: str(b[5]) ;Y: str(img.height() - b[6]) ;TargetX: str(img.width()) ;TargetY: str(img.height()) ;) # 如果没有找到目标颜色 # if Target 0: # uart.write(Target: str(Target) ;X: 0 ;Y: 0 ;TargetX: str(img.width()) ;TargetY: str(img.height()) ;) img.draw_cross(64, 80, color(255,0,0)) # cx, cy img.draw_rectangle(40,100,128-40*2,60, color(0,0,255)) # 显示到lcd屏上面 lcd.write(img) # Take a picture and display the image. #print(clock.fps()) # 注意: 你的OpenMV连到电脑后帧率大概为原来的一半 #如果断开电脑帧率会增加3. 效果演示我的最新作品快来一睹为快五、训练神经网络目标检测1. 获取数据集首先打开OpenMVIDE连接openmv然后找到新数据集数据集采集使用方法2. 训练神经网络目标检测OpenMV合作伙伴EdgeImpulse在线训练网站https://www.edgeimpulse.com/加入数据集yes等待后叉掉即可。数据标注标记错误解决方法训练模型选择模型导入模型获取模型解压后如下3. 代码编写将代码修改加入lcd屏后串口发送注释版# Edge Impulse - OpenMV FOMO Object Detection Example # # 这个工作受MIT许可证授权。 # 版权所有 (c) 2013-2024 OpenMV LLC。保留所有权利。 # 许可证详情见https://github.com/openmv/openmv/blob/master/LICENSE # 导入必要的库 import sensor, image, time, os, ml, math, uos, gc from ulab import numpy as np import ustruct import display from pyb import UART # 配置UART串口使用UART 3波特率115200 uart UART(3, 115200) uart.init(115200, bits8, parityNone, stop1) # RX连接P5, TX连接P4 # 初始化串口 clock time.clock() # 用于追踪帧率 # 初始化LCD显示屏 lcd display.SPIDisplay() # 初始化摄像头传感器 sensor.reset() # 重置并初始化传感器 sensor.set_pixformat(sensor.RGB565) # 设置像素格式为RGB565或灰度 sensor.set_framesize(sensor.QQVGA2) # 使用QQVGA分辨率以提高速度 sensor.set_windowing((240, 240)) # 设置240x240的窗口 sensor.skip_frames(time2000) # 让摄像头调整 # 加载模型 net None labels None min_confidence 0.5 # 设置最小置信度阈值 try: # 尝试加载模型如果剩余内存少于64K则将模型文件加载到Flash中 net ml.Model(trained.tflite, load_to_fbuos.stat(trained.tflite)[6] (gc.mem_free() - (64*1024))) except Exception as e: # 如果加载失败抛出异常 raise Exception(加载trained.tflite失败是否已将.tflite和labels.txt文件复制到存储设备 ( str(e) )) try: # 尝试加载标签文件 labels [line.rstrip(\n) for line in open(labels.txt)] except Exception as e: # 如果加载失败抛出异常 raise Exception(加载labels.txt失败是否已将.tflite和labels.txt文件复制到存储设备 ( str(e) )) # 定义颜色列表用于绘制检测到的对象边框 colors [ (255, 0, 0), ( 0, 255, 0), (255, 255, 0), ( 0, 0, 255), (255, 0, 255), ( 0, 255, 255), (255, 255, 255), ] # 设置置信度阈值列表 threshold_list [(math.ceil(min_confidence * 255), 255)] # 定义后处理函数用于处理模型输出 def fomo_post_process(model, inputs, outputs): # 获取模型的输出形状 ob, oh, ow, oc model.output_shape[0] # 计算x和y方向的缩放比例 x_scale inputs[0].roi[2] / ow y_scale inputs[0].roi[3] / oh scale min(x_scale, y_scale) # 计算x和y方向的偏移量 x_offset ((inputs[0].roi[2] - (ow * scale)) / 2) inputs[0].roi[0] y_offset ((inputs[0].roi[3] - (oh * scale)) / 2) inputs[0].roi[1] # 初始化列表用于存储每个类别的检测结果 l [[] for i in range(oc)] # 遍历每个输出通道 for i in range(oc): # 将输出通道的数据转换为图像 img image.Image(outputs[0][0, :, :, i] * 255) # 在图像中查找二值化区域blobs blobs img.find_blobs( threshold_list, x_stride1, y_stride1, area_threshold1, pixels_threshold1 ) # 遍历每个检测到的区域 for b in blobs: rect b.rect() # 获取区域的矩形框 x, y, w, h rect # 计算区域的置信度 score img.get_statistics(thresholdsthreshold_list, roirect).l_mean() / 255.0 # 将区域的坐标和尺寸转换回原始图像坐标系 x int((x * scale) x_offset) y int((y * scale) y_offset) w int(w * scale) h int(h * scale) # 将检测结果添加到列表中 l[i].append((x, y, w, h, score)) return l # 重置帧率追踪器 clock time.clock() # 初始化变量 Target 0 # 目标检测标志 Xmax 0 # 最大X坐标 Ymax 0 # 最大Y坐标 # 向串口发送开始信号 uart.write(start) while(True): clock.tick() # 更新帧率追踪器 # 拍摄一帧图像 img sensor.snapshot() # 水平翻转图像 sensor.set_hmirror(True) # 垂直翻转图像 sensor.set_vflip(True) # 通过串口发送当前检测状态和目标位置信息 uart.write(Target: str(Target) ;X: str(Xmax) ;Y: str(Ymax) ;TargetX: str(img.width()) ;TargetY: str(img.height()) ;) print(Target: str(Target) ;X: str(Xmax) ;Y: str(Ymax) ;TargetX: str(img.width()) ;TargetY: str(img.height()) ;) # 重置变量 Xmax 0 Ymax 0 Target 0 # 使用模型进行预测并处理输出结果 for i, detection_list in enumerate(net.predict([img], callbackfomo_post_process)): if i 0: continue # 跳过背景类别 if len(detection_list) 0: continue # 如果该类别没有检测结果则跳过 # 打印类别标签 print(********** %s ********** % labels[i]) # 遍历检测结果 for x, y, w, h, score in detection_list: Target 1 # 设置目标检测标志 # 计算中心坐标 center_x math.floor(x (w / 2)) center_y math.floor(y (h / 2)) # 更新最大X坐标和Y坐标注意Y坐标需要转换为相对于底部的距离 Xmax center_x Ymax img.height() - center_y # 打印检测结果 print(fx {center_x}\ty {center_y}\tscore {score}) # 在图像上绘制检测到的对象中心 img.draw_circle((center_x, center_y, 12), colorcolors[i]) # 打印当前帧率 print(clock.fps(), fps, end\n\n) # 在图像上绘制一个十字标记和一个矩形框仅用于演示 img.draw_cross(64, 80, color(255,0,0)) # cx, cy img.draw_rectangle(40,100,128-40*2,60, color(0,0,255)) # 将图像显示到LCD上 lcd.write(img)无注释版# Edge Impulse - OpenMV FOMO Object Detection Example # # This work is licensed under the MIT license. # Copyright (c) 2013-2024 OpenMV LLC. All rights reserved. # https://github.com/openmv/openmv/blob/master/LICENSE import sensor, image, time, os, ml, math, uos, gc from ulab import numpy as np import ustruct import display from pyb import UART # UART 3, and baudrate. uart UART(3, 115200) uart.init(115200, bits8, parityNone, stop1) #RX P5 TX P4 # 初始化串口 clock time.clock() # 追踪帧率 lcd display.SPIDisplay() sensor.reset() # Reset and initialize the sensor. sensor.set_pixformat(sensor.RGB565) # Set pixel format to RGB565 (or GRAYSCALE) sensor.set_framesize(sensor.QQVGA2) # 使用 QQVGA 速度快一些 sensor.set_windowing((240, 240)) # Set 240x240 window. sensor.skip_frames(time2000) # Let the camera adjust. net None labels None min_confidence 0.5 try: # load the model, alloc the model file on the heap if we have at least 64K free after loading net ml.Model(trained.tflite, load_to_fbuos.stat(trained.tflite)[6] (gc.mem_free() - (64*1024))) except Exception as e: raise Exception(Failed to load trained.tflite, did you copy the .tflite and labels.txt file onto the mass-storage device? ( str(e) )) try: labels [line.rstrip(\n) for line in open(labels.txt)] except Exception as e: raise Exception(Failed to load labels.txt, did you copy the .tflite and labels.txt file onto the mass-storage device? ( str(e) )) colors [ # Add more colors if you are detecting more than 7 types of classes at once. (255, 0, 0), ( 0, 255, 0), (255, 255, 0), ( 0, 0, 255), (255, 0, 255), ( 0, 255, 255), (255, 255, 255), ] threshold_list [(math.ceil(min_confidence * 255), 255)] def fomo_post_process(model, inputs, outputs): ob, oh, ow, oc model.output_shape[0] x_scale inputs[0].roi[2] / ow y_scale inputs[0].roi[3] / oh scale min(x_scale, y_scale) x_offset ((inputs[0].roi[2] - (ow * scale)) / 2) inputs[0].roi[0] y_offset ((inputs[0].roi[3] - (ow * scale)) / 2) inputs[0].roi[1] l [[] for i in range(oc)] for i in range(oc): img image.Image(outputs[0][0, :, :, i] * 255) blobs img.find_blobs( threshold_list, x_stride1, y_stride1, area_threshold1, pixels_threshold1 ) for b in blobs: rect b.rect() x, y, w, h rect score ( img.get_statistics(thresholdsthreshold_list, roirect).l_mean() / 255.0 ) x int((x * scale) x_offset) y int((y * scale) y_offset) w int(w * scale) h int(h * scale) l[i].append((x, y, w, h, score)) return l clock time.clock() Target 0 Xmax 0 Ymax 0 uart.write(start) while(True): clock.tick() img sensor.snapshot() # 水平方向翻转 sensor.set_hmirror(True) # 垂直翻转图像 sensor.set_vflip(True) uart.write(Target: str(Target) ;X: str(Xmax) ;Y: str(Ymax) ;TargetX: str(img.width()) ;TargetY: str(img.height()) ;) print(Target: str(Target) ;X: str(Xmax) ;Y: str(Ymax) ;TargetX: str(img.width()) ;TargetY: str(img.height()) ;) Xmax 0 Ymax 0 Target 0 for i, detection_list in enumerate(net.predict([img], callbackfomo_post_process)): if i 0: continue # background class if len(detection_list) 0: continue # no detections for this class? print(********** %s ********** % labels[i]) for x, y, w, h, score in detection_list: Target 1 center_x math.floor(x (w / 2)) center_y math.floor(y (h / 2)) Xmax center_x Ymax img.height() - center_y print(fx {center_x}\ty {center_y}\tscore {score}) img.draw_circle((center_x, center_y, 12), colorcolors[i]) print(clock.fps(), fps, end\n\n) img.draw_cross(64, 80, color(255,0,0)) # cx, cy img.draw_rectangle(40,100,128-40*2,60, color(0,0,255)) lcd.write(img) # Take a picture and display the image.六、串口发送Target:1;X:X_COORD;Y:Y_COORD;TargetX:IMG_WIDTH;TargetY:IMG_HEIGHT;七、总结优缺点分析单阈值识别优点实现简单代码逻辑相对简单易于理解和上手对于初学者来说是一种快速实现目标识别的方法。计算效率高由于只使用一个固定的颜色阈值进行二值化处理计算量较小能保证较高的帧率实时性较好。缺点对环境要求高光照、网球材质、表面磨损等因素导致网球颜色变化时识别准确性会大幅下降适应性较差。误识别率高容易将图像中的噪点或类似颜色的其他物体误认为是网球鲁棒性不足。多阈值识别加最多像素点识别优点适应性增强通过设置多个颜色阈值范围能覆盖网球在不同光照和环境条件下的颜色变化提高了识别的适应性。减少误识别增加像素量作为识别条件有助于排除图像中的噪点和小面积的类似颜色区域降低误识别率。缺点阈值获取复杂需要在不同环境和光照条件下分别获取阈值并保存到数组中增加了前期准备工作的复杂度。计算量增加对每个阈值范围分别进行阈值识别并合并结果计算量相对单阈值识别有所增加可能会影响帧率。训练神经网络目标检测优点识别准确性高通过大量数据训练得到的模型能够学习到网球的特征对不同光照、角度和环境下的网球都有较好的识别效果鲁棒性强。适应性强可以适应网球颜色、形状等的变化对于复杂场景的处理能力优于传统的阈值识别方法。缺点训练成本高需要获取大量的数据集并进行标注训练过程也需要一定的计算资源和时间。模型加载和运行要求高需要将训练好的模型文件和标签文件复制到存储设备且对设备的内存和计算能力有一定要求会影响设备的运行效率。参考资料1. 实际道路环境交通标志识别 - 自行训练神经网络目标点检测2. OpenMV 图像串口传输示例接单片机设计DPJSJ0X78