Создать DivX-кодированный AVI из кадров, используя OpenCV

Этот вопрос похож на этот и особенно этот но мой желаемый результат отличается. Я пытаюсь захватить рабочий стол для видео с помощью OpenCV. Предпочтительным выводом является AVI-файл с кодировкой DivX. Я новичок в 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&ТВ приложение. Это 100 кадров сплошного желтого цвета, но это показывает, что видео создается правильно.

Следующий шаг: замените фиктивную рамку cv :: Mat в приведенном выше коде серией скриншотов рабочего стола. Я получаю дескриптор к окну рабочего стола, используя GetDesktopWindow (), а затем используйте функцию hwnd2mat (взято из этот ТАК вопрос — спасибо!) Для преобразования растрового изображения, полученного из дескриптора рабочего стола, в cv :: Mat, который я могу записать в мое видео.

Я скопировал функцию hwnd2mat дословно, за исключением того, что я не масштабирую изображение — растровое изображение рабочего стола уже имеет размер 1920×1200, а также созданный 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 работать, резюме :: Mat имеет быть 4-канальным, то есть CV_8UC4, как оригинальный код hwnd2mat до того, как я его изменил. Если это не CV_8UC4, данные не копируются (GetDIBits возвращает 0 скопированных строк сканирования), и поэтому мой avi был просто серым. Итак, первым изменением было создание src cv :: Mat как 4-канального:

src.create(height, width, CV_8UC4);

Но для файла avi в кодировке DivX, который я пытаюсь создать, кадры должны быть 3-канальными (не спрашивайте меня, почему). Я добавил вызов функции преобразования opencv после вызова GetDIBits () следующим образом:

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

И тогда я возвращаю dst из hwnd2mat вместо src. Вызов cvtColor удаляет альфа-канал (A в RGBA), и dst заканчивается только каналами R, G, B.

1

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector