代码之家  ›  专栏  ›  技术社区  ›  user2048466

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

  •  1
  • user2048466  · 技术社区  · 9 年前

    这个问题类似于 this one 特别是 this one 但我想要的输出是不同的。我正在尝试使用opencv将桌面捕获为视频。首选输出是具有divx编码的avi文件。我是opencv和位图编程的新手。

    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或电影&电视应用程序。它是100帧纯黄色,但它显示视频正在正确创建。

    下一步:用一系列桌面截图替换上面代码中的虚拟cv::Mat框架。我使用获得桌面窗口的句柄 GetDesktopWindow() ,然后使用函数hwnd2mat(取自 this 所以这个问题-谢谢将从桌面句柄获得的位图转换为cv::Mat,以便写入视频。

    我逐字复制了hwnd2mat函数,只是我没有缩放图像-桌面位图已经是1920x1200,而且我创建的cv::Mat是cv_8UC3而不是cv_8 UC4(cv_9UC4导致我的应用程序崩溃)。

    以下是代码,包括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框架中。我在BitMapInfo标头中尝试了无数个值的组合,但没有任何效果,老实说,我不知道我在做什么。我知道opencv有转换功能,但我甚至不知道我要转换什么。

    2 回复  |  直到 9 年前
        1
  •  1
  •   user2048466    9 年前

    我不知道这是不是最好的方法,所以还是欢迎评论或其他解决方案。

    似乎是 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
  •  0
  •   unagipie    5 年前

    您可以从GetDIBits中获得没有alpha通道的位图,并将其直接写入cv::VideoWriter。只需将biBitCount更改为24。将Mat格式保留为CV_8IC3。这对我有效。 src。创建(高度、宽度、CV_8UC3);

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