【问题标题】:Create a divx-encoded avi from frames using opencv使用 opencv 从帧创建 divx 编码的 avi
【发布时间】:2016-11-15 19:42:00
【问题描述】:

这个问题类似于this one,尤其是this one,但我想要的输出不同。我正在尝试使用 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(取自this SO 问题 - 谢谢!)将从桌面句柄获得的位图转换为我可以编写的 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 有转换功能,但我什至不知道我要转换成/从什么。

任何帮助表示赞赏!

【问题讨论】:

    标签: c++ windows opencv bitblt


    【解决方案1】:

    想出了一种让它发挥作用的方法——我不知道这是否是最好的方法,所以仍然欢迎 cmets 或替代解决方案。

    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 通道。

    【讨论】:

      【解决方案2】:

      您可以从 GetDIBits 获取没有 alpha 通道的位图,并将其直接写入 cv::VideoWriter。只需将 biBitCount 更改为 24。将 Mat 格式保留为 CV_8IC3。这对我有用。 src.create(height, width, CV_8UC3);

      bi.biBitCount = 24; // 这是要改变的地方

      【讨论】:

        猜你喜欢
        • 2012-05-21
        • 2012-03-04
        • 2010-11-11
        • 2011-06-27
        • 1970-01-01
        • 2011-09-17
        • 2022-01-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多