QT下利用OpenCV进行视频震动检测

项目背景:

用于检测地震,火山喷发,海啸,等任何能产生次声波得现象。

本项目就是利用摄像头观察水面得震动来判断有没有次声波(水面可以放漂浮物也可以不放)

一,移植opencv

首先要使用Opencv那么就必须移植,因为QT原本不支持,这里最主要看.pro文件中要添加的opencv路径

TARGET_ARCH = $${QT_ARCH}
contains(TARGET_ARCH, arm){
    CONFIG += link_pkgconfig
    PKGCONFIG += opencv
    INCLUDEPATH += /opt/fsl-imx-x11/4.1.15-2.1.0/sysroots/cortexa7hf-neon-poky-linux-gnueabi/usr/include
} else {
    LIBS += -L/usr/local/lib \
            -lopencv_core \
            -lopencv_highgui \
            -lopencv_imgproc \
            -lopencv_videoio \
            -lopencv_imgcodecs

    #INCLUDEPATH可写可不写,系统会到找到此路径
    INCLUDEPATH += /usr/local/include
}

二、摄像头得到的QImage转化为Mat类型

我们知道opencv中最继承的类型就是mat,使用OpenCV进行图像处理那么我们必须将QImage转化为Mat。

在摄像头中添加一个信号:

我们这个震动显示的项目其实是基于摄像头项目的。在那个项目中,摄像头类中通过一定时器,以一定的频率将QImage发生给mainwindow,然后mainwindow进行渲染图像。

void Camera::timerTimeOut()
{
    /* 如果摄像头没有打开,停止定时器,返回 */
    if (!capture->isOpened()) {
        timer->stop();
        return;
    }
    static cv :: Mat frame;
    *capture >> frame;

    if (frame.cols)
    {
        /* 发送图片信号 */
        emit readyImage(matToQImage(frame));
        //emit check(matToQImage(frame),frame);
    }
}

而我们要实现图像处理我们当然得在mainwindow添加一个新的处理函数,然后在摄像头类中添加一个信号与其连接。

1.添加连接

connect(camera, SIGNAL(check(QImage,cv::Mat)),this, SLOT(showchanged(QImage,cv::Mat)));

2.添加信号

emit check(matToQImage(frame),frame);

三、两种方法进行震动检测


一、帧差法+背景减除法

其中帧差法主要用于数值上得震动判断,而背景减除法主要用于视觉上得震动判断。

1.帧差法:

原理其实很简单,我们知道mat类型数据其实保存着每个像素点的值。而我们判断视频有没有动只需要将两帧图像的mat相减就行了。

但是这里有个注意点:

那就是,即使视频没有发生改变,我们两帧图像相减得到的数值还是不为0,只是很小。所有我们判断视频有没有震动,或者震动的剧烈情况,我们就要将两帧相减的值与我们设定的阈值比较。如果大于这个阈值就说明是否发生震动,或者发生几级震动。

步骤:

1.将上一次的mat与这一次得到的mat一起进行灰度化。(进行灰度化的原因在于:灰度化之后,mat的信息更小,这样再进行图像处理误差会更小)

2.将两帧图像进行相减

3.将相减后的mat的所有像素点取平均值

cv::Mat mat1_grey,mat2_grey,mat_diff;
//先转化为灰度图再做判断
cvtColor(mat1, mat1_grey, COLOR_BGR2GRAY);
cvtColor(mat2, mat2_grey, COLOR_BGR2GRAY);

absdiff(mat1_grey,mat2_grey,mat_diff);
avg = mean(mat_diff)[0];

4.然后再根据阈值进行判断,分支处理 (当然这些阈值也是要经过实验之后确定的,比如是否震动的阈值)

    if(avg>(jiaoyan_avg+0.02))
    qDebug()<<"avg"<<avg<<"震动";
    else
    qDebug()<<"avg"<<avg;

    if(avg>(jiaoyan_avg+5))
    {
        flag = 10;
        sprintf(buffer, "%f:>%f\n",(jiaoyan_avg+30));
        question2->setText(buffer);
        goto label1;
        return true;
    }
    else if(avg>(jiaoyan_avg+2))
    {
        flag = 10;
        sprintf(buffer, "%f:>%f\n",(jiaoyan_avg+10));
        question2->setText(buffer);
        return true;
    }
    else if(avg>(jiaoyan_avg+1))
    {
        flag = 10;
        sprintf(buffer, "%f:>%f\n",(jiaoyan_avg+5));
        question2->setText(buffer);
        return true;
    }
    else if(avg>(jiaoyan_avg+0.02))
    {
        flag = 10;
        sprintf(buffer, "%f:>%f\n",avg,(jiaoyan_avg+0.02));
        question2->setText(buffer);
        return true;
    }
2.背景减除法:

背景减除法要实现的效果就是先将图片二值化(二值化就是黑白图片),将动的部分显示出来。

步骤:

1.或得帧差法得到得差值mat

2.讲差值mat进行二值化

3.再进行腐蚀操作(作用在于去除那些微弱得干扰)

4.渲染

avg = mean(mat_diff)[0];

//相减后的mat进行阈值处理
threshold(mat_diff,mat_diff,40,255,THRESH_BINARY);
//腐蚀
Mat kernel_erode = getStructuringElement(MORPH_RECT, Size(3, 3));
erode(mat_diff,mat_diff,kernel_erode);

//处理后的mat转化成Qimage
back_dec = Mat2QImage(mat_diff);

二、漂浮物几何中心相减法

上面的方法是要记住上一次的mat,而几何中心法是要先描绘出水面的漂浮物的几何中心,然后记录这个几何中心,并与上一次的几何中心进行比较。

同时视觉上也要显示出勾勒出的几何轮廓以及几何中心。

步骤:

1.对获得的mat进行灰度化

2.对灰度化的mat进行平滑处理(作用在与去除微弱的干扰)

3.对平滑处理后的mat进行二值化(注意这里的二值化阈值要根据自己的情况来定,不同的场景二值化的阈值也要设置不同的,原因在于我们要突出漂浮物在其它背景上,而漂浮物与背景的颜色就决定了阈值不是固定的)

4.获取图像中所有的外接多边形,以及外接多边形的内切圆的圆心和半径(轮廓其实就是外接多边形,而有多个的原因在于图像或多或少是存在干扰的,而我们放的漂浮物最好就一个)

5.根据我们需要的漂浮物的外接多边形的内切圆的半径,筛选出只有我们想要的轮廓,并且获取其圆心(当然这个半径也需要我们自己调试之后才能发现)

6.渲染漂浮物的内切圆以及它的几何中心。

void MainWindow::CenterCompare(const cv::Mat mat)
{
    char buffer[256];
    static int contours_size=0;
    float x_change = 0 ;
    float y_change = 0;
    /*找出漂浮物的轮廓与几何中心*/
    Mat image_gray;
    cvtColor(mat, image_gray, cv::COLOR_BGR2GRAY);
    blur( image_gray, image_gray, Size(3,3) );//平滑处理
    Mat threshold_output;
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;
    threshold( image_gray, threshold_output, 25/*这个量要自己设置,我用的是黑色正方形的的漂浮物*/, 255, THRESH_BINARY );//二值化处理,阈值为20
    findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
    vector<vector<Point> > contours_poly( contours.size() );
    vector<Rect> boundRect( contours.size() );
    vector<Point2f>center( contours.size() );
    vector<float>radius( contours.size() );
    for( size_t i = 0; i < contours.size(); i++ )
    {
      approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
      boundRect[i] = boundingRect( Mat(contours_poly[i]) );
      minEnclosingCircle( contours_poly[i], center[i], radius[i] );
    }
    qDebug()<<"contours"<<contours.size();

    /*排除干扰*/
    for( size_t i = 0; i< contours.size(); i++ )
    {
      if(abs(radius[i]-26)<1)//这个26是要根据自己的漂浮物找出来的,如果没有筛选会出现contours.size个轮廓,你必须找出自己漂浮物的轮廓的大致半径
      {
          contours_size++;
          if(contours_size>1)//我们只允许出现我们想要的轮廓
          {
               contours_size = 0;
               return;
          }
      }
    }
    contours_size = 0;
    for( size_t i = 0; i< contours.size(); i++ )
    {
      if(abs(radius[i]-26)<1)
      {
            if(last_center.x==0&&last_center.y==0)//如果是第一次那么直接返回
            {
                last_center.x = center[i].x;
                last_center.y = center[i].y;
                return;
            }
            x_change = abs(center[i].x-last_center.x);
            y_change = abs(center[i].y-last_center.y);
            if(x_change>0.5&&y_change>0.5)//如果都大于0.5表示震动
            {
                flag = true;
                sprintf(buffer, "振幅: %f\n",x_change+y_change);//直接用x,y改变量之和表示振幅
                question2->setText(buffer);
            }
            else
                flag = false;
            center_point.x = last_center.x = center[i].x;
            center_point.y = last_center.y = center[i].y;
            Scalar color = Scalar(0,255,0);//给绿色
            rectangle( mat, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );//画出轮廓外接多边形
            circle( mat, center[i], (int)radius[i], color, 2, 8, 0 );//画出多边形内切圆
            circle( mat, center_point, 1, color, 2, 8, 0 );
            qDebug()<<"r:"<<radius[i];
            qDebug()<<center[i].x<<","<<center[i].y;
      }
    }
    //mat转 Qimage
    cvtColor(mat, mat, CV_BGR2RGB);
    QImage imaged(mat.data,
                 mat.cols,
                 mat.rows,
                 mat.step,
                 QImage::Format_RGB888);
     displayLabel->setPixmap(QPixmap::fromImage(imaged));
     if(flag)
     {
         photoLabel1->setPixmap(QPixmap::fromImage(imaged));
     }
     else
     {
         photoLabel1->setPixmap(QPixmap::fromImage(*img));
     }
}
源码地址:(gittee下载)

背景消除法:

https://gitee.com/hktk-cww/qt-opencv-backdec.git

描绘轮廓法:

https://gitee.com/hktk-cww/qt-opencv-contour.git

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值