【C++/OpenCV】スクリーンキャプチャした映像を表示 or 動画ファイルとして保存
タイトルの通りです。
C++を使ってスクリーンキャプチャを行います。
ちなみに、Python版は↓です。
shizenkarasuzon.hatenablog.com
ではまず、ただ単にモニター映像をリアルタイムで表示するプログラムから(録画機能は無し)
プログラム1(キャプチャ映像表示)
# include <opencv2/opencv.hpp> # include <opencv2/highgui.hpp> # include <opencv2/videoio.hpp> #include <Windows.h> int main(int argc, char * const argv[]){ // モニターサイズ取得 HWND desktop = GetDesktopWindow(); RECT rect; GetWindowRect(desktop, &rect); // DIBの情報を設定する BITMAPINFO bmpInfo; bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmpInfo.bmiHeader.biWidth = rect.right; bmpInfo.bmiHeader.biHeight = -rect.bottom; bmpInfo.bmiHeader.biPlanes = 1; bmpInfo.bmiHeader.biBitCount = 24; //cv::Matの画像をアルファチャンネル有りにする場合はは32; bmpInfo.bmiHeader.biCompression = BI_RGB; LPDWORD lpPixel; HDC hDC = GetDC(desktop); HBITMAP hBitmap = CreateDIBSection(hDC, &bmpInfo, DIB_RGB_COLORS, (void**)&lpPixel, NULL, 0); HDC hMemDC = CreateCompatibleDC(hDC); SelectObject(hMemDC, hBitmap); cv::Mat monitor_img; monitor_img.create(rect.bottom, rect.right, CV_8UC3); //RGBのみ。アルファちゃんねるを加えるにはCV_8UN4 // 画像表示用のWindowを作成(ウィンドウの大きさを640x480に固定する) cv::namedWindow("Screenshot", cv::WINDOW_NORMAL); cv::resizeWindow("Screenshot", 640, 480); while (true){ //hDCの画像(スクリーンショット)をhMemDCにコピーする BitBlt(hMemDC, 0, 0, rect.right, rect.bottom, hDC, 0, 0, SRCCOPY); // hMemDCの内容をcv::Matの画像(monitor_img)にコピー GetDIBits(hMemDC, hBitmap, 0, rect.bottom, monitor_img.data, (BITMAPINFO *)&bmpInfo, DIB_RGB_COLORS); //copy from hwindowCompatibleDC to hbwindow cv::imshow("Screenshot", monitor_img); // スクリーンショットの表示 if (cv::waitKey(1) == 27) break; //Escキーで終了 } //メモリ開放 ReleaseDC(desktop, hDC); DeleteDC(hMemDC); DeleteObject(hBitmap); return 0; }
実行結果1
プログラムを実行すると、下の画像の様にスクリーンの映像がリアルタイムで表示されます
終了するにはEscキーを押してください。
プログラム2(キャプチャした映像をmp4で保存)
上のプログラム1を少し改良して、スクリーンキャプチャした映像をmp4形式で録画してみようと思います。
# include <opencv2/opencv.hpp> # include <opencv2/highgui.hpp> # include <opencv2/videoio.hpp> #include <Windows.h> #include <string> #include <vector> #include <time.h> #include <conio.h> void main() { /* デスクトップのサイズ */ HWND desktop = GetDesktopWindow(); RECT rect; GetWindowRect(desktop, &rect); int width = rect.right; int height = rect.bottom; // DIBの情報を設定する BITMAPINFO bmpInfo; bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmpInfo.bmiHeader.biWidth = width; bmpInfo.bmiHeader.biHeight = -height; bmpInfo.bmiHeader.biPlanes = 1; bmpInfo.bmiHeader.biBitCount = 24; //cv::Matの画像をアルファチャンネル有りにする場合はは32; bmpInfo.bmiHeader.biCompression = BI_RGB; LPDWORD lpPixel; HDC hDC = GetDC(desktop); HBITMAP hBitmap = CreateDIBSection(hDC, &bmpInfo, DIB_RGB_COLORS, (void**)&lpPixel, NULL, 0); HDC hMemDC = CreateCompatibleDC(hDC); SelectObject(hMemDC, hBitmap); cv::Mat monitor_img; monitor_img.create(height, width, CV_8UC3); //RGBのみ。アルファちゃんねるを加えるにはCV_8UN4 //スクリーンショットの映像一覧 std::vector<cv::Mat> screen_shot_list; std::cout << "Start Recording !!" << std::endl; clock_t start = clock(); cv::Mat cloned_img; while (_kbhit() == 0) { //何か入力があるまで録画し続ける //hDCの画像(スクリーンショット)をhMemDCにコピーする BitBlt(hMemDC, 0, 0, width, height, hDC, 0, 0, SRCCOPY); // hMemDCの内容をcv::Matの画像(monitor_img)にコピー GetDIBits(hMemDC, hBitmap, 0, height, monitor_img.data, (BITMAPINFO *)&bmpInfo, DIB_RGB_COLORS); //copy from hwindowCompatibleDC to hbwindow cloned_img = monitor_img.clone(); // cv::Matはポインターのようなやつなので、cloneして実体を作らないと正しくpush_backできない screen_shot_list.push_back(cloned_img); } //cv::String fname("SC.mp4"); clock_t end = clock(); double duration = static_cast<double>(end - start) / CLOCKS_PER_SEC * 1000.0; int fps = int(float(screen_shot_list.size() * 1000)/ duration); std::cout << "-------------------------------------" << std::endl; std::cout << "duration[ms] = " << duration << std::endl; std::cout << "frame = " << screen_shot_list.size() << std::endl; std::cout << "FPS = " << fps << std::endl; //std::cout << "saving as \"" << "SC.mp4" << " \"" << std::endl; std::cout << "width = " << width << std::endl; std::cout << "height = " << height << std::endl; std::cout << "-------------------------------------\n"; //http://shibafu3.hatenablog.com/entry/2016/11/13/151118 int codec; // codec = cv::VideoWriter::fourcc('x', '2', '6', '4'); // codec = cv::VideoWriter::fourcc('m', 'p', 'e', 'g'); codec = cv::VideoWriter::fourcc('m', 'p', '4', 'v'); cv::VideoWriter writer = cv::VideoWriter("SC.mp4", codec, fps, cv::Size(width, height)); for (int i = 0; i < screen_shot_list.size(); i++) { // 1フレーム書き込む // writer.write(screen_shot_list[i])でも可 writer << screen_shot_list[i]; } writer.release(); std::cout << "wrote video successfully !!\n"; std::cout << "Prease press some key...\n"; std::string s; std::cin >> s; //メモリ開放 ReleaseDC(desktop, hDC); DeleteDC(hMemDC); DeleteObject(hBitmap); return; }
実行結果2
上のプログラムを実行すると、
Start Recording !!
と表示されます。この状態から録画スタートです。
そして、録画を停止するには何かキーを押してください。すると、
------------------------------------- duration[ms] = 5622 frame = 167 FPS = 29 width = 1536 height = 864 -------------------------------------
のような表示が出て、その後SC.mp4という名前でキャプチャ映像が書きだされるはずです。