C++ 使用opencv从帧创建divx编码的avi

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))

这个问题类似于,特别是我想要的结果不同。我正在尝试使用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;
}
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;//这是改变的地方