1.VideoWriter可将数据流保存为指定格式的视频或图像序列:
(1)录制为视频数据,需要指定视频的完整路径,如/root/data/vtest.avi;同时需要指定fourcc、帧率、图像尺寸及是否保存为彩色,fourcc值参见OpenCV官网指定的链接。
(2)保存为图像序列,需要指定图像序列的完整路径,其中路径中的文件名的格式同C言语中prinf的格式,可参见《CV学习日志:C语言中文件IO使用要点》。如/root/data/ima%d.png;如/root/data/ima%03d.png,这里%03d表示图像名序列号是整数、占宽三位、不足三位的高位是零填充。同时,需要设置fourcc或帧率为0。
2.VideoWriter常用的函数是构造和write:
(1)构造函数:用于创建数据流。
(2)write函数:写一帧数据流。
3.类AboutVideoIO是VideoCapture和VideoWriter的综合使用样例,其功能如下:
(1)打开数据流功能:可打开相机、视频或图像序列,相机指定设备号、视频和图像序列指定完整路径。
(2)保存数据流功能:按空格保存图像到给定的目录或默认的workDir/data,图像名是时间戳,单位纳秒。
(3)帧显示时间设置功能:默认每帧显示30毫秒,可指定为期望显示的时间,其中设为零表示一直显示直到按下任意键。
(4)帧高度宽度设置功能:对于相机数据流,可指定相机支持的尺寸,否则为相机默认的输出尺寸。
(5)录制数据流功能:按1开始或继续录制图像到给定的目录或默认的workDir/data,按2暂停录制。
(6)录制时帧率设置功能:当帧率设置为0时,录制为图像序列videoio*.png,否则录制为视频videoio.avi。
(7)录制时编码设置功能:即VideoWriter::fourcc码,当帧率设置为0时,此设置无效,内部自动置为0。
(8)录制时色彩设置功能:当设置为0时录制为灰度流,否则录制为彩色流,默认录制为彩色流。
(9)自动生成配置文件功能:首次运行或配置文件不存在将自动生成workDir/videoio.yml,然后修改配置项为期望值后重启。
可在https://github.com/opencv/opencv/tree/master/samples/data下载vtest.avi和left*.jpg测试视频流和图像序列。
也可参见关于VedioCapture的使用样例《OpenCV-Utils学习日志:VideoCapture使用样例》
以下是详细代码,依赖于C++14、OpenCV4.x和Spdlog。
1 #include <opencv2/opencv.hpp> 2 #include <opencv2/core/utils/filesystem.hpp> 3 #include <spdlog/spdlog.h> 4 using namespace std; 5 using namespace cv; 6 7 #ifndef StrPairKey 8 #define StrPairKey(key) make_pair(#key, key) 9 #define StrPairVal(val) make_pair(val, #val) 10 #endif 11 12 class AboutVideoIO 13 { 14 public: 15 map<int, pair<int, string>> capProps = 16 { 17 make_pair(0, StrPairVal(CAP_PROP_POS_MSEC)), 18 make_pair(1, StrPairVal(CAP_PROP_POS_FRAMES)), 19 make_pair(2, StrPairVal(CAP_PROP_FRAME_COUNT)), 20 make_pair(3, StrPairVal(CAP_PROP_POS_AVI_RATIO)), 21 22 make_pair(4, StrPairVal(CAP_PROP_FRAME_WIDTH)), 23 make_pair(5, StrPairVal(CAP_PROP_FRAME_HEIGHT)), 24 make_pair(6, StrPairVal(CAP_PROP_FPS)), 25 make_pair(7, StrPairVal(CAP_PROP_FOURCC)), 26 make_pair(8, StrPairVal(CAP_PROP_FORMAT)), 27 make_pair(9, StrPairVal(CAP_PROP_MODE)), 28 29 make_pair(10, StrPairVal(CAP_PROP_BRIGHTNESS)), 30 make_pair(11, StrPairVal(CAP_PROP_HUE)), 31 make_pair(12, StrPairVal(CAP_PROP_SATURATION)), 32 make_pair(13, StrPairVal(CAP_PROP_CONTRAST)), 33 make_pair(14, StrPairVal(CAP_PROP_GAIN)), 34 make_pair(15, StrPairVal(CAP_PROP_EXPOSURE)) 35 }; 36 37 public: 38 struct VideoIO 39 { 40 char imaFrom[512] = "0"; 41 char saveDir[512] = "./data"; 42 int waitTime = 30; 43 int deviceRows = 0; 44 int deviceCols = 0; 45 int writeFPS = 24; 46 char writeFourcc[8] = "DX50"; 47 bool writeColor = true; 48 void write(FileStorage& fs) 49 { 50 fs << "imaFrom" << imaFrom; fs.writeComment("deviceId like\"0\" or videoPath like\"/root/data/vtest.avi\" or imaListPath like\"/root/data/left%03d.jpg)\"", true); 51 fs << "saveDir" << saveDir; fs.writeComment("auto created if not existing", true); 52 fs << "waitTime" << waitTime; fs.writeComment("0-wait for pressing any key", true); 53 fs << "deviceRows" << deviceRows; fs.writeComment("0-auto size", true); 54 fs << "deviceCols" << deviceCols; fs.writeComment("0-auto size", true); 55 fs << "writeFPS" << writeFPS; fs.writeComment("0-save as videoio*.png or else videoio.avi", true); 56 fs << "writeFourcc" << writeFourcc; 57 fs << "writeColor" << writeColor; 58 } 59 void read(FileStorage& fs) 60 { 61 strcpy(imaFrom, fs["imaFrom"].string().c_str()); 62 strcpy(saveDir, fs["saveDir"].string().c_str()); 63 fs["waitTime"] >> waitTime; 64 fs["deviceRows"] >> deviceRows; 65 fs["deviceCols"] >> deviceCols; 66 fs["writeFPS"] >> writeFPS; 67 strcpy(writeFourcc, fs["writeFourcc"].string().c_str()); 68 fs["writeColor"] >> writeColor; 69 } 70 string print(string savePath = "") 71 { 72 string str; 73 str += fmt::format("imaFrom: {}\n", imaFrom); 74 str += fmt::format("saveDir: {}\n", saveDir); 75 str += fmt::format("waitTime: {}\n", waitTime); 76 str += fmt::format("deviceRows: {}\n", deviceRows); 77 str += fmt::format("deviceCols: {}\n", deviceCols); 78 str += fmt::format("writeFPS: {}\n", writeFPS); 79 str += fmt::format("writeFourcc: {}\n", writeFourcc); 80 str += fmt::format("writeColor: {}\n", writeColor); 81 if (savePath.empty() == false) { FILE* out = fopen(savePath.c_str(), "w"); fprintf(out, str.c_str()); fclose(out); } 82 return str; 83 } 84 static VideoIO GetOne(string fsPath, bool doPrint = true) 85 { 86 VideoIO videoIO; 87 if (cv::utils::fs::exists(fsPath) == false) 88 { 89 cv::utils::fs::createDirectories(getFilePathInfo(fsPath)); 90 FileStorage fs(fsPath, FileStorage::WRITE); 91 videoIO.write(fs); 92 fs.release(); 93 //memset(&videoIO, 0, sizeof(videoIO)); 94 spdlog::critical("No exist: {}", fsPath); 95 spdlog::info("Created file: {}", fsPath); 96 spdlog::info("Modify default values and relaunch"); 97 } 98 else 99 { 100 FileStorage fs(fsPath, FileStorage::READ); 101 videoIO.read(fs); 102 fs.release(); 103 } 104 if (doPrint) spdlog::info(videoIO.print()); 105 return videoIO; 106 } 107 static string getFilePathInfo(string path, int mode = 0/*0=dir 1=basename 2=extname 3=dirbasename 4=filename*/) 108 { 109 int ind1 = (int)path.find_last_of('/'); 110 int ind2 = (int)path.find_last_of('.'); 111 if (mode == 0) return path.substr(0, ind1); 112 else if (mode == 1) return path.substr(ind1 + 1, ind2 - ind1 - 1); 113 else if (mode == 2) return path.substr(ind2 + 1); 114 else if (mode == 3) return path.substr(0, ind2); 115 else return path.substr(ind1 + 1); 116 } 117 }; 118 119 public: 120 void TestMe(int argc = 0, char** argv = 0) 121 { 122 //0.GetParams 123 string videoioCfgPath = "./videoio.yml"; 124 VideoIO videoIO = VideoIO::GetOne(videoioCfgPath); 125 126 //1.CreatDirectory 127 utils::fs::createDirectories(videoIO.saveDir); 128 spdlog::set_pattern("%v"); spdlog::info(videoIO.print()); 129 130 //2.OpenStream 131 VideoCapture cap; 132 if (std::strlen(videoIO.imaFrom) == 1) 133 if (cap.open(atoi(videoIO.imaFrom)) == false) { spdlog::critical("Failed to open device: {}", videoIO.imaFrom); return; } 134 if (std::strlen(videoIO.imaFrom) > 1) 135 if (cap.open(videoIO.imaFrom) == false) { spdlog::critical("Failed to open file: {}", videoIO.imaFrom); return; } 136 137 //3.SetDevice 138 if (videoIO.deviceRows != 0) cap.set(CAP_PROP_FRAME_HEIGHT, videoIO.deviceRows); 139 if (videoIO.deviceCols != 0) cap.set(CAP_PROP_FRAME_WIDTH, videoIO.deviceCols); 140 141 //4.GetDevice 142 spdlog::info("cap.isOpened(): {}", cap.isOpened()); 143 spdlog::info("cap.getBackendName(): ", cap.getBackendName()); 144 for (map<int, pair<int, string>>::iterator it = capProps.begin(); it != capProps.end(); ++it) 145 spdlog::info("cap.get({}): {}", it->second.second, cap.get(it->second.first)); 146 147 //5.CreatStream 148 string path = fmt::format("{}/{}", videoIO.saveDir, videoIO.writeFPS == 0 ? "videoid%04d.png" : "videoio.avi"); 149 int fourcc = videoIO.writeFPS == 0 ? 0 : VideoWriter::fourcc(videoIO.writeFourcc[0], videoIO.writeFourcc[1], videoIO.writeFourcc[2], videoIO.writeFourcc[3]); 150 VideoWriter wrt(path, fourcc, videoIO.writeFPS, Size((int)cap.get(CAP_PROP_FRAME_WIDTH), (int)cap.get(CAP_PROP_FRAME_HEIGHT)), videoIO.writeColor); 151 spdlog::info("wrt.isOpened(): {}", wrt.isOpened()); 152 spdlog::info("wrt.getBackendName(): ", wrt.getBackendName()); 153 spdlog::info("wrt.get(VIDEOWRITER_PROP_QUALITY): {}", wrt.get(VIDEOWRITER_PROP_QUALITY)); 154 spdlog::info("wrt.get(VIDEOWRITER_PROP_FRAMEBYTES): {}", wrt.get(VIDEOWRITER_PROP_FRAMEBYTES)); 155 spdlog::info("wrt.get(VIDEOWRITER_PROP_NSTRIPES): {}", wrt.get(VIDEOWRITER_PROP_NSTRIPES)); 156 spdlog::info("wrt.get(VIDEOWRITER_PROP_IS_COLOR ): {}", wrt.get(VIDEOWRITER_PROP_IS_COLOR)); 157 158 //6.ReadWriteStream 159 cv::namedWindow(__FUNCTION__, WINDOW_NORMAL); 160 spdlog::info("Press space to save one and q/Q to exit"); 161 spdlog::info("press 1 to start/continue and 2 to stop recording"); 162 Mat frame; int flag = 0; 163 while (cap.read(frame)) 164 { 165 //6.1 ShowImage 166 cv::imshow(__FUNCTION__, frame); 167 int c = cv::waitKey(videoIO.waitTime); 168 if (c == 'q' || c == 'Q') break; 169 170 //6.2 SaveImage 171 if (c == ' ') 172 { 173 string savePath = videoIO.saveDir + fmt::format("/{}.png", chrono::time_point_cast<chrono::nanoseconds>(chrono::system_clock::now()).time_since_epoch().count()); 174 cv::imwrite(savePath, frame); 175 spdlog::info("Saved to: " + savePath); 176 } 177 178 //6.3 RecordImage 179 if (c == '1' || c == '2') flag = c; 180 if (flag == '1') { wrt.write(frame); spdlog::info("Record timestamp: {}", chrono::time_point_cast<chrono::nanoseconds>(chrono::system_clock::now()).time_since_epoch().count()); } 181 } 182 cv::destroyWindow(__FUNCTION__); 183 } 184 }; 185 186 int main(int argc, char** argv) { AboutVideoIO me; me.TestMe(argc, argv); return 0; }