OPEN-EC快速食用指南(四)简单使用类似OpenCV的API实现黑色物体检测

本实验就以检测黑色圆珠笔为例,将检测到的结果通过串口2发送出来。
image

大致思路

  1. 把摄像头获取的图像转化成灰度。
  2. 灰度通过设定阈值二值化得到单色图像。
  3. 对单色图像腐蚀膨胀去除噪声。
  4. 在单色图像中寻找连通域。
  5. 找出最大的连通域。
  6. 把最终的结果在彩色原图中框出来。
  7. 通过串口2发送检测结果的坐标。

代码

setup

void setup() {  
    app_camera_main();
    app_led_init();
    app_led_set(0);
    uart2_init(115200);
    Lcd_Init();
    Lcd_Clear(WHITE);
}

初始化摄像头、LCD、串口2

下面是loop

图像读取以及转灰度

    Image img=image_read_from_camera();
    Image gray=image_rgb565_to_gray(img);

二值化

    Image bin=image_binary(gray,100,0);
    //阈值设置为100,最后的0表示不反色,即灰度值大于阈值的是255,小于的是0

先看一下二值化的结果

    display_show_img(bin);

调调阈值,找出最合适的阈值。
最后记得释放内存:

    image_free(img);
    image_free(gray);
    image_free(bin);

二值化的结果:(阈值为90)
image

还是存在一些噪声。

腐蚀膨胀去噪声

    Image erode_res=image_erode(bin,3);
    Image dilate_res=image_dilate(erode_res,3);

然后显示去噪声之后的结果:

    display_show_img(dilate_res);
    //要把之前显示二值化图像的那行注释掉

结果:
image

找连通域

接下来只需要找连通域就可以了。

    //先建一个数组用于存放矩形
    Rect rects[100];
    int rect_num=image_find_contours(dilate_res,rects);
    //返回值是连通域个数,连通域的最小外接矩形存放在传进去的地址中。

画框显示

为了去除稍微大一点的噪声,选取了面积最大的连通域作为最终结果。

    if(rect_num>0){
        int max_sqr=rects[0].w*rects[0].h;
        int max_index=0;
        for(int i=1;i<rect_num;i++){
            if(rects[i].w*rects[i].h > max_sqr){
                max_sqr=rects[i].w*rects[i].h;
                max_index=i;
            }
            
        }
        image_draw_rect(img,rects[max_index].x,rects[max_index].y,rects[max_index].w,rects[max_index].h,COLOR16(255,0,0)); 
    }

最终的结果:
image

显示中心坐标

矩形框的几何中心作为他的坐标,
计算方式是(x+w/2,y+h/2)

        char s[30];
        sprintf(s,"(%3d,%3d)",rects[max_index].x+rects[max_index].w/2,rects[max_index].y+rects[max_index].h/2);
        display_show_string_bottom(s,0,COLOR16(0,0,255));

显示结果:
image

中心坐标通过串口2发送

同样现在setup初始化串口二,详情见上一篇。

得到中心坐标之后,因为图片尺寸是160x120,宽高都在一个字节(255)以内,所以套上一个帧头,直接把两个字节(x和y)发出去。帧头用0x55 0x56
例如,上述得到的坐标是91,88,那么发送的数据是
十进制:[85,86,91,88]
十六进制:[0x55,0x56,0x5b,0x58]

        char send_data[4]={0x55,0x56,rects[max_index].x+rects[max_index].w/2,rects[max_index].y+rects[max_index].h/2};
        uart_write_bytes(UART_NUM_2, send_data, 4);

串口2连上电脑,打开串口调试助手,记得选中16进制接收,然后把宽度拉的小一点,一行四个字节,就看的清发过来的数据了。下面是结果:

image

完整的代码


#include "esp_camera.h"
#include "app_led.h"
#include "app_camera.h"
#include "display.h"
#include "app_uart.h"
#include "image.h"





void setup() {  
    app_camera_main();
    app_led_init();
    app_led_set(0);
    uart2_init(115200);
    Lcd_Init();
    Lcd_Clear(WHITE);
}

void loop() {
    Image img=image_read_from_camera();
    Image gray=image_rgb565_to_gray(img);
    Image bin=image_binary(gray,90,0);
    Image erode_res=image_erode(bin,3);
    Image dilate_res=image_dilate(erode_res,3);
    Rect rects[100];
    int rect_num=image_find_contours(dilate_res,rects);
    if(rect_num>0){
        int max_sqr=rects[0].w*rects[0].h;
        int max_index=0;
        for(int i=1;i<rect_num;i++){
            if(rects[i].w*rects[i].h > max_sqr){
                max_sqr=rects[i].w*rects[i].h;
                max_index=i;
            }
            
        }
        image_draw_rect(img,rects[max_index].x,rects[max_index].y,rects[max_index].w,rects[max_index].h,COLOR16(255,0,0)); 
        char s[30];
        sprintf(s,"(%3d,%3d)",rects[max_index].x+rects[max_index].w/2,rects[max_index].y+rects[max_index].h/2);
        display_show_string_bottom(s,0,COLOR16(0,0,255));
        char send_data[4]={0x55,0x56,rects[max_index].x+rects[max_index].w/2,rects[max_index].y+rects[max_index].h/2};
        uart_write_bytes(UART_NUM_2, send_data, 4);
    }
    
    display_show_img(img);
    image_free(img);
    image_free(gray);
    image_free(bin);
    image_free(erode_res);
    image_free(dilate_res);
    app_led_toggle();
}

上一篇:串口2的使用

posted @ 2022-04-30 21:39  explorerSZY  阅读(114)  评论(0)    收藏  举报