Re: Way to convert a cv::Mat object to a wxBitmap or wxImage

Rossano Paris <[email protected]>
Newsgroups gmane.comp.lib.wxwindows.general
Message-ID <[email protected]>
Hallo at all
I'd like to offer my way to achieve what I asked days ago ...

Here attached you can find a class useful to convert a cv::Mat object to a 
wxWidgets image object.
There are two different way to make the conversion, one based on opencv 
functions *(1)*, which is very slow and the faster one *(2),* which is 
based on this code: link to code 
<https://github.com/opencv/opencv/blob/master/modules/highgui/src/window_w32.cpp>

*(1)* static const wxImage cvMatToWxImage(const cv::Mat& image, 
wxBitmapType imgtype, std::vector<int> par);
*(2)* const wxImage wxhOpenCvTools::cvMatToWxImage(wxWindow* pWindow, const 
cv::Mat& image)

best regards
Rossano

On Sunday, 12 February 2023 at 14:27:43 UTC+1 Rossano Paris wrote:

> Thank you Eric for your tips
> I'll take a look at them
>
> On Thursday, 9 February 2023 at 14:02:50 UTC+1 Eric Jensen wrote:
>
>> Hello Rossano,
>>
>> Thursday, February 9, 2023, 12:57:38 PM, you wrote:
>>
>> RP> Hallo at all.
>>
>> RP> I came here to ask someone a more conveninet way to convert a cv::Mat 
>> image
>> RP> to BMP.
>> RP> Expecially talking about loading such image from memory.
>>
>> Code:
>> https://github.com/PBfordev/wxopencvtest/blob/main/convertmattowxbmp.cpp
>>
>> Related forum post:
>> https://forums.wxwidgets.org/viewtopic.php?f=20&t=47603
>>
>> Documentation for bitmap raw pixel access:
>> https://docs.wxwidgets.org/trunk/classwx_pixel_data.html
>>
>> Credit to PB (pbfordev)
>>
>> Hth
>> Eric
>>
>>
>> -- 
>>
>>

-- 
Please read https://www.wxwidgets.org/support/mlhowto.htm before posting.
--- 
You received this message because you are subscribed to the Google Groups "wx-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To view this discussion on the web visit https://groups.google.com/d/msgid/wx-users/16d19d15-7243-49b8-8a7c-7934da1ff1d5n%40googlegroups.com.
wxhOpenCvTools.h (text/x-chdr, 1.3 KB)
/*******************************************************************************
*
*	Company name:	HOBBIT di Paris Rossano
*	File name:		...
*	Creation Date:	...
*	Creator:		Rossano Paris
*	Description:	...
*
********************************************************************************/

#ifndef _WXH_OPENCV_UTILITY_H_
#define _WXH_OPENCV_UTILITY_H_

//
//Header files
//
#if defined(_WIN32)
	#include <wx/msw/dib.h>
#endif
#include <vector>
#include <opencv2/core/mat.hpp>
#include <wx/image.h>
#include <wx/object.h>

class wxhOpenCvTools : public wxObject
{
	wxDECLARE_CLASS(wxhOpenCvTools);

public:
	wxhOpenCvTools();
	~wxhOpenCvTools();
	
	// Functions
	static const wxImage cvMatToWxImage(wxWindow* pWindow, const cv::Mat& image);
	static const wxImage cvMatToWxImage(const cv::Mat& image, wxBitmapType imgtype = wxBITMAP_TYPE_BMP, std::vector<int> par = std::vector<int>(0));

private:

	// Functions
	void Init();

	#if defined(_WIN32)
		static const wxImage mswMatToWxImage(wxWindow* pWindow, const cv::Mat& image);
		static bool mswGetBitmapInformation(HDC dc, int& channels);
		static bool mswFillBitmapInfo(BITMAPINFO* bmi, int width, int height, int bpp, int origin);
		static bool mswConvertImageToShow(const cv::Mat& src, cv::Mat& dst, bool toRGB);
	#endif
};

#endif //_WXH_OPENCV_UTILITY_H_
wxhOpenCvTools.cpp (text/x-c++src, 6.1 KB)
/*******************************************************************************
*
*	Company name:	HOBBIT di Paris Rossano
*	File name:		...
*	Creation Date:	...
*	Creator:		Rossano Paris
*	Description:	...
*
********************************************************************************/

// For compilers that support precompilation, includes "wx/wx.h".
#include <wx/wxprec.h>

#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif

/*
	Convertion from cv::Mat to wxImage has been possible thanks to this opencv file:
	https://github.com/opencv/opencv/blob/master/modules/highgui/src/window_w32.cpp
	
	wxhOpenCvTools::cvMatToWxImage(wxWindow* pWindow, const cv::Mat& image)
	is based on such code
*/

//
//Header files
//
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/core.hpp>
#include <wx/mstream.h>
#include "wxhOpenCvTools.h"

wxIMPLEMENT_CLASS(wxhOpenCvTools, wxObject );

//Distruttore
wxhOpenCvTools::~wxhOpenCvTools()
{
}

//Costruttore
wxhOpenCvTools::wxhOpenCvTools() : wxObject()
{
	Init();
}

void wxhOpenCvTools::Init()
{
}

/*
	std::vector<int> par
	example:

	vector<int> par;
	par.push_back(IMWRITE_JPEG_QUALITY);
	par.push_back(100);

	see:
	https://docs.opencv.org/3.4/d4/da8/group__imgcodecs.html#ga288b8b3da0892bd651fce07b3bbd3a56
*/

// Slower
const wxImage wxhOpenCvTools::cvMatToWxImage(const cv::Mat& image, wxBitmapType imgtype, std::vector<int> par)
{
	std::vector<unsigned char> imgbuffer;
	wxString fmt = wxEmptyString;
	wxImage wximg = wxNullImage;

	if (!image.empty()) {
		switch (imgtype) {
			case wxBITMAP_TYPE_BMP:
				fmt = ".bmp";
				break;
			case wxBITMAP_TYPE_PNG:
				fmt = ".png";
				break;
			case wxBITMAP_TYPE_JPEG:
				fmt = ".jpg";
				break;
			case wxBITMAP_TYPE_TIF:
				fmt = ".tif";
				break;
			case wxBITMAP_TYPE_PNM:
				fmt = ".pnm";
				break;				
			default:
				imgtype = wxBITMAP_TYPE_INVALID;
				break;
		}

		// Supported type
		if (imgtype != wxBITMAP_TYPE_INVALID) {
			// Convert to vector
			cv::imencode(fmt.ToStdString(), image, imgbuffer, par);

			// Valid buffer
			if (imgbuffer.size() > 0) {
				wxMemoryInputStream stream(imgbuffer.data(), imgbuffer.size());
				if (!wximg.LoadFile(stream, imgtype)) wximg = wxNullImage;
			}
		}
	}

	return wximg;
}

// Faster, but BMP only
const wxImage wxhOpenCvTools::cvMatToWxImage(wxWindow* pWindow, const cv::Mat& image)
{
	wxImage wximg = wxNullImage;

	if (!image.empty()) {
		#if defined(_WIN32)
			wximg = mswMatToWxImage(pWindow, image);
		#endif
	}

	return wximg;
}

#if defined(_WIN32)

const wxImage wxhOpenCvTools::mswMatToWxImage(wxWindow* pWindow, const cv::Mat& image)
{
	wxImage wximg = wxNullImage;
	const int channels = 3;
	int bmpchannels = 0;
	HDC dc = nullptr;

	// retrieves a handle to a device context (DC) for the client area of a specified window 
	if (pWindow) dc = GetDC(pWindow->GetHWND());	// dc from a window
	else dc = GetDC(nullptr);						// dc from screen

	// Valid dc
	if (dc != nullptr) {
		// Valid bitmap information
		if (mswGetBitmapInformation(dc, bmpchannels)) {

			// Get cv image to store converted image
			void* pDst = calloc(image.cols * image.rows * bmpchannels, 1);
			cv::Mat dst(image.rows, image.cols, CV_8UC3, pDst, (image.cols * channels + 3) & -4);

			// Image conversion
			if (mswConvertImageToShow(image, dst, false)) {
				uchar buffer[sizeof(BITMAPINFO) + 255 * sizeof(RGBQUAD)];
				BITMAPINFO* binfo = (BITMAPINFO*)buffer;

				// Valid bitmap info
				if (mswFillBitmapInfo(binfo, image.cols, image.rows, channels * 8, 0)) {
					// Create bitmap
					HBITMAP bmp = CreateDIBitmap(dc,
												 &binfo->bmiHeader,
												 CBM_INIT,
												 dst.data,
												 binfo,
												 DIB_RGB_COLORS);
					// Valid bitmap
					if (bmp != nullptr) {
						// Convert to wxImage
						wximg = wxDIB(bmp).ConvertToImage(wxDIB::Convert_AlphaAuto);

						// Delete object
						DeleteObject(bmp);
					}
				}
			}

			// Free resources
			std::free(pDst);
		}

		// Release DC
		if (pWindow) ReleaseDC(pWindow->GetHWND(), dc);
		else ReleaseDC(nullptr, dc);
	}

	return wximg;
}

bool wxhOpenCvTools::mswGetBitmapInformation(HDC dc, int& channels)
{
	bool vr = false;

	// flushes the calling thread's current batch.
	GdiFlush();

	// Retrieves a handle to an object of the specified type that has been selected into the specified device context (DC).
	HGDIOBJ h = GetCurrentObject(dc, OBJ_BITMAP);

	// Valid object
	if (h != nullptr) {
		BITMAP bmp = {};

		// Retrieve object attributes
		if (GetObject(h, sizeof(bmp), &bmp) != 0) {
			channels = bmp.bmBitsPixel / 8;
			vr = true;
		}
	}

	return vr;
}

bool wxhOpenCvTools::mswFillBitmapInfo(BITMAPINFO* bmi, int width, int height, int bpp, int origin)
{
	bool vr = false;

	// Valid parameters
	if (bmi && width >= 0 && height >= 0 && (bpp == 8 || bpp == 24 || bpp == 32)) {
		BITMAPINFOHEADER* bmih = &(bmi->bmiHeader);

		memset(bmih, 0, sizeof(*bmih));
		bmih->biSize = sizeof(BITMAPINFOHEADER);
		bmih->biWidth = width;
		bmih->biHeight = origin ? abs(height) : -abs(height);
		bmih->biPlanes = 1;
		bmih->biBitCount = (unsigned short)bpp;
		bmih->biCompression = BI_RGB;

		if (bpp == 8) {
			RGBQUAD* palette = bmi->bmiColors;
			int i = 0;

			for (i = 0; i < 256; i++) {
				palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed = (BYTE)i;
				palette[i].rgbReserved = 0;
			}
		}

		// Return value
		vr = true;
	}

	return vr;
}

bool wxhOpenCvTools::mswConvertImageToShow(const cv::Mat& src, cv::Mat& dst, bool toRGB)
{
	int src_depth = src.depth();
	bool vr = false;

	// Valid depth
	if (src_depth != CV_16F && src_depth != CV_32S) {
		cv::Mat tmp;

		switch (src_depth) {
			case CV_8U:
				tmp = src;
				break;
			case CV_8S:
				cv::convertScaleAbs(src, tmp, 1, 127);
				break;
			case CV_16S:
				cv::convertScaleAbs(src, tmp, 1 / 255., 127);
				break;
			case CV_16U:
				cv::convertScaleAbs(src, tmp, 1 / 255.);
				break;
			case CV_32F:
			case CV_64F: // assuming image has values in range [0, 1)
				src.convertTo(tmp, CV_8U, 255., 0.);
				break;
		}

		// Converts an input image from one color space to another
		cv::cvtColor(tmp, dst, toRGB ? cv::COLOR_BGR2RGB : cv::COLOR_BGRA2BGR, dst.channels());

		// Return value
		vr = true;
	}

	return vr;
}

#endif
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.