写了个程序,想要把24位真彩色图像转换成灰度图像,但转换后的图像很黑,不知道为啥,用VC++6.0写的,主要代码如下:void CBmpdisView::OnContogray()
{
// TODO: Add your command handler code here
if(hDIB == NULL)
return;
LPSTR pDIB = (LPSTR)GlobalLock(hDIB);
LPSTR lpbits = pDIB + sizeof(BITMAPINFOHEADER);//lpbits指向位图数据 //获得图像信息
LPBITMAPINFOHEADER lpbmi = (LPBITMAPINFOHEADER)pDIB;
int nwidth = lpbmi->biWidth;
int nheigth = lpbmi->biHeight;
//将24位真彩色图像改为灰度位图
double Intensity = 0;
int nGray = 0;
for(int y = 0; y < nheigth; y++){
for(int x = 0; x < nwidth; x++){
nGray = y*nwidth*3+x*3;
Intensity = ( lpbits[nGray] + lpbits[nGray+1] + lpbits[nGray+2] ) / 3;
if(Intensity > 255) Intensity = 255;
if(Intensity < 0) Intensity = 0; lpbits[nGray] = Intensity;
lpbits[nGray+1] = Intensity;
lpbits[nGray+2] = Intensity; }
} GlobalUnlock(hDIB);
Invalidate(true);//指示背景区域无效,开始重绘
}大家帮忙看看是哪里的毛病?
{
// TODO: Add your command handler code here
if(hDIB == NULL)
return;
LPSTR pDIB = (LPSTR)GlobalLock(hDIB);
LPSTR lpbits = pDIB + sizeof(BITMAPINFOHEADER);//lpbits指向位图数据 //获得图像信息
LPBITMAPINFOHEADER lpbmi = (LPBITMAPINFOHEADER)pDIB;
int nwidth = lpbmi->biWidth;
int nheigth = lpbmi->biHeight;
//将24位真彩色图像改为灰度位图
double Intensity = 0;
int nGray = 0;
for(int y = 0; y < nheigth; y++){
for(int x = 0; x < nwidth; x++){
nGray = y*nwidth*3+x*3;
Intensity = ( lpbits[nGray] + lpbits[nGray+1] + lpbits[nGray+2] ) / 3;
if(Intensity > 255) Intensity = 255;
if(Intensity < 0) Intensity = 0; lpbits[nGray] = Intensity;
lpbits[nGray+1] = Intensity;
lpbits[nGray+2] = Intensity; }
} GlobalUnlock(hDIB);
Invalidate(true);//指示背景区域无效,开始重绘
}大家帮忙看看是哪里的毛病?
r = g = b = L = 0.299R + 0.587G + 0.114B
也可以简单地用算术平均来进行灰化:
r = g = b = L = (R + G + B) / 3
btw:
LPBITMAPINFOHEADER lpbmi = (LPBITMAPINFOHEADER)pDIB;
int nwidth = lpbmi->biWidth;
int nheigth = lpbmi->biHeight; 没有鉴定biBitCount..假如是32bit的呢?那肯定全错了..
LPSTR lpbits用LPBYTE lpbits试试
因为位图中的数据是0~255的, 你用的数据类型可能是char型的, 取不到128以上的, 所以出现变黑的情况, 其实可以到Debug下调试看看问题出现在哪里哈
char是有符号的,-127~128
观察了下你的灰度图跟原图,最亮的地方(接近255)变成黑了,很明显就是数值溢出了。变负数了。另外~~~变灰度你还要考虑色彩抖动。
哥前两天用gdi+写了一个,给你参考参考#include <windows.h>
#include <GdiPlus.h>
#pragma comment(lib, "gdiplus.lib")
using namespace Gdiplus;GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;const BYTE BayerPattern[8][8] =
{
{0,32,8,40,2,34,10,42},
{48,16,56,24,50,18,58,26},
{12,44,4,36,14,46,6,38},
{60,28,52,20,62,30,54,22},
{3,35,11,43,1,33,9,41},
{51,19,59,27,49,17,57,25},
{15,47,7,39,13,45,5,37},
{63,31,55,23,61,29,53,21}
};int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
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 )
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
} free(pImageCodecInfo);
return -1; // Failure
}void BayerDither( IN Bitmap* input )
{
UINT width = input->GetWidth();
UINT height = input->GetHeight(); Bitmap copy(width, height);
Graphics g(©);
g.DrawImage(input, 0, 0); BitmapData bitmapData;
Rect rect(0, 0, width, height); copy.LockBits(
&rect,
ImageLockModeWrite,
PixelFormat32bppARGB,
&bitmapData); UINT* pixels;
pixels = (UINT*)bitmapData.Scan0;
for(UINT y = 0; y < height; ++y)
{
for(UINT x = 0; x < width; ++x)
{
UINT index = y*bitmapData.Stride/4+x;
if((pixels[index]&0x00ffffff)/0x40000 < BayerPattern[y%8][x%8])
{
pixels[index] = 0xff000000;
}
else
{
pixels[index] = 0xffffffff;
}
//if(y%8 == 0 || x%8 == 0){ pixels[index] = 0x22ff0000; }
}
} copy.UnlockBits(&bitmapData); CLSID encoder;
if(GetEncoderClsid(L"image/jpeg", &encoder)==-1)
{
__asm int 3
}
copy.Save(L"BayerDither.jpg", &encoder);
system( "BayerDither.jpg" );
}void ErrorDither( IN Bitmap* input )
{
UINT width = input->GetWidth();
UINT height = input->GetHeight(); Bitmap copy(width, height);
Graphics g(©);
g.DrawImage(input, 0, 0); BitmapData bitmapData;
Rect rect(0, 0, width, height); copy.LockBits(
&rect,
ImageLockModeWrite,
PixelFormat32bppARGB,
&bitmapData); UINT* pixels;
pixels = (UINT*)bitmapData.Scan0; int error = 0;
const UINT median = 0xffffff/2+0xff000000; for(UINT y = 0; y < height; ++y)
{
for(UINT x = 0; x < width; ++x)
{
UINT index = y*bitmapData.Stride/4+x;
if(pixels[index] >= median)
{
error = (pixels[index]&0x00ffffff) - 0x00ffffff;
pixels[index] = 0xffffffff;
}
else
{
error = (pixels[index]&0x00ffffff) - 0x00000000;
pixels[index] = 0xff000000;
}
pixels[y*bitmapData.Stride/4+x+1] += (UINT)(3.0/8*error);
pixels[(y+1)*bitmapData.Stride/4+x] += (UINT)(3.0/8*error);
pixels[(y+1)*bitmapData.Stride/4+x+1] += (UINT)(2.0/8*error);
}
} copy.UnlockBits(&bitmapData); CLSID encoder;
if(GetEncoderClsid(L"image/jpeg", &encoder)==-1)
{
__asm int 3
}
copy.Save(L"ErrorDither.jpg", &encoder);
system( "ErrorDither.jpg" );
}int main( int argc, char *argv[] )
{
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
Bitmap orginal(L"test.jpg");
BayerDither(&orginal);
ErrorDither(&orginal);
//GdiplusShutdown(gdiplusToken);
return 0;
}
nGray = y*nwidth*3+x*3; 这行代码严重错误,你还不了解扫描行对齐的概念。
彩色变灰度是不需要抖动的,彩色转索引色才需要抖动。 严格的说灰度也是索引色,但是因为灰度的调色板的特殊性,使得他和其他类型的索引色有很大的不同。楼主的代码也只是24位彩色图像转为24位灰度效果,而不是彩色转为灰度图像。
区别位图中像素占用4字节还是3字节,(有4字节表示一个像素的位图,这要看头部)
区别RGB排列数序,有可能是BGR顺序排列;
记住位图按照扫描行4倍字节补齐;(要看行像素数目)