【C++】【OpenCV-4.9.0】影片寫入(VideoWriter,藉助samples中的程式碼示例來進行學習)

VanGoghpeng發表於2024-03-13

藉助官方離線文件中的samples來理解VideoWriter

文件位置:samples/cpp/tutorial_code/videoio/video-write/video-write.cpp

注:需要提前下載 openh264-1.8.0-win64.dll,然後放在Release資料夾下 ,否則無法正確對輸出檔案進行編碼從而執行失敗

  1 #include <iostream>
  2 #include <string>
  3 
  4 #include <opencv2/core.hpp>
  5 #include <opencv2/videoio.hpp>
  6 
  7 using namespace std;
  8 using namespace cv;
  9 
 10 void help() {
 11     cout
 12         << "------------------------------------------------------------------------------" << endl
 13         << "This program shows how to write video files." << endl
 14         << "You can extract the R or G or B color channel of the input video." << endl
 15         << "Usage:" << endl
 16         << "./video-write <input_video_name> [ R | G | B] [Y | N]" << endl
 17         << "------------------------------------------------------------------------------" << endl
 18         << endl;
 19 }
 20 
 21 int main(int argc, char* argv[]) {
 22     help();
 23 
 24     // 如果命令列接收的引數長度不等於4則異常退出。
 25     if (argc != 4) {
 26         cout << "Not enough parameters" << endl;
 27         return -1;
 28     }
 29 
 30     // 輸入的影片檔案,如:CV.exe AVI.avi R G , source = "AVI.avi"
 31     const string source = argv[1];
 32     // 判斷使用者是否詢問輸出型別,如果是N,將使用輸入影片的編碼器型別
 33     const bool askOutputType = argv[3][0] == 'Y';
 34 
 35     VideoCapture inputVideo(source);
 36     if (!inputVideo.isOpened()) {
 37         cout << "Could not open the input video: " << source << endl;
 38         return -1;
 39     }
 40 
 41     // 查詢最後一個擴充套件點,返回下標索引位置,如:CV.exe AVI.avi R G ,pAt = 3
 42     string::size_type pAt = source.find_last_of('.');
 43 
 44     // 定義輸出檔名,NAME = "AVIR.avi"
 45     const string NAME = source.substr(0, pAt) + argv[2][0] + ".avi";
 46 
 47     // 獲取影片流中的第一幀的編碼型別,並轉換為int型別(影片流中,每一幀的編碼格式可能都不一樣)
 48     // 例:
 49     // 轉換前 -- 8.75967e+08
 50     // 轉換後 -- 875967080
 51     unsigned int ex = static_cast<int>(inputVideo.get(CAP_PROP_FOURCC));
 52 
 53     // 透過按位運算子從int轉換為char
 54     // 轉換前 -- 875967080 對應的二進位制:00110100 00110110 00110010 01101000
 55     // 轉換後 -- h264
 56     char EXT[] = {
 57         (char)(ex & 0XFF),
 58         (char)((ex & 0XFF00) >> 8),
 59         (char)((ex & 0XFF0000) >> 16),
 60         (char)((ex & 0XFF000000) >> 24),
 61         0
 62     };
 63 
 64     // 獲取輸入影片的尺寸
 65     Size S(
 66         (int)inputVideo.get(CAP_PROP_FRAME_WIDTH),
 67         (int)inputVideo.get(CAP_PROP_FRAME_HEIGHT)
 68     );
 69 
 70     // 建立一個寫入物件,並指定相關引數(輸出影片檔名、編碼格式、輸入影片幀率、輸出影片尺寸、是否為彩色或者灰色)
 71     VideoWriter outputVideo;
 72     if (askOutputType)
 73         outputVideo.open(NAME, ex = -1, inputVideo.get(CAP_PROP_FPS), S, true); // 由CV自動選擇幀的編碼格式
 74     else
 75         outputVideo.open(NAME, ex, inputVideo.get(CAP_PROP_FPS), S, true); // 和輸入影片中的每一幀編碼格式保持一致
 76 
 77     // 如果開啟失敗則輸出錯誤並異常退出
 78     if (!outputVideo.isOpened()) {
 79         cout << "Could not open the output video for write: " << source << endl;
 80         return -1;
 81     }
 82 
 83     // 輸出影片尺寸、總幀數和輸入影片的編碼格式
 84     cout << "Input frame resolution: Width = " << S.width << " Height = " << S.height
 85         << "of nr#: " << inputVideo.get(CAP_PROP_FRAME_COUNT) << endl;
 86     cout << "Input codec type: " << EXT << endl;
 87 
 88     // 選擇顏色通道
 89     int channel = 2;
 90     switch (argv[2][0]) {
 91     case 'R':channel = 2; break;
 92     case 'G':channel = 1; break;
 93     case 'B':channel = 0; break;
 94     }
 95 
 96     Mat src, res;
 97     vector<Mat> spl;
 98 
 99     // 迴圈寫入影片
100     for (;;) {
101         inputVideo >> src;
102         if (src.empty()) break;
103 
104         // 分離通道
105         split(src, spl);
106         
107         // 將不符合選擇的顏色通道都置零處理
108         for (int i = 0; i < 3; ++i)
109             if (i != channel)
110                 spl[i] = Mat::zeros(S, spl[0].type());
111 
112         // 合併通道
113         merge(spl, res);
114 
115         outputVideo << res;
116     }
117 
118     cout << "Finished writing" << endl;
119     return 0;
120 }

執行【CV.exe Video.avi R N】結果:

執行【CV.exe Video.avi R Y】結果:對於給出的報錯說檔名有誤實在沒有頭緒,就懶得繼續深究了。

上述的註釋基本解釋了每行程式碼的作用,現在總結下執行邏輯:

上述程式碼功能——透過選擇不同的顏色通道,將輸入影片另存為選擇好的單一顏色通道的影片檔案。

1、判斷命令列啟動引數個數是否正確;

2、獲取輸入影片檔案的首幀編碼格式、影片檔案尺寸;

3、透過輸入引數中的【R | G | B】的選擇定義輸出影片檔名;

4、建立輸出類物件,並透過輸入引數中的最後一個來確定編碼型別;

5、將輸入檔案根據選擇好的顏色通道進行分離置零再合併,然後迴圈寫入輸出檔案;

注:split對幀進行分離後,得到的三通道順序是BGR,這就是為什麼 ‘R’ 對應的是 channel[2]。

相關文章