藉助官方離線文件中的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]。