把一幅24位真彩色BMP图象变成灰度图象
  原理:只要一个象素r,g,b的三个分量的值相等,图象就显示为灰度图
  下边的代码可以编译通过。但是执行结果却不是灰度图象,为什么?
   帮俺看看
//得到原图象象素的值
lpSrc =(char*) lpDIBBits + lLineBytes * (lHeight - 1 - i0) + j0;
//得到r,g,b各个分量的值
r=GetRValue(*(lpSrc));
g=GetGValue(*(lpSrc));
b=GetBValue(*(lpSrc));
//求其平均值
cc=(r+g+b)/3;
//对平均值取整
cc=int(cc);//对新图象的象素赋值
*lpDst=RGB(cc,cc,cc);

解决方案 »

  1.   

    我怀疑可能是*lpDst=RGB(cc,cc,cc);这一句出问题了,可能是函数RGB()的用法不正确!那位朋友能帮俺指点一下,给一个RGB()的用法示例也行啊!
      

  2.   

    在24位BMP文件中,每个象素使用3字节分别记录R、G、B色彩分量。
    而GetRValue,...的参数类型是DWORD(4字节),
    会不会这地方处理有误
      

  3.   

    不会,灰度图象的颜色计算方法并不是(r+g+b)/3,而是r,g,b分量所乘的系数不同
    你用这种方法做出来的应该是灰度的,但是灰度不正确,至少不应该出现彩色,对吗?
      

  4.   

    至于具体的例子可以去www.vckbase.com上找到,如果没发现www.codeproject.com上也应该有
      

  5.   

    谢谢, qrlvls(忍者神牛),你讲的有道理!我去查查看
      

  6.   

    Gray(i,j)=0.11*R(i,j)+0.59*G(i,j)+0.3*B(i,j)
      

  7.   

    这个是我使用的
    bool KColorDib::ColorTransform(void (__cdecl *map)(BYTE &,BYTE &,BYTE &))
    {
    //处理颜色表,假如是24位的,可以删掉了
    if(m_pRGBTRIPLE)
    {
    for(int i=0;i<m_nClrUsed;i++)
    map(m_pRGBTRIPLE[i].rgbtRed,m_pRGBTRIPLE[i].rgbtGreen,m_pRGBTRIPLE[i].rgbtBlue);
    return true;
    } if(m_pRGBQUAD)
    {
    for(int i=0;i<m_nClrUsed;i++)
    map(m_pRGBQUAD[i].rgbRed,m_pRGBQUAD[i].rgbGreen,m_pRGBQUAD[i].rgbBlue);
    return true;
    }
    //处理其他的
    for(int y=0;y<m_nHeight;y++)
    {
    int width=m_nWidth;
    unsigned char *pBuffer=(unsigned char*)m_pBits+y*m_nBPS; switch(m_nFormat)
    {
    //处理16位555类型的
    case DIB_16RGB555:  // 15 bit RGB color image, 5-5-5, 1 bit unused
    for (; width>0; width--)
    {
    BYTE red   = ( (* (WORD *) pBuffer) & 0x7C00 ) >> 7;
    BYTE green = ( (* (WORD *) pBuffer) & 0x03E0 ) >> 2;
    BYTE blue  = ( (* (WORD *) pBuffer) & 0x001F ) << 3;

    map( red, green, blue );

    * ( WORD *) pBuffer = ( ( red >> 3 ) << 10 ) | ( ( green >> 3 ) << 5 ) | ( blue >> 3 );

    pBuffer += 2;
    }
    break;
    //处理16位565类型的
    case DIB_16RGB565: // 16 bit RGB color image, 5-6-5
    for (; width>0; width--)
    {
    const DWORD StandardMask565[] =  { 0x00F800, 0x0007E0, 0x00001F }; BYTE red   = ( (* (WORD *) pBuffer) & 0xF800 ) >> 8;
    BYTE green = ( (* (WORD *) pBuffer) & 0x07E0 ) >> 3;
    BYTE blue  = ( (* (WORD *) pBuffer) & 0x001F ) << 3;

    map( red, green, blue );

    * ( WORD *) pBuffer = ( ( red >> 3 ) << 11 ) | ( ( green >> 2 ) << 5 ) | ( blue >> 3 );

    pBuffer += 2;
    }
    break;
    //这个就是处理24位的,假如只要24位,可以将前面的全删掉吧。
    case DIB_24RGB888:
    for(;width>0;width--)
    {
    map(pBuffer[2],pBuffer[1],pBuffer[0]);
    pBuffer+=3;
    }
    break; case DIB_32RGBA8888:
    case DIB_32RGB888:
    for(;width>0;width--)
    {
    map(pBuffer[2],pBuffer[1],pBuffer[0]);
    pBuffer+=4;
    }
    break; default:
    return false;
    }
    }
    return true;
    }
    //这个是典型的灰度图像的算法
    //这个函数是准备传递给上面的那个函数的。主要是灰度图像
    inline void maptogrey(BYTE &red,BYTE &green,BYTE &blue)
    {
    red=(red*77+green*150+blue*29+128)/256;
    green=red;
    blue=red;
    }
    //这个就是生成的接口,处理图像的每个点,并变成灰度图像。maptogrey作为一个指针传入
    bool KColorDib::ToGrey()
    {
    return ColorTransform(maptogrey);
    }
      

  8.   

    //下面就是三色通道分离//只有红的
    inline void maptored(BYTE &red,BYTE &green,BYTE &blue)
    {
    green=0;
    blue=0;
    }
    //只有绿的
    inline void maptogreen(BYTE &red,BYTE &green,BYTE &blue)
    {
    red=0;
    blue=0;
    }
    //只有蓝的
    inline void maptoblue(BYTE &red,BYTE &green,BYTE &blue)
    {
    red=0;
    green=0;
    }bool KColorDib::ToRedc()
    {
    return ColorTransform(maptored);
    }bool KColorDib::ToGreenc()
    {
    return ColorTransform(maptogreen);
    }bool KColorDib::ToBluec()
    {
    return ColorTransform(maptoblue);
    }
    //下面是经常看到的有人问:怎么样使图像变得更亮的GAMMA校正做法,是先定义了三个数组,这样就不样每次都算了,
    节省了好多时间啊。
    //gamma校正的做法
    BYTE redramp[256];
    BYTE greenramp[256];
    BYTE blueramp[256];inline void mapgamma(BYTE &red,BYTE &green,BYTE &blue)
    {
    red=redramp[256];
    green=greenramp[256];
    blue=blueramp[256];
    }
    BYTE gamma(double g,int index)
    {
    return min(255,(int)((255.0*pow(index/255.0,1.0/g))+0.5));
    }bool KColorDib::GammaCorrect(double redgamma, double greengamma, double bluegamma)
    {
    for(int i=0;i<256;i++)
    {
    redramp[i]=gamma(redgamma,i);
    greenramp[i]=gamma(greengamma,i);
    blueramp[i]=gamma(bluegamma,i);
    }
    return ColorTransform(mapgamma);
    }//超值大放送
    //下面一个也是常用的类,在那本VC++图像处理里面能看到的,不过那里面把每个都用了一个大的函数来实现的,根本不管
    每次实现重复的地方。
    //这个类主要处理一些诸如边缘检测啦,高斯模糊啦之类的,反正大家学图像处理的一看就知道有什么用了。
    Filter??bpp只处理一行线的问题。这些函数应该只被内部调用。
    就是说将psrc开始的一行扫描线经过处理后拷到pdest里面去了。
    主要用在两个DIB之间,一个是需要处理的图像,一个是处理后的图像。
    每行线还是调用的函数指针,而我将函数都写成内联的了,所以说其中并没有什么效率问题。
    //8位的处理方法
    void KFilterDib::Filter8bpp(BYTE *pdest, BYTE *psrc, int nwidth, int dy, BYTE (__cdecl *fk)(BYTE *,int,int))
    {
    memcpy(pdest,psrc,m_nHalf);
    pdest+=m_nHalf;
    psrc+=m_nHalf; for(int i=nwidth-2*m_nHalf;i>0;i--)
    *pdest++=fk(psrc++,1,dy); memcpy(pdest,psrc,m_nHalf);
    }//24位的处理方法
    void KFilterDib::Filter24bpp(BYTE *pdest, BYTE *psrc, int nwidth, int dy, BYTE (__cdecl *fk)(BYTE *,int,int))
    {
    //因为一行线左边界上一些点不用处理了,所以直接拷过去就行了,而右边界一些点也不用处理,最后的memcpy的作用也一样的。
    memcpy(pdest,psrc,m_nHalf*3);
    pdest+=m_nHalf*3;
    psrc+=m_nHalf*3;
    //每个像素24,所以为3
    for(int i=nwidth-2*m_nHalf;i>0;i--)
    {
    *pdest++=fk(psrc++,3,dy);
    *pdest++=fk(psrc++,3,dy);
    *pdest++=fk(psrc++,3,dy);
    } memcpy(pdest,psrc,m_nHalf*3);
    }void KFilterDib::Filter32bpp(BYTE *pdest, BYTE *psrc, int nwidth, int dy, BYTE (__cdecl *fk)(BYTE *,int,int))
    {
    memcpy(pdest,psrc,m_nHalf*4);
    pdest+=m_nHalf*3;
    psrc+=m_nHalf*3; for(int i=nwidth-2*m_nHalf;i>0;i--)
    {
    *pdest++=fk(psrc++,4,dy);
    *pdest++=fk(psrc++,4,dy);
    *pdest++=fk(psrc++,4,dy);
    *pdest++=*psrc++;
    } memcpy(pdest,psrc,m_nHalf*4);
    }这个是一个最重要的函数了。
    传入另外一个KDIB的引用,先自己生成一个DIB(我用的是copyfrom(kdb),把那个所有都拷过来了,是图省事的)
    然后对那个DIB做图像处理后,持到自己的像素里面去。//只是一个总接口,具体工作还是由下面的函数来做的。传入的那个函数的值是PBITS的起始区域,下一个顶点的偏移,和移到下一行的顶点的偏移。
    bool KFilterDib::FilterBase(KDib& kdb,BYTE (__cdecl *fk)(BYTE *,int,int))
    {
    CopyFrom(kdb);
    Clear();
    for(int y=0;y<m_nHeight;y++)
    {
    BYTE* pBuffer=(BYTE*)kdb.GetBits()+m_nBPS*y;
    BYTE* pdest=(BYTE*)m_pBits+m_nBPS*y; if( (y>=m_nHalf) &&(y<(m_nHeight-m_nHalf)))
    switch(m_nFormat)
    {
    case DIB_8BPP:
    Filter8bpp(pdest,pBuffer,m_nWidth,m_nBPS,fk);
    break; case DIB_24RGB888:
    Filter24bpp(pdest,pBuffer,m_nWidth,m_nBPS,fk);
    break; case DIB_32RGBA8888:
    case DIB_32RGB888:
    Filter32bpp(pdest,pBuffer,m_nWidth,m_nBPS,fk);
    break; default:
    return false;
    }
    else
    memcpy(pdest,pBuffer,m_nBPS);
    } return true;
    }
      

  9.   

    //这个应该不用说了吧。
    //erosion
    inline void smaller(BYTE &x,BYTE& y)
    {
    if(y<x)x=y;
    }BYTE fksmaller(BYTE *p,int dx,int dy)
    {
    BYTE m=p[-dy-dx];
    smaller(m,p[-dy]);
    smaller(m,p[-dy+dx]);
    smaller(m,p[-dx]);
    smaller(m,p[+dx]);
    smaller(m,p[dy-dx]);
    smaller(m,p[dy]);
    smaller(m,p[dy+dx]); return min(p[0],m);
    }bool KFilterDib::Erosion(KDib &kdb)
    {
    return FilterBase(kdb,fksmaller);
    }
    +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    //33的过滤函数,传入fk33common里面,再进行一定的处理,对周转九个顶点计算加权。
    这里面的处理应该是经常见到的,传入的也是一个函数,只不过这个函数是一个模板的实例。
    这种做法好像在VC6通过不了,如果不行的话,换到VC7上面就可以了。
    bool KFilterDib::Smooth33(KDib &kdb)
    {
    return FilterBase(kdb,FK33base<1,1,1,1,1,1,1,1,1,9,0,false>);
    }
    //还不知道吗?
    bool KFilterDib::GausSmooth33(KDib &kdb)
    {
    return FilterBase(kdb,FK33base<0,1,0,1,4,1,0,1,0,8,0,false>);
    }
    //锐化
    bool KFilterDib::Sharp33(KDib &kdb)
    {
    return FilterBase(kdb,FK33base<1,1,1,1,1,1,1,1,1,1,9,false>);
    }
    //记不得叫什么了,是边缘检测的一种算法
    bool KFilterDib::Lpls(KDib& kdb)
    {
    return FilterBase(kdb,FK33base<0,1,0,1,-4,1,0,1,0,1,1,false>);
    }
    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    //这里面是传入九个顶点的加权值,checkbound是检测像素的,不要也可以。
    template<int k00,int k01,int k02,
     int k10,int k11,int k12,
     int k20,int k21,int k22,
     int weight,int add,bool checkbound>
    BYTE FK33base(BYTE *pPixel,int dx,int dy)
    {
    int r=( pPixel[-dy-dx] * k00 + pPixel[-dy] * k01 + pPixel[-dy+dx] * k02 +
        pPixel[   -dx] * k10 + pPixel[0]   * k11 + pPixel[   +dx] * k12 +
        pPixel[ dy-dx] * k20 + pPixel[dy]  * k21 + pPixel[ dy+dx] * k22 ) / weight + add;
    if(checkbound)
    if(r<0)
    return 0;
    else if(r>255)
    return 255;
    return r;
    }使用起来很方便
    ktmp,kdb都是DIB类,其中KDB是一开始读入位图文件的那个类
    void CTst1Dlg::Ongreen() 
    {
    ktmp.ToGrey();
    //或者ktmp.Sharp33(kdb);
    OnPaint();
    // TODO: Add your control notification handler code here

    }
    void CTst1Dlg::Onorigin() 
    {
    ktmp.CopyFrom(kdb);
    OnPaint();
    // TODO: Add your control notification handler code here

    }我曾经将函数指针改为模板来实现,不过打开ASM一看,好像编译的时候并没有出现我想要的效果,
    这样做的话速度还没有达到最快,因为每次好像都要调用函数指针,多好几个CPU周期。