Конвертировать 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;
}
Другие вопросы по тегам