I am writing function to compress image using GDI+ on windows, and it's working well,
void ImageProcessorImpl::compressImpl(const std::string& path, int size, UInt8 quality)
{
HBITMAP hbmReturn = NULL;
Bitmap* bmPhoto = NULL;
std::wstring upath;
UnicodeConverter::toUTF16(path, upath);
// make source file close automatically, Bitmap detructor will be called
{
Bitmap image(upath.c_str());
int srcWidth = image.GetWidth();
int srcHeight = image.GetHeight();
float percent = 0;
int destX = 0, destY = 0;
if (srcWidth > srcHeight)
{
percent = ((float)size/(float)srcWidth);
destX = (int)((size - (srcWidth * percent))/2);
}
else
{
percent = ((float)size/(float)srcHeight);
destY = (int)((size - (srcHeight * percent))/2);
}
if (percent >= 1.0f)
return; // skip compress
int destWidth = (int)(srcWidth * percent);
int destHeight = (int)(srcHeight * percent);
bmPhoto = new Bitmap(destWidth, destHeight, PixelFormat24bppRGB);
bmPhoto->SetResolution(image.GetHorizontalResolution(), image.GetVerticalResolution());
Graphics *grPhoto = Graphics::FromImage(bmPhoto);
Color colorW(255, 255, 255, 255);
grPhoto->Clear(colorW);
grPhoto->SetInterpolationMode(InterpolationModeHighQualityBicubic);
grPhoto->DrawImage(&image, Rect(destX, destY, destWidth, destHeight));
bmPhoto->GetHBITMAP(colorW, &hbmReturn);
delete grPhoto;
} // end source image file, Bitmap image(upath.c_str());
// find appropriate encoder, jpeg
CLSID encoderClsid;
getEncoderClsid(L"image/jpeg", &encoderClsid);
// set output quality for jpeg alone
EncoderParameters encoderParameters;
setEncoderQuality(&encoderParameters, &quality);
// output to image file with desired quality
bmPhoto->Save(upath.c_str(), &encoderClsid, &encoderParameters);
// release resources
delete bmPhoto;
DeleteObject(hbmReturn);
}
int ImageProcessorImpl::getEncoderClsid(const WCHAR* format, void* clsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if(size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
{
*(CLSID*)clsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; //Success
}
}
free(pImageCodecInfo);
return -1; // Failure
}
void ImageProcessorImpl::setEncoderQuality(void* params, UInt8* quality)
{
EncoderParameters* encoderParams = (EncoderParameters*)params;
encoderParams->Count = 1;
encoderParams->Parameter[0].Guid = EncoderQuality;
encoderParams->Parameter[0].Type = EncoderParameterValueTypeLong;
encoderParams->Parameter[0].NumberOfValues = 1;
encoderParams->Parameter[0].Value = quality;
}
But, I want to have this function on linux, I don't know what lib I can use to implement such function on linux, who can help me?
Thnx
You can use the ImageMagick library or netpbm library. Netpbm also has command line tools to manipulate images.
OpenCV provides an easy to use interface for all types of image processing as well as simple things such as image compression. It may be a bit of an overkill for image compression only, but I suppose it may helpful if you plan on doing other things with these images.
Related
Is any way can we convert HICON or HCURSOR in to Byte array, I googled in all the way I didnt found a single generic solution, below I tried to convert HICON color and mask BITMAP to byte array and sending this through socket and creating my icon using CreateIconIndirect API but instead of doing all this stuff if I can able to send a HICON directly that will be good.
int ProcessMouse()
{
BYTE m_hbmMaskBits[70000];
BYTE m_hbmColorBits[70000];
CURSORINFO CursorInfo;
CursorInfo.cbSize = sizeof(CursorInfo);
GetCursorInfo(&CursorInfo);
ICONINFO iconInfo;
if (!GetIconInfo(CursorInfo.hCursor, &iconInfo))
{
MessageBox(NULL, _T("CreateCursor Failed"),_T("message"),MB_OK|MB_SYSTEMMODAL);
}
bool isColorShape = (iconInfo.hbmColor != NULL);
bool isMaskShape = (iconInfo.hbmMask != NULL);
LONG cbSize = 0; int nWidth = 0; int nHeight = 0; int actualHeight = 0; int bmPlanes = 0;
int bmBitsPixel = 0; int xHotspot = 0; int yHotspot = 0; int widthBytes = 0;
// Return width,height,actualheight,bmplanes,bmbitspixel,hotsopt of cursor.
if(!CopyIconInfo( CursorInfo.hCursor,
nWidth,
nHeight,
actualHeight,
bmPlanes,
bmBitsPixel,
xHotspot,
yHotspot,
widthBytes ))
{
return 0;
}
std::vector<BYTE> bColor;
std::vector<BYTE> bMask;
int sz_hbmColor = 0;
int sz_hbmMask = 0;
_tempWidth = nWidth;
_tempHeight = nHeight;
//If HCURSOR have both color and mask go with regular approach.
if(isColorShape)
{
//Convert iconInfo.hbmColor HBITMAP to Byte array.
bColor = HBIMAPtoBYTE(iconInfo.hbmColor,sz_hbmColor);
//Convert iconInfo.hbmMask HBITMAP to Byte array.
bMask = HBIMAPtoBYTE(iconInfo.hbmMask,sz_hbmMask);
}
// If HCURSOR have only mask data go with new approach(split mask bitmap to color and mask).
else if(isMaskShape)
{
std::vector<BYTE> bSrcBitmap;
int sz_hbmBitmap = 0;
//Convert iconInfo.hbmMask HBITMAP to Byte array.
bSrcBitmap = HBIMAPtoBYTE(iconInfo.hbmMask,sz_hbmBitmap);
sz_hbmColor = sz_hbmBitmap/2;
sz_hbmMask = sz_hbmBitmap/2;
bMask.resize(bMask.size() + sz_hbmBitmap/2);
memcpy(&bMask[bSrcBitmap.size() - sz_hbmBitmap], &bSrcBitmap[0], sz_hbmBitmap/2 * sizeof(BYTE));
bColor.resize(bColor.size() + sz_hbmBitmap/2);
memcpy(&bColor[bSrcBitmap.size() - sz_hbmBitmap], &bSrcBitmap[sz_hbmBitmap/2], sz_hbmBitmap/2 * sizeof(BYTE));
//Clear at end.
bSrcBitmap.clear();
}
try{
err = memcpy_s((m_hbmMaskBits), sz_hbmMask, &(bMask[0]), sz_hbmMask );
err = memcpy_s((m_hbmColorBits),sz_hbmColor,&(bColor[0]),sz_hbmColor);
//Clear at end.
bMask.clear();
bColor.clear();
return 1;
}catch(...) {
if(err) {
MessageBox(NULL, _T("memcopy failed at mask or color copy"),_T("message"),MB_OK|MB_SYSTEMMODAL);
}
}
}
I tried in below way but it doesn't support for few monochrome cursors.
PICTDESC pd = {sizeof(pd), PICTYPE_ICON};
pd.icon.hicon = CursorInfo.hCursor;
CComPtr<IPicture> pPict = NULL;
CComPtr<IStream> pStrm = NULL;
BOOL res = FALSE;
res = SUCCEEDED( ::CreateStreamOnHGlobal(NULL, TRUE, &pStrm) );
res = SUCCEEDED( ::OleCreatePictureIndirect(&pd, IID_IPicture, TRUE, (void**)&pPict) );
res = SUCCEEDED( pPict->SaveAsFile( pStrm, TRUE, &cbSize ) );
if( res )
{
// rewind stream to the beginning
LARGE_INTEGER li = {0};
pStrm->Seek(li, STREAM_SEEK_SET, NULL);
// write to file
DWORD dwWritten = 0, dwRead = 0, dwDone = 0;
while( dwDone < cbSize )
{
if( SUCCEEDED(pStrm->Read(bCursorBuff, sizeof(bCursorBuff), &dwRead)) )
{
dwDone += dwRead;
}
}
_ASSERTE(dwDone == cbSize);
}
//End of Cursor image
pStrm.Release();
pPict.Release();
HICON and HCURSOR are system handles, so they work only on the current machine.
Over network only the actual data can be sent (bitmap bytes). Then that machine can create its own handles for it.
Using the HBITMAP bytes is the correct approach. You can find some details here:
How to convert HICON to HBITMAP in VC++?
You can get the raw HBITMAP bits using GetDIBits(). More information: C++/Win32: How to get the alpha channel from an HBITMAP?
Below Code works only for color cursor for monochrome cursor use to
convert 16bpp bitmap to 32bpp bitmap and use same code its works.
bool saveToMemory(HICON hIcon, BYTE* buffer, DWORD& nSize)
{
if (hIcon == 0)
return FALSE;
int * pImageOffset;
int nNumIcons = 1;
nSize = 0;
// copy iconheader first of all
ICONHEADER iconheader;
// Setup the icon header
iconheader.idReserved = 0; // Must be 0
iconheader.idType = 1; // Type 1 = ICON (type 2 = CURSOR)
iconheader.idCount = nNumIcons; // number of ICONDIRs
// save to memory
memcpy(buffer, &iconheader, sizeof(iconheader));
nSize += sizeof(iconheader); // update
//
// Leave space for the IconDir entries
//
nSize += sizeof(ICONDIR);
pImageOffset = (int *)malloc(nNumIcons * sizeof(int));
ICONINFO iconInfo;
BITMAP bmpColor, bmpMask;
GetIconBitmapInfo(hIcon, &iconInfo, &bmpColor, &bmpMask);
// record the file-offset of the icon image for when we write the icon directories
pImageOffset[0] = nSize;
// bitmapinfoheader + colortable
//WriteIconImageHeader(hFile, &bmpColor, &bmpMask);
BITMAPINFOHEADER biHeader;
UINT nImageBytes;
// calculate how much space the COLOR and MASK bitmaps take
nImageBytes = NumBitmapBytes(&bmpColor) + NumBitmapBytes(&bmpMask);
// write the ICONIMAGE to disk (first the BITMAPINFOHEADER)
ZeroMemory(&biHeader, sizeof(biHeader));
// Fill in only those fields that are necessary
biHeader.biSize = sizeof(biHeader);
biHeader.biWidth = bmpColor.bmWidth;
biHeader.biHeight = bmpColor.bmHeight * 2; // height of color+mono
biHeader.biPlanes = bmpColor.bmPlanes;
biHeader.biBitCount = bmpColor.bmBitsPixel;
biHeader.biSizeImage = nImageBytes;
// write the BITMAPINFOHEADER
//WriteFile(hFile, &biHeader, sizeof(biHeader), &nWritten, 0);
memcpy(&buffer[nSize], &biHeader, sizeof(biHeader));
nSize += sizeof(biHeader);
// save color and mask bitmaps
saveIconData(buffer, nSize, iconInfo.hbmColor);
saveIconData(buffer, nSize, iconInfo.hbmMask);
DeleteObject(iconInfo.hbmColor);
DeleteObject(iconInfo.hbmMask);
//
// Lastly, save the icon directories.
//
DWORD size = saveIconDirectoryEntry(buffer, sizeof(ICONHEADER), pImageOffset[0], hIcon);
free(pImageOffset);
return TRUE;
}
//
// Return the number of BYTES the bitmap will take ON DISK
//
static UINT NumBitmapBytes(BITMAP *pBitmap)
{
int nWidthBytes = pBitmap->bmWidthBytes;
// bitmap scanlines MUST be a multiple of 4 bytes when stored
// inside a bitmap resource, so round up if necessary
if (nWidthBytes & 3)
nWidthBytes = (nWidthBytes + 4) & ~3;
return nWidthBytes * pBitmap->bmHeight;
}
// same as WriteIconData but save to memory
static UINT saveIconData(BYTE* buffer, DWORD& nSize, HBITMAP hBitmap)
{
BITMAP bmp;
int i;
BYTE * pIconData;
UINT nBitmapBytes;
DWORD nWritten = 0;
GetObject(hBitmap, sizeof(BITMAP), &bmp);
nBitmapBytes = NumBitmapBytes(&bmp);
pIconData = (BYTE *)malloc(nBitmapBytes);
GetBitmapBits(hBitmap, nBitmapBytes, pIconData);
// bitmaps are stored inverted (vertically) when on disk..
// so write out each line in turn, starting at the bottom + working
// towards the top of the bitmap. Also, the bitmaps are stored in packed
// in memory - scanlines are NOT 32bit aligned, just 1-after-the-other
for (i = bmp.bmHeight - 1; i >= 0; i--)
{
// Write the bitmap scanline
// save to memory
memcpy(&buffer[nSize], pIconData + (i * bmp.bmWidthBytes), bmp.bmWidthBytes);
nSize += bmp.bmWidthBytes;
nWritten += bmp.bmWidthBytes;
}
free(pIconData);
return nWritten;
}
//
// same as WriteIconDirectoryEntry but save to memory
//
static UINT saveIconDirectoryEntry(BYTE* buffer, DWORD pos, int imageOffset, HICON hIcon)
{
ICONINFO iconInfo;
ICONDIR iconDir;
BITMAP bmpColor;
BITMAP bmpMask;
DWORD nWritten = 0;
UINT nColorCount;
UINT nImageBytes;
GetIconBitmapInfo(hIcon, &iconInfo, &bmpColor, &bmpMask);
nImageBytes = NumBitmapBytes(&bmpColor) + NumBitmapBytes(&bmpMask);
if (bmpColor.bmBitsPixel >= 8)
nColorCount = 0;
else
nColorCount = 1 << (bmpColor.bmBitsPixel * bmpColor.bmPlanes);
// Create the ICONDIR structure
iconDir.bWidth = (BYTE)bmpColor.bmWidth;
iconDir.bHeight = (BYTE)bmpColor.bmHeight;
iconDir.bColorCount = nColorCount;
iconDir.bReserved = 0;
iconDir.wPlanes = bmpColor.bmPlanes;
iconDir.wBitCount = bmpColor.bmBitsPixel;
iconDir.dwBytesInRes = sizeof(BITMAPINFOHEADER) + nImageBytes;
iconDir.dwImageOffset = imageOffset;
// save to memory
memcpy(&buffer[pos], &iconDir, sizeof(iconDir));
nWritten += sizeof(iconDir);
// Free resources
DeleteObject(iconInfo.hbmColor);
DeleteObject(iconInfo.hbmMask);
return nWritten;
}
I was able to do so by calling GetDIBits() twice, once to get the actual details of the cursor images and another time to get the pixels.
You can apply this code for the color and mask, just be aware that it only returns 32x32px cursors, also only the first frame, even if the size is configured for something else.
var windowDeviceContext = User32.GetWindowDC(IntPtr.Zero);
//Initialize the bitmap header and calculate its size.
var maskHeader = new BitmapInfoHeader();
maskHeader.Size = (uint) Marshal.SizeOf(maskHeader);
//Gets the image details.
Gdi32.GetDIBits(windowDeviceContext, iconInfo.Mask, 0, 0, null, ref maskHeader, DibColorModes.RgbColors);
//If there's any data, get it.
if (maskHeader.Height != 0)
{
//To prevent the cursor image from being inverted.
maskHeader.Height *= -1;
var maskBuffer = new byte[maskHeader.SizeImage];
Gdi32.GetDIBits(windowDeviceContext, iconInfo.Mask, 0, (uint) maskHeader.Height, maskBuffer, ref maskHeader, DibColorModes.RgbColors);
}
It's C#, but easily converted to your language of choice.
I have a utility method which takes in unsigned char array as argument and converts it in to a *.png image
Here is the method:
#define MIME_TYPE_PNG L"image/png"
#define RED 253
#define GREEN 254
#define BLUE 255
#define MAX_GRAY 252
/// <summary>
/// Helper methods for dealing with images
/// </summary>
class ImageUtils
{
private:
static int GetEncoderClsid(const WCHAR* format, CLSID* pClsid);
static void Internal_SaveAsPNG(BITMAPINFO *gdiBitmapInfo, void *gdiBitmapData, char* imageFilePath);
public:
static void SaveAsPNG(int displayXSize, int displayYSize, char* imageFilePath, unsigned char* imageData);
};
/// <summary>
/// Writes an 8-bit PNG file at the given filePath using the imageData and imageDimensions
/// </summary>
void ImageUtils::SaveAsPNG(int displayXSize, int displayYSize, char* imageFilePath, unsigned char* imageData)
{
// Create the info header and add custom color table to it
const int COLORNUM = 256;
BITMAPINFOHEADER infoHeader;
infoHeader.biSize = sizeof(BITMAPINFOHEADER); // bytes in structure
infoHeader.biWidth = displayXSize; // width in pixels
infoHeader.biHeight = -displayYSize; // negative height indicates top-down DIB(Device Independent Bitmap)
infoHeader.biPlanes = 1; // must be 1
infoHeader.biBitCount = 8; // bits per pixel
infoHeader.biCompression = BI_RGB; // uncompressed RGB
infoHeader.biSizeImage = displayXSize * displayYSize; // size of image in bytes
infoHeader.biXPelsPerMeter = 2500; // pixels per meter X (probably exact number doesn't matter)
infoHeader.biYPelsPerMeter = 2500; // pixels per meter Y (probably exact number doesn't matter)
infoHeader.biClrUsed = COLORNUM; // number of color indices actually used by the bitmap
infoHeader.biClrImportant = 0; // all colors are important
BITMAPINFO *petBitmapInfo = (BITMAPINFO *)(new char[sizeof(BITMAPINFOHEADER) + (COLORNUM * sizeof(RGBQUAD))]);
petBitmapInfo->bmiHeader = infoHeader;
// First 253/256 colors are grays
for (int i = 0; i < COLORNUM-3; i++)
{
petBitmapInfo->bmiColors[i].rgbBlue = (BYTE)i;
petBitmapInfo->bmiColors[i].rgbGreen = (BYTE)i;
petBitmapInfo->bmiColors[i].rgbRed = (BYTE)i;
petBitmapInfo->bmiColors[i].rgbReserved = 0;
}
// Last three colors are pure red, green and blue respectively
petBitmapInfo->bmiColors[RED].rgbRed = 255;
petBitmapInfo->bmiColors[RED].rgbGreen = 0;
petBitmapInfo->bmiColors[RED].rgbBlue = 0;
petBitmapInfo->bmiColors[RED].rgbReserved = 0;
petBitmapInfo->bmiColors[GREEN].rgbRed = 0;
petBitmapInfo->bmiColors[GREEN].rgbGreen = 255;
petBitmapInfo->bmiColors[GREEN].rgbBlue = 0;
petBitmapInfo->bmiColors[GREEN].rgbReserved = 0;
petBitmapInfo->bmiColors[BLUE].rgbRed = 0;
petBitmapInfo->bmiColors[BLUE].rgbGreen = 0;
petBitmapInfo->bmiColors[BLUE].rgbBlue = 255;
petBitmapInfo->bmiColors[BLUE].rgbReserved = 0;
Internal_SaveAsPNG(petBitmapInfo, imageData, imageFilePath);
}
/// <summary>
/// Combines the BITMAPINFO and bitMapData into an image and saves it as PNG at the given location
/// </summary>
void ImageUtils::Internal_SaveAsPNG(BITMAPINFO *gdiBitmapInfo, void *gdiBitmapData, char* imageFilePath)
{
// Initialize GDI+.
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
// Get the CLSID of the PNG encoder.
CLSID encoderClsid;
int clsidResult = GetEncoderClsid(L"image/png", &encoderClsid);
// Convert filepath to wide char
size_t filePathSize = strlen(imageFilePath) + 1;
wchar_t * imageFilePath_w = new wchar_t[filePathSize];
size_t numCharsConverted = 0;
mbstowcs_s(&numCharsConverted, imageFilePath_w, filePathSize, imageFilePath, _TRUNCATE);
// Create a bitmap object from the given header and image data, and save it as png (Bitmap class handles the compression internally)
Gdiplus::Bitmap* image = new Gdiplus::Bitmap(gdiBitmapInfo, gdiBitmapData);
Gdiplus::Status stat = image->Save(imageFilePath_w, &encoderClsid, NULL);
delete image;
Gdiplus::GdiplusShutdown(gdiplusToken);
if (stat != Gdiplus::Ok)
{
string message = "Unable to save ";
message.append(imageFilePath);
throw new exception(message.c_str());
}
}
/// <summary>
/// Queries all the available image encoders and sets pClsid to the CLSID of the requested encoder
/// </summary>
int ImageUtils::GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT numEncoders = 0;
UINT sizeImageEncoder = 0;
// How many codecs are available and what's the total size?
Gdiplus::GetImageEncodersSize(&numEncoders, &sizeImageEncoder);
// Fail if no encoders are found
if (sizeImageEncoder == 0)
return -1;
// Information of all available codecs
Gdiplus::ImageCodecInfo* codecInfo = (Gdiplus::ImageCodecInfo*)(malloc(sizeImageEncoder));
if (codecInfo == NULL)
return -1;
Gdiplus::GetImageEncoders(numEncoders, sizeImageEncoder, codecInfo);
// Find out the CLSID of the requested encoder
for (UINT j = 0; j < numEncoders; ++j)
{
if (wcscmp(codecInfo[j].MimeType, format) == 0)
{
*pClsid = codecInfo[j].Clsid;
free(codecInfo);
return j;
}
}
free(codecInfo);
return -1;
}
Problem is that images which should look like this:
.. end up looking like this:
After some trial and error, I found out that any image with a width which is a
multiple of 4
will render correctly, whereas all others will be sheared, as is the case in the example above where the width is 50.
Can anyone shed light on this behavior and how to resolve it?
I have got a hard time to display jpg file at VIEW class by ImageMagicK in MFC.
The following code are my code. the problem is looking as following
image
void CTestview::DoDisplayImage(void)
{
CDC *pDC = GetDC();
if (fileposition != NULL)
AfxMessageBox(fileposition);
char m_szAppPath[255];
//m_szAppPath = (char) * fileposition;
(void)MagickCore::SetClientPath(fileposition);
InitializeMagick(fileposition);
MagickCore::RegisterStaticModules();
// Parse command line for standard shell commands, DDE, file open
Image m_Image;
m_Image.read(fileposition);
/*
char m_szAppPath[255];
(void) MagickCore::SetClientPath(m_szAppPath);
InitializeMagick(m_szAppPath);
MagickCore::RegisterStaticModules();
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
Image master;
master.read("D:\\work\\mfc_test5\\q1.jpg");
*/
if (pDC != NULL && m_Image.isValid())
{
CRect rectClient;
GetClientRect(rectClient);
// Clear the background
pDC->FillSolidRect(rectClient, pDC->GetBkColor());
// Set up the Windows bitmap header
BITMAPINFOHEADER bmi;
bmi.biSize = sizeof(BITMAPINFOHEADER); // Size of structure
bmi.biWidth = m_Image.columns(); // Bitmaps width in pixels
bmi.biHeight = (-1)*m_Image.rows(); // Bitmaps height n pixels
bmi.biPlanes = 1; // Number of planes in the image
bmi.biBitCount = 32; // The number of bits per pixel
bmi.biCompression = BI_RGB; // The type of compression used
bmi.biSizeImage = 0; // The size of the image in bytes
bmi.biXPelsPerMeter = 0; // Horizontal resolution
bmi.biYPelsPerMeter = 0; // Veritical resolution
bmi.biClrUsed = 0; // Number of colors actually used
bmi.biClrImportant = 0; // Colors most important
// Extract the pixels from Magick++ image object and convert to a DIB section
PixelPacket *pPixels = m_Image.getPixels(0, 0, m_Image.columns(), m_Image.rows());
RGBQUAD *prgbaDIB = 0;
HBITMAP hBitmap = CreateDIBSection
(
pDC->m_hDC, // handle to device context
(BITMAPINFO *)&bmi, // pointer to structure containing bitmap size, format, and color data
DIB_RGB_COLORS, // color data type indicator: RGB values or palette indices
(void**)&prgbaDIB, // pointer to variable to receive a pointer to the bitmap's bit values
NULL, // optional handle to a file mapping object
0 // offset to the bitmap bit values within the file mapping object
);
if (!hBitmap)
return;
unsigned long nPixels = m_Image.columns() * m_Image.rows();
RGBQUAD *pDestPixel = prgbaDIB;
// Transfer pixels, scaling to Quantum
/*
for( unsigned long nPixelCount = nPixels; nPixelCount ; nPixelCount-- )
{
pDestPixel->rgbRed = MagickCore::GetPixelRed(m_Image.constImage(),pPixels)/257;
pDestPixel->rgbGreen = MagickCore::GetPixelGreen(m_Image.constImage(),pPixels)/257;
pDestPixel->rgbBlue = MagickCore::GetPixelBlue(m_Image.constImage(),pPixels)/257;
pDestPixel->rgbReserved = 0;
++pDestPixel;
pPixels+=MagickCore::GetPixelChannels(m_Image.constImage());
}
*/
for (int i = 0; i < 256; i++)
{
pPixels->blue = (BYTE)i;
pPixels->green = (BYTE)i;
pPixels->red = (BYTE)i;
pPixels->opacity = 0;
pPixels++;
}
// Now copy the bitmap to device.
HDC hMemDC = CreateCompatibleDC(pDC->m_hDC);
SelectObject(hMemDC, hBitmap);
BitBlt(pDC->m_hDC, 0, 0, m_Image.columns(), m_Image.rows(), hMemDC, 0, 0, SRCCOPY);
DeleteObject(hMemDC);
}
}
Especially, at the following code does not sure.
What should I do for solving this problem?
for( int i = 0 ; i < 256 ; i++ )
{
pPixels->blue = (BYTE)i;
pPixels->green = (BYTE)i;
pPixels->red = (BYTE)i;
pPixels->opacity = 0;
pPixels++;
}
Would you please help me how do I do for solving this problem?
As you can see, the output image has black.
I thought displaying OpenCV2 Mat on MFC View is simple but is not. This is only relevant material I found on google. Excuse me for my ignorance but I can't find any other materials showing how to use SetDIBitsToDevice with one dimensional array "data" member returns. More specifically, I need to know how to specify BITMAPINFO for the function. Do I go back to Old C-style OpenCV to work with MFC?
UPDATE:
I found an example of SetDIBitsToDevice which is actually for old C-style OpenCV. But it was straightforward to convert it for OpenCV2. There are things I need to mention to make it work:
Bpp method does not work well as Mat's depth returns 0. I just changed like this:
static int Bpp(cv::Mat img) { return 8 * img.channels(); }
Mat does not have origin member. But simply putting 0 is fine for origin argument of FillBitmapInfo method.
Other than that, following code works great. Hope this helps other devs too.
void COpenCVTestView::OnDraw(CDC* pDC)
{
COpenCVTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
if(pDoc->m_cvImage.empty()) return;
// TODO: add draw code for native data here
int height=pDoc->m_cvImage.rows;
int width=pDoc->m_cvImage.cols;
uchar buffer[sizeof( BITMAPINFOHEADER ) + 1024];
BITMAPINFO* bmi = (BITMAPINFO* )buffer;
FillBitmapInfo(bmi,width,height,Bpp(pDoc->m_cvImage),0);
SetDIBitsToDevice(pDC->GetSafeHdc(), 0, 0, width,
height, 0, 0, 0, height, pDoc->m_cvImage.data, bmi,
DIB_RGB_COLORS);
}
void COpenCVTestView::FillBitmapInfo(BITMAPINFO* bmi, int width, int height, int bpp, int origin)
{
assert(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;
for (int i = 0; i < 256; i++)
{
palette[i].rgbBlue = palette[i].rgbGreen = palette[i].rgbRed = (BYTE)i;
palette[i].rgbReserved = 0;
}
}
}
Here is another possible way of displaying OpenCV data in MFC which I use and works great:
IplImage* image// <-- this contains the image you want to display
CvvImage tempdefault;
RECT myrect; // <-- specifiy where on the screen you want it to be displayed
myrect.top = 0;
myrect.bottom = _pictureh;
myrect.left = _picturex;
myrect.right = _picturew+_picturex;
tempdefault.Create(_pictureh,_picturew,32);
tempdefault.CopyOf(image);
tempdefault.DrawToHDC(pDC->GetSafeHdc(),&myrect);
From MSDN:
lpvBits [in]
A pointer to the color data stored as an array of bytes. For more information, see the following Remarks section.
This is the pointer you must init with the data returned from Mat::data.
CvvImage is not available in new versions of OpenCV. Using the following code you can convert Mat to CImage and then display CImage everywhere you want:
int Mat2CImage(Mat *mat, CImage &img){
if(!mat || mat->empty())
return -1;
int nBPP = mat->channels()*8;
img.Create(mat->cols, mat->rows, nBPP);
if(nBPP == 8)
{
static RGBQUAD pRGB[256];
for (int i = 0; i < 256; i++)
pRGB[i].rgbBlue = pRGB[i].rgbGreen = pRGB[i].rgbRed = i;
img.SetColorTable(0, 256, pRGB);
}
uchar* psrc = mat->data;
uchar* pdst = (uchar*) img.GetBits();
int imgPitch = img.GetPitch();
for(int y = 0; y < mat->rows; y++)
{
memcpy(pdst, psrc, mat->cols*mat->channels());//mat->step is incorrect for those images created by roi (sub-images!)
psrc += mat->step;
pdst += imgPitch;
}
return 0;
}
I've searched around using google but I'm completely confused on how to load an image (PNG in my case) from resource and then converting it to a bitmap in memory for use in my splash screen. I've read about GDI+ and libpng but I don't really know how to do what I want. Could anyone help?
GDI+ supports PNG directly. See here and here.
EDIT: The GDI+ documentation offers some advice for how to use GDI+ in a DLL. In your case, the best solution is probably to define initialisation and teardown functions that the client code is required to call.
I ended up using PicoPNG to convert the PNG to a two dimensional vector which I then manually contructed a bitmap from. My final code looked like this:
HBITMAP LoadPNGasBMP(const HMODULE hModule, const LPCTSTR lpPNGName)
{
/* First we need to get an pointer to the PNG */
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)
{
log_debug("Error while decoding png splash: %d", err);
return NULL;
}
/* Create the bitmap */
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);
ReleaseDC(NULL, screen);
/* 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
}
/* Done! */
return result;
}