写了个程序,想要把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);//指示背景区域无效,开始重绘
}大家帮忙看看是哪里的毛病?

解决方案 »

  1.   

    灰化(gray)指将彩色图像变成灰度图,标准的处理办法,是根据人眼对不同色彩的强度感知不同,采取加权平均的方法:
    r = g = b = L = 0.299R + 0.587G + 0.114B
    也可以简单地用算术平均来进行灰化:
    r = g = b = L = (R + G + B) / 3
      

  2.   

    Intensity =  ( lpbits[nGray] + lpbits[nGray+1] + lpbits[nGray+2] ) / 3;你把上面这句换成这样试试:Intensity = lpbits[nGray] / 3 + lpbits[nGray+1] / 3 + lpbits[nGray+2] / 3;估计是一些数据类型不对,我在matlab中做的时候当两个类型的是uint8(matlab中)的时候并且相加超过255时就会取255,不会取更大的了。
      

  3.   

    nGray = y*nwidth*3+x*3; //没考虑4字节对齐,除非宽度刚好是4的倍数,否则就是错的.nGray = y *nWidhtBytes + x*3;// nWidthBytes = ( ( ( nWidth * 24 ) + 31 ) / 32 ) * 4;
    btw:
    LPBITMAPINFOHEADER lpbmi = (LPBITMAPINFOHEADER)pDIB; 
    int nwidth = lpbmi->biWidth; 
    int nheigth = lpbmi->biHeight; 没有鉴定biBitCount..假如是32bit的呢?那肯定全错了..
      

  4.   

    我觉得吧可能是你的数据类型有问题
    LPSTR lpbits用LPBYTE lpbits试试
    因为位图中的数据是0~255的, 你用的数据类型可能是char型的, 取不到128以上的, 所以出现变黑的情况, 其实可以到Debug下调试看看问题出现在哪里哈
      

  5.   

    LPSTR对应的就是char*
    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(&copy);
    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(&copy);
    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;
    }
      

  6.   


    nGray = y*nwidth*3+x*3; 这行代码严重错误,你还不了解扫描行对齐的概念。
    彩色变灰度是不需要抖动的,彩色转索引色才需要抖动。  严格的说灰度也是索引色,但是因为灰度的调色板的特殊性,使得他和其他类型的索引色有很大的不同。楼主的代码也只是24位彩色图像转为24位灰度效果,而不是彩色转为灰度图像。
      

  7.   

    使用0.299R + 0.587G + 0.114B 是科学的方法;
    区别位图中像素占用4字节还是3字节,(有4字节表示一个像素的位图,这要看头部)
    区别RGB排列数序,有可能是BGR顺序排列;
    记住位图按照扫描行4倍字节补齐;(要看行像素数目)
      

  8.   

    果然如cloudyi大侠所说,改了数据类型,就正常了;KenFire大侠说的也对,还贴了那么长的代码,只是晚了一点,只给了5分,别介意啊,呵呵谢谢大家!
      

  9.   

    你把图像数据区的数据转换成BYTE再试试