把一幅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);
原理:只要一个象素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);
而GetRValue,...的参数类型是DWORD(4字节),
会不会这地方处理有误
你用这种方法做出来的应该是灰度的,但是灰度不正确,至少不应该出现彩色,对吗?
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);
}
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;
}
//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周期。