zoukankan      html  css  js  c++  java
  • 基于opencv下对视频的灰度变换,高斯滤波,canny边缘检测处理,同窗体显示并保存

    如题:使用opencv打开摄像头或视频文件,实时显示原始视频,将视频每一帧依次做灰度转换、高斯滤波、canny边缘检测处理(原始视频和这3个中间步骤处理结果分别在一个窗口显示),最后将边缘检测结果保存为一个视频avi文件。

    这里问题综合性比较大,这里进行分治。

    该类问题可分为四个方面的处理:

    (1)打开 视频或者是摄像头,并播放视频

    (2)对视频的每一帧做处理

    (3)同窗体显示四个结果

    (4)保存视频文件

    以下分为这三个方面进行处理:

    (1)打开 视频或者摄像头,并播放视频

    这个利用opencv来说算是非常简单的操作了:

    步骤分为四步:(简单操作,播放效果不是最佳的,适用于入门)

    a.创建摄像头/播放器

    b.将视频文件赋值给播放器

    c.逐帧播放

    d.释放播放器和文件

    这里直接给上源码(面向对象的方法):

      1 /******************************************************
      2 文件名   :main.cpp
      3 描  述   :灰度转换、高斯滤波、canny边缘检测处理视频,并存储canny边缘处理的视频
      4 语  言   :c++
      5 作  者   :重交亲爸爸
      6 修  改   :
      7 日  期   :2018-05-10
      8 说  明   :opencv的支持
      9 ******************************************************/
     10 #include <cv.h>
     11 #include <stdio.h>
     12 #include <stdarg.h>
     13 #include <time.h>
     14 #include<opencv2highguihighgui.hpp>  
     15 #include<opencv2imgprocimgproc.hpp>  
     16 #include<opencv2corecore.hpp>  
     17 using namespace cv;
     18 using namespace std;
     19 /******************************************************
     20 类  名   :Myclass
     21 描  述   :灰度转换、高斯滤波、canny边缘检测处理视频,并存储canny边缘处理的视频
     22 语  言   :
     23 作  者   :
     24 修  改   :
     25 日  期   :2018-05-10
     26 说  明   :
     27 ******************************************************/
     28 class MyClass
     29 {
     30 public:
     31     MyClass(char* argv);
     32     ~MyClass();
     33     void play();//播放
     34 private:
     35     CvCapture* capture;//视频播放器
     36 };
     37 /******************************************************
     38 函数名称: MyClass
     39 函数功能: 初始化
     40 传入参数:
     41 返 回 值:
     42 建立时间: 2018-05-10
     43 修改时间:
     44 建 立 人: 
     45 修 改 人:
     46 其它说明:
     47 ******************************************************/
     48 MyClass::MyClass(char* argv)
     49 {
     50     //capture = cvCreateFileCapture(argv[1]);
     51     capture = cvCreateFileCapture(argv);
     52     if (!cvGetCaptureProperty(capture, CAP_PROP_FRAME_COUNT))
     53     {
     54         exit(1);
     55     }
     56 }
     57 /******************************************************
     58 函数名称: ~MyClass
     59 函数功能: 释放空间
     60 传入参数:
     61 返 回 值:
     62 建立时间: 2018-05-10
     63 修改时间:
     64 建 立 人: 
     65 修 改 人:
     66 其它说明:very important
     67 ******************************************************/
     68 MyClass::~MyClass()
     69 {
     70     cvReleaseCapture(&capture);
     71     capture = NULL;
     72 }
     73 /******************************************************
     74 函数名称: play
     75 函数功能: 播放
     76 传入参数:
     77 返 回 值:
     78 建立时间: 2018-05-10
     79 修改时间:
     80 建 立 人: 
     81 修 改 人:
     82 其它说明:
     83 ******************************************************/
     84 void MyClass::play(){
     85     cvNamedWindow("播放页面", 1);
     86     IplImage* frame;
     87     while (true)
     88     {
     89         frame = cvQueryFrame(capture);
     90         if (!frame)break;
     91         cvShowImage("播放页面",(frame));
     92         char c = cvWaitKey(33);
     93         if (c == 27)break;//Esc的编码为27
     94     }
     95 }
     96 int main(int argc,char** argv){
     97     //char* a = "F:\Pictures\fcq.avi";
     98     //char* b = "F:\Pictures\fcq2.avi";
     99     if (argc > 2){
    100         MyClass *myclass = new MyClass(argv[1]);
    101         myclass->play();
    102         delete myclass;
    103     }
    104     else{
    105         printf("无视频或者录像存储空间");
    106         exit(1);
    107     }
    108     return 0;
    109 }

     使用vs2013生成解决方案后,打开window power shell进行测试。寻找到文件夹,打开相应的可执行文件

    测试结果:

    ok.你已经成功完成第一步了。^.^

    (2)对视频的每一帧做处理

    a.高斯滤波

    百度百科的解释:高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,广泛应用于图像处理的减噪过程。通俗的讲,高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。高斯滤波的具体操作是:用一个模板(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值。

    这里有opencv封装好的方法可以直接操作,这里采用的是cvSmooth的方法,然后填入核函数CV_GAUSSIAN,设置图像源和转变后的接收源。

     1 /******************************************************
     2 函数名称: R_Gussian
     3 函数功能: 返回高斯滤波处理
     4 传入参数:
     5 返 回 值:
     6 建立时间: 2018-05-10
     7 修改时间:
     8 建 立 人: 
     9 修 改 人:
    10 其它说明:
    11 ******************************************************/
    12 IplImage* MyClass::R_Gussian(IplImage* frame){
    13     //IplImage* edges = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, image->nChannels);
    14     IplImage* edges = cvCreateImage(cvGetSize(frame), frame->depth, frame->nChannels);
    15     cvSmooth(frame, edges, CV_GAUSSIAN, 7, frame->nChannels);  //  Gauss平滑滤波,核大小为7x7  
    16     return edges;
    17 }

    按照(1)中进行测试,结果:

    看上去有点模糊了。

    b.灰度变换

    百度百科的解释:灰度变换是指根据某种目标条件按一定变换关系逐点改变源图像中每一个像素灰度值的方法。目的是为了改善画质,使图像的显示效果更加清晰。 图像的灰度变换处理是图像增强处理技术中的一种非常基础、直接的空间域图像处理方法,也是图像数字化软件和图像显示软件的一个重要组成部分。

    同理,opencv也提供的灰度变化的函数,这里要注意的是转变的时候都是通道变成1

    /******************************************************
    函数名称: R_Gray
    函数功能: 返回灰度处理
    传入参数:
    返 回 值:
    建立时间: 2018-05-10
    修改时间:
    建 立 人: 
    修 改 人:
    其它说明:由于需要在同一个窗体显示,需要将通道数目统一,不能使用单通道
    ******************************************************/
    IplImage* MyClass::R_Gray(IplImage* frame){
        //IplImage* edges = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1);
        //IplImage *frame_gray = cvCreateImage(cvGetSize(image), frame->depth, 1);
        /*IplImage* edges = cvCreateImage(cvGetSize(imagesrc), imagesrc->depth, imagesrc->nChannels);
        cvCvtColor(frame, edges, CV_BGR2GRAY);
        return edges;*/
    
        IplImage *frame_gray = cvCreateImage(cvGetSize(frame), frame->depth, 1);
        IplImage *frame1 = cvCreateImage(cvGetSize(frame), frame->depth, frame->nChannels);
        cvCvtColor(frame, frame_gray, CV_RGB2GRAY);
        cvCvtColor(frame_gray, frame1, CV_GRAY2BGR);//见说明
        return frame1;
    }

    这里多一步的目的是为了后面多个图像显示在一块窗口上(如果不考虑同窗体显示的话就不需要统一通道数)

    结果测试:

    灰度效果显示出来了。

    c.Canny边缘检测

    百度百科的解释:Canny 算法使用 4 个 mask 检测水平、垂直以及对角线方向的边缘。原始图像与每个 mask 所作的卷积都存储起来。对于每个点我们都标识在这个点上的最大值以及生成的边缘的方向。

    这里使用cvCanny的方法进行转变,但是这个cvCanny边缘检测还需要弄成灰度图。

     1 /******************************************************
     2 函数名称: R_Canny
     3 函数功能: 返回Canny边缘处理
     4 传入参数:
     5 返 回 值:
     6 建立时间: 2018-05-10
     7 修改时间:
     8 建 立 人: 
     9 修 改 人:
    10 其它说明: 由于需要在同一个窗体显示,需要将通道数目统一,不能使用单通道
    11 ******************************************************/
    12 IplImage* MyClass::R_Canny(IplImage* frame){
    13     //IplImage* edges = cvCreateImage(cvGetSize(grayimage), IPL_DEPTH_8U, 1);
    14     IplImage *frame_gray = cvCreateImage(cvGetSize(frame), frame->depth, 1);
    15     IplImage *frame1 = cvCreateImage(cvGetSize(frame), frame->depth, frame->nChannels);
    16     cvCanny(frame, frame_gray, 20, 75, 3);
    17     cvCvtColor(frame_gray, frame1, CV_GRAY2BGR);
    18     return frame1;
    19 }

    同样也要统一通道数。

    测试结果:

    这样我们三个图像处理就完成了。^.^

    (3)同窗体显示四个结果

    这里参考了CSDN上的一个方法https://blog.csdn.net/augusdi/article/details/9019473

    作者稍作整理了下,站在巨人的肩膀上。

      1 /******************************************************
      2 函数名称: cvShowManyImages
      3 函数功能: 展示多个图片
      4 传入参数: const char* title, int nArgs
      5 返 回 值:
      6 建立时间: 2018-05-10
      7 修改时间:
      8 建 立 人: CSDN Augusdi
      9 修 改 人: 
     10 其它说明:
     11 ******************************************************/
     12 void MyClass::cvShowManyImages(const char* title, int nArgs, ...){
     13     // img - Used for getting the arguments
     14     IplImage *img;
     15 
     16     // DispImage - the image in which input images are to be copied
     17     IplImage *DispImage;
     18 
     19     int size;
     20     int i;
     21     int m, n;
     22     int x, y;
     23 
     24     // w - Maximum number of images in a row
     25     // h - Maximum number of images in a column
     26     int w, h;
     27 
     28     // scale - How much we have to resize the image
     29     float scale;
     30     int max;
     31 
     32     // If the number of arguments is lesser than 0 or greater than 12
     33     // return without displaying
     34     if (nArgs <= 0) {
     35         printf("Number of arguments too small....n");
     36         return;
     37     }
     38     else if (nArgs > 12) {
     39         printf("Number of arguments too large....n");
     40         return;
     41     }
     42     // Determine the size of the image,
     43     // and the number of rows/cols
     44     // from number of arguments
     45     else if (nArgs == 1) {
     46         w = h = 1;
     47         size = 300;
     48     }
     49     else if (nArgs == 2) {
     50         w = 2; h = 1;
     51         size = 300;
     52     }
     53     else if (nArgs == 3 || nArgs == 4) {
     54         w = 2; h = 2;
     55         size = 300;
     56     }
     57     else if (nArgs == 5 || nArgs == 6) {
     58         w = 3; h = 2;
     59         size = 200;
     60     }
     61     else if (nArgs == 7 || nArgs == 8) {
     62         w = 4; h = 2;
     63         size = 200;
     64     }
     65     else {
     66         w = 4; h = 3;
     67         size = 150;
     68     }
     69 
     70     // Create a new 3 channel image
     71     DispImage = cvCreateImage(cvSize(100 + size*w, 60 + size*h), 8, 3);
     72 
     73     // Used to get the arguments passed
     74     va_list args;
     75     va_start(args, nArgs);
     76 
     77     // Loop for nArgs number of arguments
     78     for (i = 0, m = 20, n = 20; i < nArgs; i++, m += (20 + size)) {
     79 
     80         // Get the Pointer to the IplImage
     81         img = va_arg(args, IplImage*);
     82 
     83         // Check whether it is NULL or not
     84         // If it is NULL, release the image, and return
     85         if (img == 0) {
     86             printf("Invalid arguments");
     87             cvReleaseImage(&DispImage);
     88             return;
     89         }
     90 
     91         // Find the width and height of the image
     92         x = img->width;
     93         y = img->height;
     94 
     95         // Find whether height or width is greater in order to resize the image
     96         max = (x > y) ? x : y;
     97 
     98         // Find the scaling factor to resize the image
     99         scale = (float)((float)max / size);
    100 
    101         // Used to Align the images
    102         if (i % w == 0 && m != 20) {
    103             m = 20;
    104             n += 20 + size;
    105         }
    106 
    107         // Set the image ROI to display the current image
    108         cvSetImageROI(DispImage, cvRect(m, n, (int)(x / scale), (int)(y / scale)));
    109 
    110         // Resize the input image and copy the it to the Single Big Image
    111         cvResize(img, DispImage);
    112 
    113         // Reset the ROI in order to display the next image
    114         cvResetImageROI(DispImage);
    115     }
    116 
    117     // Create a new window, and show the Single Big Image
    118     //cvNamedWindow( title, 1 );
    119     cvShowImage(title, DispImage);
    120 
    121     //cvDestroyWindow(title);
    122 
    123     // End the number of arguments
    124     va_end(args);
    125 
    126     // Release the Image Memory
    127     cvReleaseImage(&DispImage);
    128 }

    这里加入一个多视频播放处理:

    /******************************************************
    函数名称: Multi_play
    函数功能: 播放
    传入参数:
    返 回 值:
    建立时间: 2018-05-10
    修改时间:
    建 立 人: 
    修 改 人:
    其它说明:
    ******************************************************/
    void MyClass::Multi_play(){
        IplImage *frame;
        cvNamedWindow("播放页面按Esc退出", 1);
        cvResizeWindow("播放页面按Esc退出", 700, 660);
        while (true)
        {
            frame = cvQueryFrame(capture);
            if (!frame)break;
            IplImage* grayimage = R_Gray(frame);
            IplImage* gsimage = R_Gussian(frame);
            IplImage* cnimage = R_Canny(frame);
            //IplImage *frame_not = cvCreateImage(cvGetSize(frame), frame->depth, frame->nChannels);//负片
            //cvNot(frame, frame_not);
            //cvShowImage("播放页面按Esc退出", R_Canny(R_Gray(frame)));
            //cvWriteFrame(writer, cnimage);
            cvShowManyImages("播放页面按Esc退出", 4, frame, grayimage, gsimage, cnimage);
            char c = cvWaitKey(33);
            if (c == 27)break;
            
            cvReleaseImage(&grayimage);
            cvReleaseImage(&gsimage);
            cvReleaseImage(&cnimage);
            //cvReleaseImage(&frame_not);
        }
    }

    结果测试:

    成功生成,这里注意一点就是如果放入的图片不是同一通道的话,会报错。

    (4)保存视频文件

    保存视频就比较简单了。

    a.创建视频记录器              CvVideoWriter* writer;

    b.初始化视频记录器           如初始化代码所示

    c.视频记录器记录每一帧    如播放功能代码所示

    d.释放视频记录器              如释放代码所示

     1 /******************************************************
     2 函数名称: MyClass
     3 函数功能: 初始化
     4 传入参数:
     5 返 回 值:
     6 建立时间: 2018-05-10
     7 修改时间:
     8 建 立 人: 
     9 修 改 人:
    10 其它说明:
    11 ******************************************************/
    12 MyClass::MyClass(char* argv,char* filepath)
    13 {
    14     //capture = cvCreateFileCapture(argv[1]);
    15     capture = cvCreateFileCapture(argv);
    16     if (!cvGetCaptureProperty(capture, CAP_PROP_FRAME_COUNT))
    17     {
    18         exit(1);
    19     }
    20     double fps = cvGetCaptureProperty(capture, CV_CAP_PROP_FPS);
    21     CvSize size = cvSize(
    22         (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH),
    23         (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT)
    24         );
    25     writer = cvCreateVideoWriter(
    26         filepath,
    27         CV_FOURCC('M', 'J', 'P', 'G'),
    28         fps,
    29         size
    30         );
    31 }
    32 /******************************************************
    33 函数名称: ~MyClass
    34 函数功能: 释放空间
    35 传入参数:
    36 返 回 值:
    37 建立时间: 2018-05-10
    38 修改时间:
    39 建 立 人: 
    40 修 改 人:
    41 其它说明:
    42 ******************************************************/
    43 MyClass::~MyClass()
    44 {
    45     cvReleaseVideoWriter(&writer);
    46     cvReleaseCapture(&capture);
    47     writer = NULL;
    48     capture = NULL;
    49 }
    50 /******************************************************
    51 函数名称: Multi_play
    52 函数功能: 播放
    53 传入参数:
    54 返 回 值:
    55 建立时间: 2018-05-10
    56 修改时间:
    57 建 立 人: 
    58 修 改 人:
    59 其它说明:
    60 ******************************************************/
    61 void MyClass::Multi_play(){
    62     IplImage *frame;
    63     cvNamedWindow("播放页面按Esc退出", 1);
    64     cvResizeWindow("播放页面按Esc退出", 700, 660);
    65     while (true)
    66     {
    67         frame = cvQueryFrame(capture);
    68         if (!frame)break;
    69         IplImage* grayimage = R_Gray(frame);
    70         IplImage* gsimage = R_Gussian(frame);
    71         IplImage* cnimage = R_Canny(frame);
    72         IplImage *frame_not = cvCreateImage(cvGetSize(frame), frame->depth, frame->nChannels);
    73         cvNot(frame, frame_not);
    74         //cvShowImage("播放页面按Esc退出", R_Canny(R_Gray(frame)));
    75         cvWriteFrame(writer, cnimage);
    76         cvShowManyImages("播放页面按Esc退出", 4, frame, grayimage, gsimage, cnimage);
    77         char c = cvWaitKey(33);
    78         if (c == 27)break;
    79         
    80         cvReleaseImage(&grayimage);
    81         cvReleaseImage(&gsimage);
    82         cvReleaseImage(&cnimage);
    83         cvReleaseImage(&frame_not);
    84     }
    85 }

    ok,在window power shell输入的时候要加上保存地址了

    视频停止或者终止后,在相应文件下就生成了新文件.avi了

    如需要源码请转移至码云:https://gitee.com/cjqbaba/MediaTest/tree/Capture_show进行源码克隆下载

    如有问题请留言评论。转载请注明出处,谢谢。

  • 相关阅读:
    输出流OutputStream简单理解
    IO流实现写入规定的acci码值
    事务的ACID属性&&五种状态
    java基础总结之Hashtable
    HBase
    oracle交换分区
    ArrayList 和 LinkedList 的区别(底层数据结构): 什么时候使用arrayList,什么时候使用LinkedList (一个小时)
    Mac中MariaDB数据库的安装步骤
    Mac OS X中MacPorts安装和使用(linux 的 yum)
    SFTP秘钥认证
  • 原文地址:https://www.cnblogs.com/cjqbaba/p/9023802.html
Copyright © 2011-2022 走看看