2016-11-15 116 views
1

這個問題類似於this one,特別是this one但我期望的輸出是不同的。我試圖使用opencv捕獲桌面到視頻。首選的輸出是使用divx編碼的avi文件。一般來說,我對opencv和位圖編程是新手。使用opencv從幀創建一個divx編碼的AVI

作爲第一步,爲了確保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; 
} 

這完美的作品 - 創建視頻文件,可以在我贏得10機VLC,Windows媒體播放器或電影&電視應用上播放。它是純黃色的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有轉換功能,但我甚至不知道我想要轉換爲/來自哪裏。

任何幫助表示讚賞!

回答

1

想通了一個方法使其工作 - 我不知道這是否是最好的方式,所以仍然歡迎評論或替代解決方案。

看起來爲GetDIBits工作,CV ::墊具有之前我改變它是4信道,即CV_8UC4,像hwnd2mat的原代碼。如果不是CV_8UC4,則不復制數據(GetDIBits返回0個複製的掃描行),這就是爲什麼我的AVI只是灰色。因此,第一個變化是建立在src CV ::墊爲4通道:

src.create(height, width, CV_8UC4); 

但對於我試圖創建的DivX編碼的AVI文件,幀應該是3通道(唐別問我爲什麼)。我加入了OpenCV的轉換函數的調用調用的GetDIBits()後,如下:

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