501|4

12

帖子

0

TA的资源

一粒金砂(中级)

嘉楠K230AI开发板测评4--图像显示、画图、边缘/线段/圆形/矩形检测、线性回归 [复制链接]

本帖最后由 dfjs 于 2024-11-2 18:13 编辑
嘉楠科K230AI开发板测评4--机器视觉篇
  1. 摄像头
           摄像头是整个机器视觉应用的基础,K230的引出了3路摄像头,接口如下图:
    wd_173351g226qibr64q2k4ma.png
        CanMV K230使用camera模块实现摄像头采集图像功能,K230硬件支持3路sensor输入(CSI接口),每个sensor设备均可独立完成图像数据采集捕获处理,并可以同时输出3路图像数据,sensor 0,sensor 1,sensor 2表示三个图像传感器;Camera Device 0,Camera Device 1,Camera Device 2表示三个sensor设备;output channel 0,output channel 1,output channel 2表示sensor设备的三个输出通道。三个图像传感器可以通过软件配置映射到不同的sensor 设备,示意图如下图。
    wd_173351baujjol451l1slgl.png
        摄像头(sensor)位于media模块下,通过from media.sensor import * #导入sensor模块,使用摄像头相关接口,sensor = Sensor(id,[width, height, fps])构建摄像头对象,id为CSI输入号,默认值为CSI2即开发板上的摄像头,width、height和fps为可选参数,分别表示sensor采集图像宽度,高度和帧率。
        sensor.reset()复位和初始化摄像头。sensor.set_framesize(framesize = FRAME_SIZE_INVAILD, [width, height],chn = CAM_CHN_ID_0, alignment=0, **kwargs)设置每个通道的图像输出尺寸,framesize: 通道图像输出尺寸。chn: 通道编号,每个摄像头设备有3个通道。
        sensor.set_pixformat(pixformat, chn = CAM_CHN_ID_0)设置图像像素格式。pixformat: 格式。chn: 通道编号,每个摄像头设备有3个通道。
        sensor.set_hmirror(enable)设置摄像头画面水平镜像。sensor.set_vflip(enable)设置摄像头画面垂直翻转。
        sensor.run()启动摄像头。
        sensor.snapshot()使用相机拍摄一张照片,并返回 image 对象。
        然后使用计算FPS(每秒帧数)的clock模块。clock=time.clock()构建一个时钟对象。clock.tick()开始追踪运行时间。clock.fps()停止追踪运行时间,并返回当前FPS(每秒帧数)。在调用该函数前始终首先调用 clock.tick(),完整代码编写流程如下。
    wd_173351vxw2a14dk1d1vpa7.png
        参考代码如下,摄像头实时拍摄并显示在IDE缓冲区,由于CanMV K230 MicroPython底层基于Linux + RTOS实现,因此可以看到代码中出现一些辅助中断等代码,这些代码相对固定。
    '''
    实验名称:摄像头使用
    实验平台:01Studio CanMV K230
    说明:实现摄像头图像采集显示
    '''
    import time, os, sys
    from media.sensor import * #导入sensor模块,使用摄像头相关接口
    from media.display import * #导入display模块,使用display相关接口
    from media.media import * #导入media模块,使用meida相关接口
    try:
    sensor = Sensor() #构建摄像头对象
    sensor.reset() #复位和初始化摄像头
    sensor.set_framesize(Sensor.FHD) #设置帧大小FHD(1920x1080),默认通道0
    sensor.set_pixformat(Sensor.RGB565) #设置输出图像格式,默认通道0
    #使用IDE缓冲区输出图像,显示尺寸和sensor配置一致。
    Display.init(Display.VIRT, sensor.width(), sensor.height())
    MediaManager.init() #初始化media资源管理器
    sensor.run() #启动sensor
    clock = time.clock()
    while True:
    os.exitpoint() #检测IDE中断
    ################
    ## 这里编写代码 ##
    ################
    clock.tick()
    img = sensor.snapshot() #拍摄一张图
    Display.show_image(img) #显示图片
    print(clock.fps()) #打印FPS
    ###################
    # IDE中断释放资源代码
    ###################
    except KeyboardInterrupt as e:
    print("user stop: ", e)
    except BaseException as e:
    print(f"Exception {e}")
    finally:
    # sensor stop run
    if isinstance(sensor, Sensor):
    sensor.stop()
    # deinit display
    Display.deinit()
    os.exitpoint(os.EXITPOINT_ENABLE_SLEEP)
    time.sleep_ms(100)
    # release media buffer
    MediaManager.deinit()

        实验结果,点击运行代码,右边显示摄像头实时拍摄情况,下方则显示RGB颜色直方图。 wd_173352dkl1dg99zyag73dk.png

  2. 图像的3种显示方式
        在摄像头拍摄图像后我们需要观察图像,这就涉及如何显示的问题,目前CanMV K230支持3种显示方式。分别是:IDE缓冲区显示、外接HDMI显示器或MIPI显示屏,3种图像显示方式,各有特点:
        IDE缓冲区显示:性价比最高,图像质量有一定下降,但能满足大部分场合调试使用。最大支持1920x1080分辨率。
        HDMI:外接HDMI显示屏,清晰度最高。最大支持1920x1080分辨率。
        MIPI显示屏:外接01Studio 3.5寸MiPi显示屏,可以一体化组装,适合离线部署调试使用。最大支持800x480分辨率。
        首先导入Display模块,Display.init(type = None, width = None, height = None, osd_num = 1, to_ide = False, fps = None)初始化Display模块,type: 显示设备类型,VIRT : IDE缓冲区显示;LT9611 : HDMI显示;ST7701 : mipi显示屏。width: 可选参数,显示图像宽度;height: 可选参数,显示图像高度;to_ide: 同时在IDE显示,仅用于设置为HDMI或MIPI屏显示时使用。
        Display.show_image(img, x = 0, y = 0, layer = None, alpha = 255, flag = 0),img为显示图像对象,x: 起始横坐标;y: 起始纵坐标。
        Display.deinit(),注销Display模块,必须在MediaManager.deinit()之前, 在sensor.stop()之后调用。代码编写流程图如下图:
    wd_173352vwdsmy4zumsrr0fu.png
        参考代码如下,只展示与摄像头节不同的地方(核心代码):
    #################################
    ## 图像3种不同显示方式(修改注释实现)
    #################################
    Display.init(Display.VIRT, sensor.width(), sensor.height()) #通过IDE缓冲区显示图像
    #Display.init(Display.LT9611, to_ide=True) #通过HDMI显示图像
    #Display.init(Display.ST7701, to_ide=True) #通过01Studio 3.5寸mipi显示屏显示图像
       实验结果,分别为IDE缓冲区,HDMI显示器和MIPI屏幕
    wd_173352uba6xg6egkvgirlb.png wd_173352zycmlmc6en65vt3c.png
    wd_173352z7kp2kuv81383442.png
  3. 画图
        通过摄像头采集到照片后,我们会进行一些处理,而这时候往往需要一些图形来指示,比如在图CanMV已经将图片处理(包含画图)封装成各类模块,我们 只需要熟悉其构造函数和使用方法即可片某个位置标记箭头、人脸识别后用矩形框提示等。
        img=sensor.snapshot()通过摄像头拍摄方式返回image对象。
        image.draw_line()对图像进行画线段,参数为起始坐标,终点坐标,颜色与线条粗细。
        image.draw_rectangle()画矩形,参数为起始坐标,宽度,高度,颜色,边框粗细,是否填充。
        image.draw_circle()画圆,参数为圆心,宽度,高度,颜色,线条粗细,是否填充。
        image.draw_arrow()画箭头,参数为起始坐标,终点坐标,颜色,箭头位置大小,线条粗细。
        image.draw_cross()画十字交叉,参数为交叉中点坐标,颜色,大小,线条粗细。
        image.draw_string()写字符,参数为起始坐标,字符内容,颜色,字体大小,强制间隔。
       image.draw_string_advanced()写字符,支持中文,参数为起始坐标,字体大小,字符内容,颜色,字体类型。
       代码编写思路如下:
    wd_173352qc7jvl3vcifkk35c.png
        核心代码如下:
    img = sensor.snapshot()
    # 画线段:从 x0, y0 到 x1, y1 坐标的线段,颜色红色,线宽度 2。
    img.draw_line(20, 20, 100, 20, color = (255, 0, 0), thickness = 2)
    #画矩形:绿色不填充。
    img.draw_rectangle(150, 20, 100, 30, color = (0, 255, 0), thickness = 2, fill = False)
    #画圆:蓝色不填充。
    img.draw_circle(60, 120, 30, color = (0, 0, 255), thickness = 2, fill = False)
    #画箭头:白色。
    img.draw_arrow(150, 120, 250, 120, color = (255, 255, 255), size = 20, thickness = 2)
    #画十字交叉。
    img.draw_cross(60, 200, color = (255, 255, 255), size = 20, thickness = 2)
    #写字符。
    #img.draw_string(150, 200, "Hello 01Studio!", color = (255, 255, 255), scale = 4, mono_space = False)
    #写字符,支持中文。
    img.draw_string_advanced(150, 180, 30, "Hello 01Studio", color = (255, 255, 255))
    img.draw_string_advanced(40, 300, 30, "人生苦短, 我用Python", color = (255, 255, 255))
    Display.show_image(img)
        实验结果如下,在合适位置依次画出线段、矩形、圆形、箭头、十字交叉和字符:
    图片.png  
  4. 图像检测
    1. 边缘检测
          生活中每个物体都有一个边缘, 简单来说就是轮廓,使用MicroPython 结合 CanMV K230 自带的库来做图像轮廓检测。
          CanMV集成了RGB565颜色块识别find_edges函数,位于 image 模块下,因此直接将拍摄到的图片进行处理即可。
          直接通过image.find_edges(edge_type[, threshold])即可对图像进行边缘检测,参数edge_type为处理方式,image.EDGE_SIMPLE : 简单的阈值高通滤波算法(其基本原理是设置一个频率阈值,将高于该阈值的频率成分保留或增强,而将低于该阈值的频率成分抑制或去除,从而实现图像的锐化或边缘检测。);   image.EDGE_CANNY: Canny 边缘检测算法(核心思想是找寻图像中灰度强度变化最强的位置,这些位置即边缘);threshold: 包含高、低阈值的二元组,默认是(100,200),仅支持灰度图像。
          代码编写思路如下:
      wd_173352vtznrs2v3wrzg6yv.png
          核心代码如下,对图片对象进行边缘:
      img = sensor.snapshot() #拍摄一张图片
      #使用 Canny 边缘检测器
      img.find_edges(image.EDGE_CANNY, threshold=(50, 80))
      # 也可以使用简单快速边缘检测,效果一般,配置如下
      #img.find_edges(image.EDGE_SIMPLE, threshold=(100, 255))
      #Display.show_image(img) #显示图片
      #显示图片,仅用于LCD居中方式显示
      Display.show_image(img, x=round((800-sensor.width())/2),y=round((480-sensor.height())/2))
      print(clock.fps()) #打印FPS
          实验结果如下,对任务画像进行了边缘化:
      wd_173352ehuyxpvzc00d95ef.png
    2. 线段检测
          CanMV集成了线段识别 find_line_segments 函数,位于 image 模块下,因此我们直接将拍摄到的图片进行处理即可。
          image.find_line_segments([roi[,merge_distance=0[,max_theta_difference=15]]])线段识别函数,返回image.line线段对象列表。参数roi: 识别区域(x,y,w,h),未指定则默认整张图片。参数merge_distance: 两条线段间可以相互分开而不被合并的最大像素。参数max_theta_difference: 将少于这个角度值的线段合并。大部分参数使用默认即可,不支持压缩图像和bayer图像。
          代码编写流程如下:
      wd_173352xbs8bi9jl56vl2vv.png
          核心代码如下:
      img = sensor.snapshot() #拍摄一张图片
      if enable_lens_corr: img.lens_corr(1.8) # for 2.8mm lens...
      # `merge_distance` 控制相近的线段是否合并. 数值 0 (默认值)表示不合并。数值
      #为1时候表示相近1像素的线段被合并。因此你可以通过改变这个参数来控制检测到线
      #段的数量。
      # `max_theta_diff` 控制相差一定角度的线段合并,默认是15度,表示15度内的线
      # 段都会合并
      for l in img.find_line_segments(merge_distance = 0, max_theta_diff = 5):
      img.draw_line(l.line(), color = (255, 0, 0), thickness=2)
      print(l)
      #Display.show_image(img) #显示图片
      #显示图片,仅用于LCD居中方式显示
      Display.show_image(img, x=round((800-sensor.width())/2),y=round((480-sensor.height())/2))
      print(clock.fps()) #打印FPS
          实验结果如下,IDE缓冲区标出图像中的横线。
      wd_173352vdjuve26z85dzd22.png
    3. 圆形检测
          CanMV集成了圆形识别find_circles函数,位于image模块下,因此我们直接将拍摄到的图片进行处理即可。
          image.find_circles([roi[, x_stride=2[, y_stride=1[, threshold=2000[, x_margin=10[, y_margin=10[, r_margin=10[, r_min=2[, r_max[, r_step=2]]]]]]]]]])找圆函数。返回一个image.circle圆形对象,该圆形对象有4个值: x, y(圆心), r (半径)和magnitude(量级);量级越大说明识别到的圆可信度越高。
          roi: 识别区域(x,y,w,h),未指定则默认整张图片;
         threshold: 阈值。返回大于或等于threshold的圆,调整识别可信度;
         x_stride y_stride : 霍夫变换时跳过x,y像素的量;
         x_margin y_margin r_margin : 控制所检测圆的合并;
         r_min r_max: 控制识别圆形的半径范围‘
         r_step:控制识别步骤。
         代码编写思路如下:
      wd_173352nvgri98828zril2v.png
         核心代码如下:
      img = sensor.snapshot() #拍摄一张图片
      # 圆形类有 4 个参数值: 圆心(x, y), r (半径)和 magnitude(量级);
      # 量级越大说明识别到的圆可信度越高。
      # `threshold` 参数控制找到圆的数量,数值的提升会降低识别圆形的总数。
      # `x_margin`, `y_margin`, and `r_margin`控制检测到接近圆的合并调节.
      # r_min, r_max, and r_step 用于指定测试圆的半径范围。
      for c in img.find_circles(threshold = 2000, x_margin = 10, y_margin= 10,
      r_margin = 10,r_min = 2, r_max = 100, r_step = 2):
      #画红色圆做指示
      img.draw_circle(c.x(), c.y(), c.r(), color = (255, 0, 0),thickness=2)
      print(c) #打印圆形的信息
      #Display.show_image(img) #显示图片
      #显示图片,仅用于LCD居中方式显示
      Display.show_image(img, x=round((800-sensor.width())/2),y=round((480-sensor.height())/2))
          实验结果如下,图片检测识别结果如图。
      wd_173352j8roow45ool8vlol.png
    4. 矩形检测
          CanMV集成了矩形识别find_rects函数,位于image模块下,因此我们直接将拍摄到的图片进行处理即可。
          image.find_rects([roi=Auto, threshold=10000])矩形识别函数。返回一个image.rect矩形对象列表。
          roi: 识别区域(x,y,w,h),未指定则默认整张图片;
          threshold: 阈值。返回大于或等于threshold的矩形,调整识别可信度。
          代码编写思路如下:
      wd_173352d6m613xg6egsmlgn.png
           核心代码如下:
      img = sensor.snapshot() #拍摄一张图片
      # `threshold` 需要设置一个比价大的值来过滤掉噪声。
      #这样在图像中检测到边缘亮度较低的矩形。矩形
      #边缘量级越大,对比越强…
      for r in img.find_rects(threshold = 10000):
      img.draw_rectangle(r.rect(), color = (255, 0, 0),thickness=2) #画矩形显示
      for p in r.corners(): img.draw_circle(p[0], p[1], 5, color = (0, 255, 0))#四角画小圆形
      print(r)
      #Display.show_image(img) #显示图片
      #显示图片,仅用于LCD居中方式显示
      Display.show_image(img, x=round((800-sensor.width())/2),y=round((480-sensor.height())/2))
      print(clock.fps()) #打印FPS
          实验结果,左边矩形识别结果如图。
      wd_173352i2umoivgivdoorm2.png
    5. 快速线性回归(巡线)
                    快速线性回归的用途非常广泛,如比赛经常用到的小车、机器人巡线,可以通过线性回归的方式判断虚线和实线的轨迹,从而做出判断和响应。
                    CanMV集成了快速线性回归get_regression函数,位于image模块下。
                    mage.get_regression(thresholds[, invert=False[, roi[, x_stride=2[, y_stride=1[, area_threshold=10[, pixels_threshold=10[, robust=False]]]]]]])对图像所有阈值像素进行线性回
              归计算。这一计算通过最小二乘法进行,通常速度较快,但不能处理任何异常值。若 robust 为True,则将使用泰尔指数。泰尔指数计算图像中所有阈值像素间的所有斜率的中值。
             若在阈值转换后设定太多像素,即使在80x60的图像上,这一N^2操作也可能将您的FPS降到5以下。 但是,只要阈值转换后的进行设置的像素数量较少,即使在超过30%的阈值像素
             为异常值的情况下,线性回归也依然有效。
                    threshold: 必须是元组列表。 (lo, hi) 定义你想追踪的颜色范围。对于灰度图像,每个元组需要包含两个值:最小灰度值和最大灰度值。
                    代码编写流程如下:
                   
wd_173353ztr1withrvyt12q9.png
            核心代码如下:
#image.binary([THRESHOLD])将灰度值在THRESHOLD范围变成了白色
img = sensor.snapshot().binary([THRESHOLD]) if BINARY_VISIBLE else sensor.snapshot()
# 返回一个类似 find_lines() 和find_line_segments()的对象.
# 有以下函数使用方法: x1(), y1(), x2(), y2(), length(),
# theta() (rotation in degrees), rho(), and magnitude().
#
# magnitude() 代表线性回归的指令,其值为(0, INF]。
# 0表示一个圆,INF数值越大,表示线性拟合的效果越好。
line = img.get_regression([(255,255) if BINARY_VISIBLE else THRESHOLD])
if (line):
img.draw_line(line.line(), color = 127,thickness=4)
print(line) #打印结果
#显示图片,仅用于LCD居中方式显示
Display.show_image(img, x=round((800-sensor.width())/2),y=round((480-sensor.height())/2))
print("FPS %f, mag = %s" % (clock.fps(), str(line.magnitude()) if (line) else "N/A"))

 

        实验结果,为了标明线性的变化趋势,取多组实验结果如下,串口结果包含拟合线段的两个点坐标,长度,以及非常重要的theta角度信息:
wd_173353b40jzij2l41jrkk9.png
wd_173353muoq9sm8pdjhe8h8.png
wd_173353hlzl300vdbuzqyqo.png
wd_173353fsqqslf8lhiibiz8.png

测评报告4.docx

4.57 MB, 下载次数: 0

最新回复

这个学习怎么喂图啊?拍摄好的?还是喂视频?   详情 回复 发表于 2024-11-2 23:09

回复
举报

9731

帖子

24

TA的资源

版主

这个开发板这么强大吗?如果不用micropython能实现相同的功能吗?或者micropython在商用产品上使用适不适合?

点评

micropython都是封装好的函数,比较容易上手,也可以用linux开发,不过比较麻烦,没有micropython的生态好,个人感觉拿来商用的话成本还是太高了    详情 回复 发表于 2024-11-3 21:44

回复

6168

帖子

7

TA的资源

版主

这个学习怎么喂图啊?拍摄好的?还是喂视频?

点评

直接电脑截图,哈哈  详情 回复 发表于 2024-11-3 21:45
个人签名

在爱好的道路上不断前进,在生活的迷雾中播撒光引


回复

12

帖子

0

TA的资源

一粒金砂(中级)

littleshrimp 发表于 2024-11-2 21:58 这个开发板这么强大吗?如果不用micropython能实现相同的功能吗?或者micropython在商用产品上使用适不 ...

micropython都是封装好的函数,比较容易上手,也可以用linux开发,不过比较麻烦,没有micropython的生态好,个人感觉拿来商用的话成本还是太高了

 


回复

12

帖子

0

TA的资源

一粒金砂(中级)

秦天qintian0303 发表于 2024-11-2 23:09 这个学习怎么喂图啊?拍摄好的?还是喂视频?

直接电脑截图,哈哈


回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表