opencv

2016-11-15 11 views
1

を使用してフレームからdivxエンコードされたAVIを作成するこの質問はthis oneと似ていますが、特にthis oneと似ていますが、希望する出力が異なります。私はopencvを使ってビデオをデスクトップにキャプチャしようとしています。好ましい出力は、divxエンコーディングのaviファイルです。私はopencvとビットマッププログラミングには一般的に新しいです。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; 
} 

これは完璧に動作します - ビデオファイルが作成され、VLC、WindowsのMedia Playerや映画&テレビアプリと私の10勝のマシン上で再生することができます。これは100フレームの黄色ですが、ビデオが正しく作成されていることを示しています。

次の手順:上記のコードのダミーcv :: Matフレームをデスクトップの一連のスクリーンショットで置き換えます。私はGetDesktopWindow()を使用してデスクトップウィンドウへのハンドルを取得し、hwnd2mat(this SOから質問しました - ありがと!)を使用して、デスクトップハンドルから取得したビットマップを自分のビデオに書き込むことができるcv :: Matに変換します。

デスクトップのビットマップはすでに1920x1200であり、CV_8UC4ではなくCV_8UC3がCV_8UC3で作成されています(CV_8UC4がクラッシュするようになりました)以外は、hwnd2mat関数をそのままコピーしました。

はここ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

これを機能させる方法を見つけました - これが最良の方法であるかどうかはわかりませんので、コメントや代替ソリューションも歓迎します。

CVを動作させるためのGetDIBitsように思える::私はそれを変更する前に、マットはhwnd2matの元のコードのように、4チャネル、すなわちCV_8UC4ことがを有しています。 CV_8UC4でない場合、データはコピーされません(GetDIBitsは0スキャンラインを返します)。そのため、私のaviはグレーだけです。だから、最初の変更は、4チャネルとしてのsrc CV ::マットを作成することでした。

src.create(height, width, CV_8UC4); 

しかし、私が作成しようとしているのDivXエンコードされたAVIファイルのために、フレームは3チャンネルでなければなりません(ドンなぜ私に尋ねないのですか?)

cv::Mat dst; 
dst.create(height, width, CV_8UC3); 
cv::cvtColor(src, dst, CV_RGBA2RGB); 

そして私はhwnd2matの代わりに、SRCからDSTを返す次のように私は、()GetDIBitsを呼び出した後OpenCVの変換関数の呼び出しを追加しました。 cvtColorへの呼び出しはアルファチャンネル(RGBAのA)を削除し、dstはR、G、Bチャンネルのみで終了します。