基於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<opencv2\highgui\highgui.hpp>  
 15 #include<opencv2\imgproc\imgproc.hpp>  
 16 #include<opencv2\core\core.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進行源碼克隆下載

如有問題請留言評論。轉載請注明出處,謝謝。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM