Конвертировать HBITMAP в cv::Mat
Кажется, я не могу понять, как это сделать, я искал в Google и нашел два примера кода, один из библиотеки захвата экрана github и другой в группе сообщений, и ни один из них, похоже, не работает.
У меня есть структура:
struct ClacksScreen
{
HWND hDesktopWnd;
int width, height;
RECT wr, cr;
HDC hdcClacksScreen; // hardware ClacksScreen
HDC hdcMemDC; // ClacksScreen in memory
HBITMAP hbmClacksScreen; //hbitmap of the ClacksScreen
BITMAP bmpClacksScreen;
BITMAPINFOHEADER bi;
};
Это обновлено. У меня определены некоторые функции, в том числе та, которая записывает растровое изображение на диск, все работает нормально, снимок экрана и запись bmp на диск, и это именно то, что я хотел.
Теперь я хочу преобразовать HBITMAP, который я беру с экрана, прямо в cv::Mat для OpenCV2.1.
Это отчасти работает, за исключением того, что изображение чисто серое и оно вылетает. Очевидно, я все еще довольно n00b, когда дело доходит до C++, так что, вероятно, есть кое-что простое, что я просто не ворчу.
static cv::Mat copyToCVMat(const ClacksScreen * s)
{
cv::Mat image;
image.create(s->bmpClacksScreen.bmWidth, s->bmpClacksScreen.bmHeight, CV_8UC4);
GetDIBits(s->hdcMemDC, s->hbmClacksScreen, 0,
(UINT)s->bmpClacksScreen.bmHeight,
image.data,
(BITMAPINFO *)&s->bi, DIB_RGB_COLORS);
return image;
}
Когда я заверну cv::imwrite(изображение); в попытке поймать я получаю ошибку распределения. Очевидно, что в этот момент мы установили, что я понятия не имею, как это сделать, поэтому любая помощь будет принята с благодарностью.
ОБНОВИТЬ
Если я запускаю этот код:
try {
cv::Mat screen = cv::imread("captureqwsx.jpg");
if (!screen.data) {
printf("no image data?");
}
cv::imwrite("out.jpg",screen);
} catch(std::exception e) {
printf("Exception %s\n",e.what());
}
Я получаю вывод:
нет данных изображения? Исключение плохое распределение
Когда я пытаюсь запустить высокий графический интерфейс, он остается таким же, как и раньше, возникает проблема как для файлов.jpg, так и.bmps, записанных на диск, которые можно просматривать в средстве просмотра изображений и в MS Paint.
Я попробовал с совершенно другим изображением,.png с сайта, та же проблема.
Так что же я делаю не так в этот момент?
4 ответа
Из документации OpenCV
data - указатель на данные пользователя. Матричные конструкторы, которые принимают данные и параметры шага, не выделяют матричные данные. Вместо этого они просто инициализируют заголовок матрицы, который указывает на указанные данные, т.е. никакие данные не копируются. Эта операция очень эффективна и может использоваться для обработки внешних данных с использованием функций OpenCV. Внешние данные не освобождаются автоматически, пользователь должен позаботиться об этом.
Я думаю, что в этом суть, использование этого конструктора не копирует буфер, который вы передаете конструктору, поэтому вы не должны освобождать эти данные до тех пор, пока вам больше не понадобится cv::Mat. Кроме того, я почти ничего не знаю об этом материале, но почему вы передаете BITMAPINFOHEADER и BITMAPFILEHEADER своему объекту cv:: Mat, что не кажется правильным вообще.
Я написал свою собственную функцию для создания чего-то похожего, надеюсь, она поможет вам: Как захватить рабочий стол в OpenCV (т.е. превратить растровое изображение в мат)?
Я не знаю, что это такое, но в ту минуту, когда я задаю вопрос, я одержим поиском ответа. Во всяком случае, часть этой проблемы на самом деле решается путем ответа на этот вопрос:
API OpenCV 2.0 C++ с использованием imshow: возвращает необработанное исключение и "плохой флаг"
В Visual C++:
Перейдите в Project->Properties (или Alt-F7) Свойства конфигурации->Linker->Input-> Дополнительные зависимости
замените обычный cv210.lib cxcore210.lib highgui210.lib на cv210d.lib cxcore210d.lib highgui210d.lib - библиотеки отладки.
highgui по-прежнему показывает серый и не работает, но чтение из HBITMAP с использованием вышеуказанного метода теперь работает. и я действительно не нуждаюсь в highgui, это было только для тестирования в любом случае.
Этот код даже надежнее той же библиотеки OpenCV. В OpenCV есть специальная функция для загрузки HBITMAPS, но у нее есть некоторые проблемы, например, загрузка изображения в обратном направлении. Я предпочитаю контролировать свой код.
cv::Mat LoadHBITMAPOpenCV(HBITMAP& HBitmap, Gdiplus::Rect rect = Gdiplus::Rect())
{
// Create a Bitmap object from the HBITMAP, avoid memory leaks
std::unique_ptr<Bitmap> bitmap(Bitmap::FromHBITMAP(HBitmap, NULL));
if (!bitmap)
{
// Error loading the HBITMAP
throw std::runtime_error("[LoadHBITMAPOpenCV] Error loading HBITMAP");
}
// Get the bitmap dimensions
int bitmapWidth = bitmap->GetWidth();
int bitmapHeight = bitmap->GetHeight();
// Check if the bitmap is valid
if (bitmapWidth <= 0 || bitmapHeight <= 0)
{
throw std::runtime_error("[LoadHBITMAPOpenCV] Corrupt HBITMAP");
}
int rectWidth = bitmapWidth;
int rectHeight = bitmapHeight;
// Check if a rect is provided, otherwise use the entire image as the rect
if (rect.IsEmptyArea())
{
rect = Gdiplus::Rect(0, 0, bitmapWidth, bitmapHeight);
}
else
{
// Check if the cropping rectangle is valid
rectWidth = rect.Width - rect.X;
rectHeight = rect.Height - rect.Y;
if (rectWidth <= 0 || rectHeight <= 0 || rect.X < 0 || rect.Y < 0 || rect.Width > bitmapWidth || rect.Height > bitmapHeight)
{
throw std::runtime_error("[LoadHBITMAPOpenCV] Invalid rect");
}
}
// Load the HBITMAP into cv::Mat using GDI+
cv::Mat mat(rectHeight, rectWidth, CV_8UC4);
BitmapData bitmapData;
Rect rect_cut(rect.X, rect.Y, rectWidth, rectHeight);
bitmap->LockBits(&rect_cut, ImageLockModeRead, PixelFormat32bppARGB, &bitmapData);
if (bitmapData.Stride < 0)
{
bitmap->UnlockBits(&bitmapData);
throw std::runtime_error("[LoadHBITMAPOpenCV] Corrupt HBITMAP stride");
}
BYTE* data = reinterpret_cast<BYTE*>(bitmapData.Scan0);
memcpy(mat.data, data, mat.total() * mat.elemSize());
bitmap->UnlockBits(&bitmapData);
cv::cvtColor(mat, mat, EXTERNAL_CTV_COLOR);
// cv::cvtColor(mat, mat, cv::COLOR_RGB2Lab);
return mat;
}