[英]Create a divx-encoded avi from frames using opencv
這個問題類似於這一個,特別是這一個,但我期望的輸出是不同的。 我正在嘗試使用 opencv 將桌面捕獲到視頻中。 首選輸出是帶有 divx 編碼的 avi 文件。 我是 opencv 和位圖編程的新手。
作為第一步,為了確保 divx 編解碼器存在,我創建了一個純色(黃色)的單幀 (cv::Mat) 並將其寫入視頻文件 100 次,如下所示:
int main(int argc, char* argv[])
{
cv::Mat frame(1200, 1920, CV_8UC3, cv::Scalar(0, 50000, 50000));
cv::VideoWriter* videoWriter = new cv::VideoWriter(
"C:/videos/desktop.avi",
CV_FOURCC('D','I','V','3'),
5, cv::Size(1920, 1200), true);
int frameCount = 0;
while (frameCount < 100)
{
videoWriter->write(frame);
::Sleep(100);
frameCount++;
}
delete videoWriter;
return 0;
}
這非常有效 - 視頻文件已創建,可以在我的 Win 10 機器上使用 VLC、Windows Media Player 或 Films&TV 應用程序播放。 它是 100 幀純黃色,但它表明視頻正在正確創建。
下一步:用一系列桌面截圖替換上面代碼中的虛擬 cv::Mat 框架。 我使用GetDesktopWindow()獲得桌面窗口的句柄,然后使用函數 hwnd2mat(取自這個SO question - 謝謝!)將從桌面句柄獲得的位圖轉換為 cv::Mat ,我可以寫入我的視頻。
我逐字復制了 hwnd2mat 函數,除非我不縮放圖像 - 桌面位圖已經是 1920x1200,而且我創建的 cv::Mat 是 CV_8UC3 而不是 CV_8UC4(CV_8UC4 導致我的應用程序崩潰)。
這是代碼,包括 hwnd2mat 的重印:
int main(int argc, char* argv[])
{
cv::VideoWriter* videoWriter = new cv::VideoWriter(
"C:/videos/desktop.avi",
CV_FOURCC('D','I','V','3'),
5, Size(1920, 1200), true);
int frameCount = 0;
while (frameCount < 100)
{
HWND hDsktopWindow = ::GetDesktopWindow();
cv::Mat frame = hwnd2mat(hDsktopWindow);
videoWriter->write(frame);
::Sleep(100);
frameCount++;
}
delete videoWriter;
return 0;
}
cv::Mat hwnd2mat(HWND hwnd)
{
HDC hwindowDC, hwindowCompatibleDC;
int height, width, srcheight, srcwidth;
HBITMAP hbwindow;
cv::Mat src;
BITMAPINFOHEADER bi;
hwindowDC = GetDC(hwnd);
hwindowCompatibleDC = CreateCompatibleDC(hwindowDC);
SetStretchBltMode(hwindowCompatibleDC, COLORONCOLOR);
RECT windowsize; // get the height and width of the screen
GetClientRect(hwnd, &windowsize);
srcheight = windowsize.bottom;
srcwidth = windowsize.right;
height = windowsize.bottom / 1; //change this to whatever size you want to resize to
width = windowsize.right / 1;
src.create(height, width, CV_8UC3);
// create a bitmap
hbwindow = CreateCompatibleBitmap(hwindowDC, width, height);
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = width;
bi.biHeight = -height; //this is the line that makes it draw upside down or not
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
// use the previously created device context with the bitmap
SelectObject(hwindowCompatibleDC, hbwindow);
// copy from the window device context to the bitmap device context
StretchBlt(hwindowCompatibleDC, 0, 0, width, height, hwindowDC, 0, 0,srcwidth, srcheight, SRCCOPY);
GetDIBits(hwindowCompatibleDC, hbwindow, 0, height, src.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
// avoid memory leak
DeleteObject(hbwindow); DeleteDC(hwindowCompatibleDC); ReleaseDC(hwnd,hwindowDC);
return src;
}
這樣做的結果是創建了視頻文件並且可以正常播放,但它只是純灰色。 桌面位圖似乎沒有正確復制到 cv::Mat 框架中。 我已經嘗試了 BITMAPINFOHEADER 中的無數個值組合,但沒有任何效果,老實說我不知道我在做什么。 我知道 opencv 有轉換功能,但同樣,我什至不知道我想轉換到/從什么。
任何幫助表示贊賞!
想出一種方法來使它工作 - 我不知道這是否是最好的方法,所以仍然歡迎評論或替代解決方案。
似乎要讓GetDIBits工作,cv::Mat必須是 4 通道,即 CV_8UC4,就像我更改之前的 hwnd2mat 的原始代碼一樣。 如果不是 CV_8UC4,則不會復制任何數據(GetDIBits 返回復制的 0 個掃描線),這就是為什么我的 avi 只是灰色的原因。 因此,第一個更改是將 src cv::Mat 創建為 4 通道:
src.create(height, width, CV_8UC4);
但是對於我嘗試創建的 divx 編碼的 avi 文件,幀應該是 3 通道的(不要問我為什么)。 我在調用GetDIBits()后添加了一個opencv轉換函數的調用,如下:
cv::Mat dst;
dst.create(height, width, CV_8UC3);
cv::cvtColor(src, dst, CV_RGBA2RGB);
然后我從 hwnd2mat 而不是 src 返回 dst。 對 cvtColor 的調用刪除了 alpha 通道(RGBA 中的 A),而 dst 最終只有 R、G、B 通道。
您可以從 GetDIBits 獲取沒有 alpha 通道的位圖並將其直接寫入 cv::VideoWriter。 只需將 biBitCount 更改為 24。將 Mat 格式保留為 CV_8IC3。 這對我有用。 src.create(高度,寬度,CV_8UC3);
bi.biBitCount = 24; // 這是要改變的地方
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.