24位真彩色转换为8位灰度图VC++代码文档格式.docx
《24位真彩色转换为8位灰度图VC++代码文档格式.docx》由会员分享,可在线阅读,更多相关《24位真彩色转换为8位灰度图VC++代码文档格式.docx(26页珍藏版)》请在冰点文库上搜索。
![24位真彩色转换为8位灰度图VC++代码文档格式.docx](https://file1.bingdoc.com/fileroot1/2023-5/6/06b0961f-300f-4686-890b-15df1b59b0e3/06b0961f-300f-4686-890b-15df1b59b0e31.gif)
DWORDdwTemp=0;
ReadFile(hSourceFile,pSource,dwSourceSize,&
dwTemp,NULL);
BITMAPFILEHEADER*pSourceFileHeader=(BITMAPFILEHEADER*)pSource;
BITMAPINFOHEADER*pSourceInfoHeader=(BITMAPINFOHEADER*)(pSource+sizeof(BITMAPFILEHEADER));
//不是BMP文件或者不是24位真彩色
if(pSourceFileHeader->
bfType!
=0x4d42||pSourceInfoHeader->
biBitCount!
=24)
CloseHandle(hSourceFile);
VirtualFree(pSource,NULL,MEM_RELEASE);
returnFALSE;
LONGnWidth=pSourceInfoHeader->
biWidth;
LONGnHeight=pSourceInfoHeader->
biHeight;
LONGnSourceWidth=nWidth*3;
if(nSourceWidth%4)nSourceWidth=(nSourceWidth/4+1)*4;
LONGnTargetWidth=nWidth;
if(nTargetWidth%4)nTargetWidth=(nTargetWidth/4+1)*4;
dwTargetSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256+nHeight*nTargetWidth;
pTarget=(PBYTE)VirtualAlloc(NULL,dwTargetSize,MEM_COMMIT,PAGE_READWRITE);
memset(pTarget,0,dwTargetSize);
if(pTarget==NULL)
VirtualFree(pTarget,NULL,MEM_RELEASE);
BITMAPFILEHEADER*pTargetFileHeader=(BITMAPFILEHEADER*)pTarget;
BITMAPINFOHEADER*pTargetInfoHeader=
(BITMAPINFOHEADER*)(pTarget+sizeof(BITMAPFILEHEADER));
pTargetFileHeader->
bfType=pSourceFileHeader->
bfType;
bfSize=dwTargetSize;
bfReserved1=0;
bfReserved2=0;
bfOffBits=sizeof(BITMAPFILEHEADER)
+sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256;
pTargetInfoHeader->
biBitCount=8;
biClrImportant=0;
biClrUsed=256;
biCompression=BI_RGB;
biHeight=pSourceInfoHeader->
biPlanes=1;
biSize=sizeof(BITMAPINFOHEADER);
biSizeImage=nHeight*nTargetWidth;
biWidth=pSourceInfoHeader->
biXPelsPerMeter=pSourceInfoHeader->
biXPelsPerMeter;
biYPelsPerMeter=pSourceInfoHeader->
biYPelsPerMeter;
RGBQUAD*pRgb;
for(inti=0;
i<
256;
i++)//初始化8位灰度图的调色板信息
pRgb=(RGBQUAD*)(pTarget+sizeof(BITMAPFILEHEADER)
+sizeof(BITMAPINFOHEADER)+i*sizeof(RGBQUAD));
pRgb->
rgbBlue=i;
pRgb->
rgbGreen=i;
rgbRed=i;
rgbReserved=0;
for(intm=0;
m<
nHeight;
m++)//转化真彩色图为灰度图
for(intn=0;
n<
nWidth;
n++)
{
pTarget[pTargetFileHeader->
bfOffBits+m*nTargetWidth+n]=
pSource[pSourceFileHeader->
bfOffBits+m*nSourceWidth+n*3]*0.114
+pSource[pSourceFileHeader->
bfOffBits+m*nSourceWidth+n*3+1]*0.587
bfOffBits+m*nSourceWidth+n*3+2]*0.299;
}
hTargetFile=CreateFile(szTargetFile,GENERIC_WRITE,FILE_SHARE_WRITE,
NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
BOOLstat=WriteFile(hTargetFile,pTarget,dwTargetSize,&
CloseHandle(hTargetFile);
returnstat;
转化效果如下图
在GDI+中将24位真彩色图转换为灰度图(原理、C#调用指针)
在图像处理中,我们经常需要将真彩色图像转换为黑白图像。
严格的讲应该是灰度图,因为真正的黑白图像是二色,即只有纯黑,纯白二色。
开始之前,我们先简单补充一下计算机中图像的表示原理。
计算机中的图像大致可以分成两类:
位图(Bitmap)和矢量图(Metafile)。
位图可以视为一个二维的网格,整个图像就是由很多个点组成的,点的个数等于位图的宽乘以高。
每个点被称为一个像素点,每个像素点有确定的颜色,当很多个像素合在一起时就形成了一幅完整的图像。
我们通常使用的图像大部分都是位图,如数码相机拍摄的照片,都是位图。
因为位图可以完美的表示图像的细节,能较好的还原图像的原景。
但位图也有缺点:
第一是体积比较大,所以人们开发了很多压缩图像格式来储存位图图像,目前应用最广的是JPEG格式,在WEB上得到了广泛应用,另外还有GIF,PNG等等。
第二是位图在放大时,不可避免的会出现“锯齿”现象,这也由位图的本质特点决定的。
所以在现实中,我们还需要使用到另一种图像格式:
矢量图。
同位图不同,矢量图同位图的原理不同,矢量图是利用数学公式通过圆,线段等绘制出来的,所以不管如何放大都不会出现变形,但矢量图不能描述非常复杂的图像。
所以矢量图都是用来描述图形图案,各种CAD软件等等都是使用矢量格式来保存文件的。
在讲解颜色转换之前,我们要先对位图的颜色表示方式做一了解。
位图中通常是用RGB三色方式来表示颜色的(位数很少时要使用调色板)。
所以每个像素采用不同的位数,就可以表示出不同数量的颜色。
如下图所示:
每像素的位数
一个像素可分配到的颜色数量
1
2^1=2
2
2^2=4
4
2^4=16
8
2^8=256
16
2^16=65,536
24
2^24=16,777,216
从中我们可以看出,当使用24位色(3个字节)时,我们可以得到1600多万种颜色,这已经非常丰富了,应该已接近人眼所能分辨的颜色了。
现在计算机中使用最多的就是24位色,别外在GDI+中还有一种32位色,多出来的一个通道用来描述Alpha,即透明分量。
24位色中3个字节分别用来描述R,G,B三种颜色分量,我们看到这其中是没有亮度分量的,这是因为在RGB表示方式中,亮度也是直接可以从颜色分量中得到的,每一颜色分量值的范围都是从0到255,某一颜色分量的值越大,就表示这一分量的亮度值越高,所以255表示最亮,0表示最暗。
那么一个真彩色像素点转换为灰度图时它的亮度值应该是多少呢,首先我们想到的平均值,即将R+G+B/3。
但现实中我们使用的却是如下的公式:
Y=0.299R+0.587G+0.114B
这个公式通常都被称为心理学灰度公式。
这里面我们看到绿色分量所占比重最大。
因为科学家发现使用上述公式进行转换时所得到的灰度图最接近人眼对灰度图的感觉。
因为灰度图中颜色数量一共只有256种(1个字节),所以转换后的图像我们通常保存为8位格式而不是24位格式,这样比较节省空间。
而8位图像是使用调色板方式来保存颜色的。
而不是直接保存颜色值。
调色板中可以保存256颜色,所以可以正好可以将256种灰度颜色保存到调色版中。
代码如下:
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Text;
usingSystem.Drawing;
usingSystem.Drawing.Imaging;
namespaceConsoleApplication2
classProgram
unsafestaticvoidMain(string[]args)
Bitmapimg=(Bitmap)Image.FromFile(@"
E:
/MyDocuments/MyPictures/cherry_blossom_1002.jpg"
Bitmapbit=newBitmap(img.Width,
img.Height,PixelFormat.Format8bppIndexed);
BitmapDatadata
=img.LockBits(newRectangle(0,0,img.Width,img.Height),ImageLockMode.ReadOnly,PixelFormat.Format24bppRgb);
byte*bp=(byte*)data.Scan0.ToPointer();
BitmapDatadata2=
bit.LockBits(newRectangle(0,0,bit.Width,bit.Height),ImageLockMode.ReadWrite,PixelFormat.Format8bppIndexed);
byte*bp2=(byte*)data2.Scan0.ToPointer();
for(inti=0;
i!
=data.Height;
i++)
for(intj=0;
j!
=data.Width;
j++)
//0.3R+0.59G+0.11B
floatvalue=0.11F*bp[i*data.Stride+j*3]
+0.59F*bp[i*data.Stride+j*3+1]
+0.3F*bp[i*data.Stride+j*3+2];
bp2[i*data2.Stride+j]=(byte)value;
img.UnlockBits(data);
bit.UnlockBits(data2);
ColorPalettepalette=bit.Palette;
=palette.Entries.Length;
palette.Entries[i]=Color.FromArgb(i,i,i);
bit.Palette=palette;
bit.Save(@"
/TEMP/bb.jpeg"
ImageFormat.Jpeg);
img.Dispose();
bit.Dispose();
}
代码中我使用了指针直接操作位图数据,同样的操作要比使用GetPixel,SetPixel快非常多。
我们要感谢微软在C#中保留了特定情况下的指针操作。
图像效果如下:
C++代码处理24位图转8位图
代码处理对于宽度和高度都为基数的图形处理会产生形变!
核心部分代码如下:
////代码中m_sourcefile指24位真彩色图片的位置,m_targetfile是转换后的256色BMP灰度图保存的位置
voidCMy24Dlg:
:
OnBtnConvert()
UpdateData();
if(m_sourcefile=="
"
||m_targetfile=="
)
return;
FILE*sourcefile,*targetfile;
//位图文件头和信息头
BITMAPFILEHEADERsourcefileheader,targetfileheader;
BITMAPINFOHEADERsourceinfoheader,targetinfoheader;
memset(&
targetfileheader,0,sizeof(BITMAPFILEHEADER));
targetinfoheader,0,sizeof(BITMAPINFOHEADER));
sourcefile=fopen(m_sourcefile,"
rb"
fread((void*)&
sourcefileheader,1,sizeof(BITMAPFILEHEADER),sourcefile);
//提取原图文件头
if(sourcefileheader.bfType!
=0x4d42)
fclose(sourcefile);
MessageBox("
原图象不为BMP图象!
sourceinfoheader,1,sizeof(BITMAPINFOHEADER),sourcefile);
//提取文件信息头
if(sourceinfoheader.biBitCount!
原图象不为24位真彩色!
if(sourceinfoheader.biCompression!
=BI_RGB)
原图象为压缩后的图象,本程序不处理压缩过的图象!
//构造灰度图的文件头
targetfileheader.bfOffBits=54+sizeof(RGBQUAD)*256;
targetfileheader.bfSize=targetfileheader.bfOffBits+sourceinfoheader.biSizeImage/3;
targetfileheader.bfReserved1=0;
targetfileheader.bfReserved2=0;
targetfileheader.bfType=0x4d42;
//构造灰度图的信息头
targetinfoheader.biBitCount=8;
targetinfoheader.biSize=40;
targetinfoheader.biHeight=sourceinfoheader.biHeight;
targetinfoheader.biWidth=sourceinfoheader.biWidth;
targetinfoheader.biPlanes=1;
targetinfoheader.biCompression=BI_RGB;
targetinfoheader.biSizeImage=sourceinfoheader.biSizeImage/3;
targetinfoheader.biXPelsPerMeter=sourceinfoheader.biXPelsPerMeter;
targetinfoheader.biYPelsPerMeter=sourceinfoheader.biYPelsPerMeter;
targetinfoheader.biClrImportant=0;
targetinfoheader.biClrUsed=256;
构造灰度图的调色版
RGBQUADrgbquad[256];
inti,j,m,n,k;
for(i=0;
i++)
rgbquad[i].rgbBlue=i;
rgbquad[i].rgbGreen=i;
rgbquad[i].rgbRed=i;
rgbquad[i].rgbReserved=0;
targetfile=fopen(m_targetfile,"
wb"
//写入灰度图的文件头,信息头和调色板信息
fwrite((void*)&
targetfileheader,1,sizeof(BITMAPFILEHEADER),targetfile);
targetinfoheader,1,sizeof(BITMAPINFOHEADER),targetfile);
rgbquad,1,sizeof(RGBQUAD)*256,targetfile);
BYTE*sourcebuf;
BYTE*targetbuf;
//这里是因为BMP规定保存时长度和宽度必须是4的整数倍,如果不是则要补足
m=(targetinfoheader.biWidth/4)*4;
if(m<
targetinfoheader.biWidth)
m=m+4;
n=(targetinfoheader.biHeight/4)*4;
if(n<
targetinfoheader.biHeight)
n=n+4;
sourcebuf=(BYTE*)malloc(m*n*3);
//读取原图的颜色矩阵信息
fread(sourcebuf,1,m*n*3,sourcefile);
fclose(sourcefile);
targetbuf=(BYTE*)malloc(m*n);
BYTEcolor[3];
通过原图颜色矩阵信息得到灰度图的矩阵信息
n;
for(j=0;
j<
m;
j++)
for(k=0;
k<
3;
k++)
color[k]=sourcebuf[(i*m+j)*3+k];
targetbuf[(i*m)+j]=color[0]*0.114+color[1]*0.587+color[2]*0.299;
if(targetbuf[(i*m)+j]>
255)
targetbuf[(i*m)+j]=255;
fwrite((void*)targetbuf,1,m*n+1,targetfile);
fclose(targetfile);
MessageBox("
位图文件转换成功!
24位真彩色和转换后的灰度图
上边的两组图就是用那段代码处理的结果
另外一种C++代码
BOOLTrans24To8(HDIBm_hDIB)
//判断8位DIB是否为空
if(m_hDIB1!
=NULL)
GlobalUnlock((HGLOBAL)m_hDIB1);
GlobalFree((HGLOBAL)m_hDIB1);
m_hDIB1=NULL;
BITMAPINFOHEADER*pBmpH;
//指向信息头的指针
m_hDIB1=(HDIB):
GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT,
sizeof(BITMAPINFOHEADER)+768*576+256*sizeof(RGBQUAD));