C++ 使用opencv从帧创建divx编码的avi
这个问题类似于,特别是我想要的结果不同。我正在尝试使用opencv将桌面捕获为视频。首选输出是带有divx编码的avi文件。一般来说,我对opencv和位图编程不熟悉 作为第一步,为了确保divx编解码器存在,我创建了一个单色(黄色)的帧(cv::Mat),并将其写入视频文件100次,如下所示:C++ 使用opencv从帧创建divx编码的avi,c++,windows,opencv,bitblt,C++,Windows,Opencv,Bitblt,这个问题类似于,特别是我想要的结果不同。我正在尝试使用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))
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;
}
intmain(intargc,char*argv[])
{
cv::Mat框架(1200、1920、cv_8UC3、cv::Scalar(0、50000、50000));
cv::VideoWriter*VideoWriter=新cv::VideoWriter(
“C:/videos/desktop.avi”,
CV_FOURCC('D','I','V','3'),
5,cv::尺寸(1920,1200),真值;
int frameCount=0;
而(帧数<100)
{
录像机->写入(帧);
::睡眠(100);
frameCount++;
}
删除录像机;
返回0;
}
这是完美的工作-视频文件是创建的,可以在我的Win 10机器上使用VLC、Windows Media Player或电影和电视应用程序播放。它是100帧纯黄色,但它显示视频创建正确
下一步:用一系列桌面截图替换上面代码中的虚拟cv::Mat框架。我使用获得桌面窗口的句柄,然后使用函数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;
}
intmain(intargc,char*argv[])
{
cv::VideoWriter*VideoWriter=新cv::VideoWriter(
“C:/videos/desktop.avi”,
CV_FOURCC('D','I','V','3'),
5,尺寸(19201200),真实尺寸;
int frameCount=0;
而(帧数<100)
{
HWND hDsktopWindow=::GetDesktopWindow();
cv::Mat frame=hwnd2mat(hDsktopWindow);
录像机->写入(帧);
::睡眠(100);
frameCount++;
}
删除录像机;
返回0;
}
cv::Mat hwnd2mat(HWND HWND)
{
HDC hwindowDC,hwindowCompatibleDC;
int-height,width,srchheight,srchwidth;
HBITMAP窗口;
cv::Mat src;
BitMapInfo头bi;
hwindowDC=GetDC(hwnd);
hwindowCompatibleDC=CreateCompatibleDC(hwindowDC);
SetStretchBltMode(hwindowCompatibleDC,COLORONCOLOR);
RECT windowsize;//获取屏幕的高度和宽度
GetClientRect(hwnd和windowsize);
srchheight=windowsize.bottom;
srcwidth=windowsize.right;
height=windowsize.bottom/1;//将其更改为要调整大小的任意大小
宽度=windowsize.right/1;
src.create(高度、宽度、CV_8UC3);
//创建位图
hbwindow=CreateCompatibleBitmap(hwindowDC、宽度、高度);
bi.biSize=sizeof(BitMapInfo头);
bi.biWidth=宽度;
bi.biHeight=-height;//这是一条线,可以使它倒画,也可以不倒画
双平面=1;
bibibitcount=32;
bi.biCompression=bi_RGB;
bi.biSizeImage=0;
bi.biXPelsPerMeter=0;
bi.biYPelsPerMeter=0;
bi.BICLRUSE=0;
bi.biclr=0;
//将先前创建的设备上下文与位图一起使用
选择对象(hwindowCompatibleDC,hbwindow);
//从窗口设备上下文复制到位图设备上下文
StretchBlt(hwindowCompatibleDC,0,0,宽度,高度,hwindowDC,0,0,srcwidth,srcheight,SRCCOPY);
GetDIBits(hwindowCompatibleDC,hbwindow,0,高度,src.data,(BITMAPINFO*)和bi,DIB_RGB_颜色);
//避免内存泄漏
DeleteObject(hbwindow);DeleteDC(hwindowCompatibleDC);ReleaseDC(hwnd,hwindowDC);
返回src;
}
这样做的结果是创建了视频文件,可以毫无错误地播放,但它只是纯灰的。桌面的位图似乎没有正确复制到cv::Mat框架中。我在BitMapInfo标头中尝试了无数个值的组合,但没有任何效果,老实说,我不知道我在做什么。我知道opencv有转换函数,但我甚至不知道我要转换什么
感谢您的帮助 找到了一种方法让它发挥作用——我不知道这是不是最好的方法,所以仍然欢迎评论或其他解决方案 似乎为了工作,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返回dst,而不是src。对cvtColor的调用将删除alpha通道(RGBA中的A),而dst最终只使用R、G、B通道。您可以从GetDIBits获得没有alpha通道的位图,并将其直接写入cv::VideoWriter。把bibibitcount改成24。将Mat格式保留为CV_8IC3。这对我有用。 src.create(高度、宽度、CV_8UC3) bi.biBitCount=24;//这是改变的地方