CreateDIBSection ERROR_TAG_NOT_FOUND when transforming a PNG type resource into a cv::Mat

2

I'm currently using a modified version of the following code I found here to try and convert a .png resource in my project to a HBITMAP and then into a cv::Map.

cv::Mat Resource2mat(const HMODULE hModule, const LPCSTR lpPNGName) {
    cv::Mat src;
    HRSRC found = FindResource(hModule, lpPNGName, "PNG");
    unsigned int size = SizeofResource(hModule, found);
    HGLOBAL loaded = LoadResource(hModule, found);
    void* resource_data = LockResource(loaded);

    /* Now we decode the PNG */
    vector<unsigned char> raw;
    unsigned long width, height;
    int err = decodePNG(raw, width, height, (const unsigned char*)resource_data, size);
    if (err != 0)
    {
        cout<<"\nError while decoding png splash: "<< err <<endl;
        return src;
    }

    // copy from the window device context to the bitmap device context
    BITMAPV5HEADER bmpheader = { 0 };
    bmpheader.bV5Size = sizeof(BITMAPV5HEADER);
    bmpheader.bV5Width = width;
    bmpheader.bV5Height = height;
    bmpheader.bV5Planes = 1;
    bmpheader.bV5BitCount = 32;
    bmpheader.bV5Compression = BI_BITFIELDS;
    bmpheader.bV5SizeImage = width * height * 4;
    bmpheader.bV5RedMask = 0x00FF0000;
    bmpheader.bV5GreenMask = 0x0000FF00;
    bmpheader.bV5BlueMask = 0x000000FF;
    bmpheader.bV5AlphaMask = 0xFF000000;
    bmpheader.bV5CSType = LCS_WINDOWS_COLOR_SPACE;
    bmpheader.bV5Intent = LCS_GM_BUSINESS;
    void* converted = NULL;
    HDC screen = GetDC(NULL);
    HBITMAP result = CreateDIBSection(screen, reinterpret_cast<BITMAPINFO*>(&bmpheader), DIB_RGB_COLORS, &converted, NULL, 0);
    cout << "Error Final: " << GetLastError() << endl;

    /* Copy the decoded image into the bitmap in the correct order */
    for (unsigned int y1 = height - 1, y2 = 0; y2 < height; y1--, y2++)
        for (unsigned int x = 0; x < width; x++)
        {
            *((char*)converted + 0 + 4 * x + 4 * width*y2) = raw[2 + 4 * x + 4 * width*y1]; // Blue
            *((char*)converted + 1 + 4 * x + 4 * width*y2) = raw[1 + 4 * x + 4 * width*y1]; // Green
            *((char*)converted + 2 + 4 * x + 4 * width*y2) = raw[0 + 4 * x + 4 * width*y1]; // Red
            *((char*)converted + 3 + 4 * x + 4 * width*y2) = raw[3 + 4 * x + 4 * width*y1]; // Alpha
        }

    GetDIBits(screen, result, 0, height, src.data, (BITMAPINFO *)&bmpheader, DIB_RGB_COLORS);

    cv::Mat Actual = src.clone();

    ReleaseDC(NULL, screen);
    /* Done! */
    return Actual;
}

my .rc file looks like this:

Folder tree

and the resources.h entry looks like this

definition


When running the code and hitting this line, I end up with a 2012 ( ERROR_TAG_NOT_FOUND ) error

HBITMAP result = CreateDIBSection(screen, reinterpret_cast<BITMAPINFO*>(&bmpheader), DIB_RGB_COLORS, &converted, NULL, 0);

Found it by calling GetLastError() before and after this line of code


And this is how I call this function in my int main() :

HINSTANCE BotModuleHandle = GetModuleHandle(NULL);

cout << "Attempting to load a resource: " << endl;

cv::Mat S = Resource2mat(BotModuleHandle, MAKEINTRESOURCE(103));

Thanks in advance.

Also any suggestions for a better approach for converting a PNG resource into a cv::Mat are highly appreciated

c++
opencv
bitmap
png
asked on Stack Overflow Apr 23, 2018 by Official229 • edited Apr 23, 2018 by Official229

1 Answer

1

If all you really wish to achieve is to load the resource image into a cv::Mat, then you can do it with a much shorter function:

cv::Mat Resource2mat(const HMODULE hModule, const LPCSTR lpPNGName)
{
    HRSRC found = FindResource(hModule, lpPNGName, "PNG");
    unsigned int size = SizeofResource(hModule, found);
    HGLOBAL loaded = LoadResource(hModule, found);
    void* resource_data = LockResource(loaded);

    return cv::imdecode(cv::_InputArray(static_cast<uchar*>(resource_data), size)
        , cv::IMREAD_UNCHANGED);
}

LockResource gives you a pointer to a buffer (array of bytes) containing a PNG encoded image (as if you just read the contents of a PNG file into an array). SizeofResource gives you the size of this array in bytes.

OpenCV provides function cv::imdecode, which can decode PNG (and other formats) images from memory buffers. There's just a small issue -- we need to pass both the pointer as well as the size in just one parameter. To do this, we can explicitly construct a temporary cv::_InputArray.


Whole test program:

#include <windows.h>
#include "resource.h"

#include <opencv2/opencv.hpp>


cv::Mat Resource2mat(const HMODULE hModule, const LPCSTR lpPNGName)
{
    HRSRC found = FindResource(hModule, lpPNGName, "PNG");
    CV_Assert(found);
    unsigned int size = SizeofResource(hModule, found);
    CV_Assert(size);
    HGLOBAL loaded = LoadResource(hModule, found);
    CV_Assert(size);
    void* resource_data = LockResource(loaded);
    CV_Assert(resource_data);

    return cv::imdecode(cv::_InputArray(static_cast<uchar*>(resource_data), size)
        , cv::IMREAD_UNCHANGED);
}

int main()
{
    HINSTANCE hModule = GetModuleHandle(NULL);
    cv::Mat image(Resource2mat(hModule, MAKEINTRESOURCE(IDB_PNG1)));
    cv::imshow("Resource image", image);
    cv::waitKey();

    return 0;
}

Resource header resource.h:

#define IDB_PNG1                        101

Resource file pngres.rc:

#include "resource.h"
#include "winres.h"

LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
IDB_PNG1                PNG                     "resource.png"

Running this gives me a window with the image correctly displayed:

Program output

answered on Stack Overflow Apr 23, 2018 by Dan MaĊĦek

User contributions licensed under CC BY-SA 3.0