C++ Gdi+将图像转换为灰度

C++ Gdi+ convert image to grayscale

本文关键字:灰度 转换 图像 Gdi+ C++      更新时间:2023-10-16

尝试将 32,24,16,8 位图像转换为其灰度演示文稿。我读过有关使用 BitBlt 的信息,但可能存在一些轻松的内置机会 在GDI+中?

法典:

#include <vector>
...
class gdiplus_init
{
ULONG_PTR token;
public:
gdiplus_init()
{
Gdiplus::GdiplusStartupInput tmp;
Gdiplus::GdiplusStartup(&token, &tmp, NULL);
}
~gdiplus_init()
{
Gdiplus::GdiplusShutdown(token);
}
};
bool getbits(const wchar_t *filename, Gdiplus::PixelFormat pixelformat, 
std::vector<BYTE> &bitmapinfo, std::vector<BYTE> &bits, int &w, int &h)
{
gdiplus_init init;
WORD bpp = 0;
int usage = DIB_RGB_COLORS;
int palettesize = 0;
switch(pixelformat)
{
case PixelFormat8bppIndexed: 
bpp = 8; 
usage = DIB_PAL_COLORS;  
palettesize = 256 * sizeof(RGBQUAD);
break;
case PixelFormat16bppRGB555: bpp = 16; break;
case PixelFormat16bppRGB565: bpp = 16; break;
case PixelFormat24bppRGB: bpp = 24; break;
case PixelFormat32bppRGB: bpp = 32; break;
default:return false;
}
auto src = Gdiplus::Bitmap::FromFile(filename);
if(src->GetLastStatus() != Gdiplus::Status::Ok)
return false;
auto dst = src->Clone(0, 0, src->GetWidth(), src->GetHeight(),
pixelformat);
w = src->GetWidth();
h = src->GetHeight();
HBITMAP hbitmap;
Gdiplus::Color color;
dst->GetHBITMAP(color, &hbitmap);
//allocate enough memory for bitmapinfo and initialize to zero
//it's sizeof BITMAPINFO structure + size of palette
bitmapinfo.resize(sizeof(BITMAPINFO) + palettesize, 0);
//fill the first 6 parameters
BITMAPINFO* ptr = (BITMAPINFO*)bitmapinfo.data();
ptr->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); //don't skip
ptr->bmiHeader.biWidth = w;
ptr->bmiHeader.biHeight = h;
ptr->bmiHeader.biPlanes = 1;
ptr->bmiHeader.biBitCount = bpp;
ptr->bmiHeader.biCompression = BI_RGB;
//magic formula to calculate the size:
//this is roughly w * h * bytes_per_pixel, it's written this way
//to account for "bitmap padding"
DWORD size = ((w * bpp + 31) / 32) * 4 * h;
//allocate memory for image
bits.resize(size, 0);
//finally call GetDIBits to fill bits and bitmapinfo
HDC hdc = GetDC(0);
GetDIBits(hdc, hbitmap, 0, h, &bits[0], (BITMAPINFO*)&bitmapinfo[0], usage);
ReleaseDC(0, hdc);
//cleanup
delete src;
delete dst;
return true;
}
void CMFCApplicationColorsView::OnDraw(CDC* pDC)
{
...
std::vector<BYTE> bi; //automatic storage
std::vector<BYTE> bits; 
int w, h;
//24-bit test
if(getbits(L"c:\test\24bit.bmp", PixelFormat24bppRGB, bi, bits, w, h))
StretchDIBits(dc, 0, 0, w, h, 0, 0, w, h, 
bits.data(), (BITMAPINFO*)bi.data(), DIB_RGB_COLORS, SRCCOPY);
//8-bit test
if(getbits(L"c:\test\8bit.bmp", PixelFormat8bppIndexed, bi, bits, w, h))
StretchDIBits(dc, 0, 220, w, h, 0, 0, w, h, 
bits.data(), (BITMAPINFO*)bi.data(), DIB_PAL_COLORS, SRCCOPY);
}

您可以使用各种转换直接绘制 GDI+。使用Gdiplus::Graphics在设备上下文上绘制。

对于灰度转换,RGB 值都必须相同。Gdiplus::ColorMatrix可以变换颜色。绿色通常更重要,它变得更重要。

void draw(CDC *pdc)
{
//this line should be in OnCreate or somewhere other than paint routine
Gdiplus::Bitmap source(L"file.jpg");
//gray scale conversion:
Gdiplus::ColorMatrix matrix =
{
.3f, .3f, .3f,   0,   0,
.6f, .6f, .6f,   0,   0,
.1f, .1f, .1f,   0,   0,
0,   0,   0,   1,   0,
0,   0,   0,   0,   1
};
Gdiplus::ImageAttributes attr;
attr.SetColorMatrix(&matrix,
Gdiplus::ColorMatrixFlagsDefault, Gdiplus::ColorAdjustTypeBitmap);
Gdiplus::Graphics gr(pdc->GetSafeHdc());
Gdiplus::REAL w = (Gdiplus::REAL)source.GetWidth();
Gdiplus::REAL h = (Gdiplus::REAL)source.GetHeight();
Gdiplus::RectF rect(0, 0, w, h);
gr.DrawImage(&source, rect, 0, 0, w, h, Gdiplus::UnitPixel, &attr);
}

请注意,我使用了灰度矩阵的粗略值。请参阅评论中提到的答案以获得更好的矩阵。

要转换文件,过程类似,只是使用Gdiplus::Graphics创建内存 dc 并保存。

int GetEncoderClsid(const WCHAR* format, CLSID* clsid)
{
int result = -1;
UINT num = 0;  // number of image encoders
UINT size = 0;  // size of the image encoder array in bytes
Gdiplus::GetImageEncodersSize(&num, &size);
if(size)
{
Gdiplus::ImageCodecInfo* codec = (Gdiplus::ImageCodecInfo*)(malloc(size));
GetImageEncoders(num, size, codec);
for(UINT j = 0; j < num; ++j)
if(wcscmp(codec[j].MimeType, format) == 0)
{
*clsid = codec[j].Clsid;
result = j;
}
free(codec);
}
return result;
}
bool convert_grayscale(const wchar_t *file_in, const wchar_t *file_out)
{
CStringW extension = PathFindExtensionW(file_out);
extension.Remove(L'.');
extension.MakeLower();
if(extension == L"jpg") extension = L"jpeg";
extension = L"image/" + extension;
CLSID clsid;
if(GetEncoderClsid(extension, &clsid) == -1)
return false;
Gdiplus::Bitmap source(file_in);
if(source.GetLastStatus() != Gdiplus::Status::Ok)
return false;
Gdiplus::REAL w = (Gdiplus::REAL)source.GetWidth();
Gdiplus::REAL h = (Gdiplus::REAL)source.GetHeight();
Gdiplus::RectF rect(0, 0, w, h);
Gdiplus::Bitmap copy((INT)w, (INT)h, source.GetPixelFormat());
Gdiplus::ColorMatrix matrix =
{
.3f, .3f, .3f,   0,   0,
.6f, .6f, .6f,   0,   0,
.1f, .1f, .1f,   0,   0,
0,   0,   0,   1,   0,
0,   0,   0,   0,   1
};
Gdiplus::ImageAttributes attr;
attr.SetColorMatrix(&matrix, 
Gdiplus::ColorMatrixFlagsDefault, Gdiplus::ColorAdjustTypeBitmap);
Gdiplus::Graphics gr(&copy);
gr.DrawImage(&source, rect, 0, 0, w, h, Gdiplus::UnitPixel, &attr);
auto st = copy.Save(file_out, &clsid);
return st == Gdiplus::Status::Ok;
}
...
convert_grayscale(L"source.jpg", L"destination.jpg");