RoboMaster视觉教程OpenCV(四)开闭运算

RoboMaster视觉教程OpenCV(四)开闭运算

一 Open CV开运算

之后的代码都在Qt Creator中运行了。毕竟在IDE中运行,比命令行方便许多。当然,删掉有关Qt的几行代码,一样可以在命令行中运行。

腐蚀与膨胀作为形态学的基本操作,经过组合后可以很容易的实现更高一级的形态学运算。

形态学(morphology)一词通常表示生物学的一个分支,该分支主要研究动植物的形态和结构。而我们图像处理中的形态学,往往指的是数学形态学(Mathematical morphology)。
简单来讲,形态学操作就是基于形状的一系列图像处理操作。最基本的形态学操作有两种,分别是:膨胀(dilate)与腐蚀(erode)。经过互相组合可以形成高级的形态学操作台运算和闭运算。

详细内容我们可以去看相关opencv参考书。如毛星云的书187~194页

膨胀:卷积求局部最大值。白色部分(高亮部分)更多。

腐蚀:卷积求局部最小值,高亮部分被腐蚀,白色部分(高亮部分)更少。

开运算(Opening Operation):先腐蚀后膨胀的过程。用来消除小物体,在纤细点处分离黑色物体,并且在平滑较大物体的边界的同时不明显改变其面积。

闭运算(Closing Operation):先膨胀后腐蚀的过程。算能够排除小型黑洞(黑色区域)。

开运算代码如下:

今天发布的文章有Qt的安装教程,以后的代码都在Qt环境里运行了。新建一个qt控制台程序,将main.cpp中的内容替换为以下部分:

#include <QCoreApplication>
#include "opencv2/opencv.hpp"
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
         using namespace cv;

        namedWindow("origin");//用来显示图像用的,
        namedWindow("erode");
        namedWindow("dilate");
        namedWindow("Opening_Operation");

        Mat image = imread("/home/dji/Desktop/OpenCV_Logo.png");
        imshow("origin",image);

        Mat element = getStructuringElement(MORPH_RECT,Size(15,15));//生成一个核
        Mat dilate_out;
        dilate(image,dilate_out,element);
        imshow("dilate",dilate_out);

        Mat erode_out;
        erode(image,erode_out,element);
        imshow("erode",erode_out);

        Mat kai_out;
        dilate(erode_out,kai_out,element);
        imshow("Opening_Operation",kai_out);

        Mat bi_out;
         erode(dilate_out,bi_out,element);
        imshow("bi",bi_out);
        waitKey(0);
    return a.exec();
}

1.1 代码解析

腐蚀膨胀两步走:

  1. 生成一个核,核的大小以及形状自己去选择
  2. 使用腐蚀或者膨胀函数

只需要记住三个函数即可。以代码中的16到19行为例。

Mat element = getStructuringElement(MORPH_RECT,Size(15,15));//生成一个核

getStructuringElement()会返回一个卷积用的核,第一个参数用来设置这个核的形状,第二个参数用来设置这个核的大小。

dilate(image,dilate_out,element);

这个函数是将第一个参数中的图像作为原图,使用第三个参数中的核对原图进行膨胀处理。处理之后的图像放到第二个参数中,第二个图像显示出来,就是经过膨胀的图像。

拓展资料:qt中编写图形界面调节开闭运算参数 https://blog.csdn.net/qingyang8513/article/details/80675872

namewindow()

namedWindow函数用于创建一个窗口。若是简单地进行图片显示,可以略去namedWindow函数的调用,即先调用imread读入图片,然后用imshow直接指定出窗口名进行显示即可。但需要在显示窗口之前就用到窗口名时,就需要namedWindow函数先创建出窗口,显式地规定窗口名称了。

这里我们先接触一下它。

二 Open CV闭运算

  1. qt中编写闭运算代码,打开摄像头,从摄像头读取每一帧进行闭运算并显示,利用CV_CAP_PROP_FPS显示硬件帧率。

  2. 在这里我们对图像进行了处理,一旦图像处理速度跟不上摄像头采集图像的速度,帧率会下降,大家可以输出一下硬件帧率和软件图像处理的帧率。

#include <QCoreApplication>
#include "opencv2/opencv.hpp"
#include <time.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    using namespace std;
    using namespace cv;

    namedWindow("origin");//用来显示图像用的,
    VideoCapture cap(0);
    int fps_h,fps_s;//硬件帧率和软件帧率
    //检查是否成功打开
    if(!cap.isOpened())
    {
        cerr << "Can not open a camera or file." << endl;
        return -1;
    }
    Mat image;
    for(;;)
    {
        clock_t  begin ,end;//解析有介绍
        begin=clock();
        //从 cap 中读一帧,存到 frame
        cap >> image;
        fps_h = int(cap.get(CV_CAP_PROP_FPS));
        //如果未读到图像
        if(image.empty())
        break;
        //显示结果
        imshow("origin", image);

        Mat element = getStructuringElement(MORPH_RECT,Size(15,15));//生成一个核
        Mat dilate_out;
        dilate(image,dilate_out,element);
        imshow("dilate",dilate_out);

        Mat bi_out;
        erode(dilate_out,bi_out,element);
        imshow("bi",bi_out);
        end=clock();
        fps_s=1/((double)(end - begin) / CLOCKS_PER_SEC);//求软件帧率
        cout << "Frames per second(Hard):" << fps_h  << ",Frames per second(Soft):"<< fps_s <<endl ;
        //等待 30 秒,如果按键则推出循环
        if(waitKey(30) >= 0)
        break;
    }
    return a.exec();
}

2.1 代码解析

clock()

这个函数会返回一个clock_t类型的变量,打开time.h中的定义:typedef long clock_t;所以,这个clock_t就是long int型,这个数字代表从程序开始,过了多少ms,这个long int型可以连续相加1193小时。而CLOCKS_PER_SEC被宏定义为1000。

第43行,(end - begin)表示图像处理的时间,单位为ms,因此需要除以1000,也就是被宏定义为1000的CLOCKS_PER_SEC。

便于理解和使用,进行了上面的描述。想看更详细的解释,看下方的拓展阅读。

clock_t 拓展阅读:https://zhidao.baidu.com/question/18831136.html

此外,Qt中可以使用Qtime类来获取时间。

2.2 软件帧率求解方法

  1. 输出每一帧图像处理时间的倒数。

    这种方法容易受到某一帧的影响,输出的帧率跳动比较厉害。

  2. 按照定义计算一秒钟内输出了多少帧。

    按照定义输出的帧率,比较稳定。第一种方法,是直接将上一次时间与这一次时间作差求倒数即为帧率,而这种方法需要判断上一次时间和这次时间有没有满一秒钟,满一秒钟之后才会输出这一秒时间内计算的帧数。

大家可以把这两种方法都试一下。笔记本硬件帧率应该就是30fps

2.3 硬件帧率与软件帧率

摄像头硬件的帧率是说摄像头一秒钟能读进来多少帧。这个东西和内部的CCD以及CMOS等器件有关。对于工业摄像头可以通过软件来设置摄像头的曝光时间时间以及分辨率。设置的曝光时间越短,那么摄像头就可以在更短的时间内读取一帧图片。图像分辨率越小,对于线阵相机来说,成像时需要扫描的行数就越少,那么自然成像的速度也越快,对于面阵相机来说, 虽然成像时是一个面一次完成,但之后传输数据还是一行一行输出来,耗时就在这里。不过,一旦分辨率以及曝光时间等参数设置好后,摄像头的帧率就是固定的。也就是曝光时间短,图片分辨率小,硬件帧率变大。(回学校以后,大家可以连上迈德威视的摄像头,利用它的上位机调节一下,看一看摄像头的实时帧率。)

软件帧率的话,其实就是说程序一秒钟可以处理多少帧从摄像头读过来的图片。像我们这几天写的代码。处理图像的时间都很短。因此软件的帧率其实几乎等于硬件帧率。而笔记本的硬件帧率我们暂时没有设置的方式。而像比赛中所使用的Robomaster中的代码。在程序处理一帧图像的时间,摄像头可能已经读进来三帧。因此也就是我们嘴边常说的说帧率不够。(不过提高帧率并不是重点,大家可以想一下从100帧提升到300帧。每一帧图像每一次做到的发送节省了7ms。而一个30m/s的弹丸(速度已经很快了)。击打三m以外的装甲板。却需要100ms,所以我个人认为提高帧率自然是多多益善,但是更重要的是坐标的预测。)

那么怎么提高帧率呢?

  1. 用更好的硬件。CPU的主频变高之后,处理每一条指令时间都会缩短,那么程序整体执行的时间就会加快,软件帧率自然会变高。有些时候掉帧,或者帧率变化幅度很大,和CPU频率不稳定也有关系。比如妙算GPU帧率跳动很严重,而CPU版本只有一两帧的跳动。
  2. 优化代码。将不必要的代码通通删减掉,可执行的代码行数减少。时间也会加快。
  3. 及时更改分辨率。根据条件即时变化要处理的图像分辨率,就是对摄像头读进来的frmae进行ROI,比如之前我们学习Mat类时,对矩阵进行裁剪。在图像处理的时间要远远大于摄像头成像的时间的时候,减小摄像头硬件的分辨率其实是没什么作用的,因为相同时间内硬件成像加快,但是软件执行的时间不变。那由于加快硬件成像速度而读进来的图片,还是没有办法进行处理。
  4. 串口115200波特率发送数据,传输包也耗费时间,其实就是也属于优化代码的内容。

微信公众号

欢迎大家关注我的个人公众号,现阶段主要总结Robomaster相关的计算机视觉知识。
公众号名称:三丰杂货铺
在这里插入图片描述

©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页