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:
and the resources.h entry looks like this
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
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: