把256级灰度图转变为单色位图,最简单的方法是使用阀值法,先定义一个
阀值(一般取128,256级灰度图的一个像素取值范围从0到255,表示这个点从黑
到白的256级灰度),如果一个点的灰度值小于该阀值,则输出一个黑点,反之则
输出一个白点。参考源码如下:
int segX= cx%8 == 0 ? cx/8 : cx/8+1 ;
// 阀值法(阀值取127)
// 灰度值大于阀值,则该点置位,否则置否
for ( int y=0; y {
LPBYTE lpbPnt=(LPBYTE)lpvDIBits+y*nBytesPerLine;
LPBYTE lpbmonoPnt=(LPBYTE)lpvMonoDIBits+y*nMonoBytesPerLine;
for ( int x=0; x {
int i=8;
BYTE val=0;
while ( i-- ) // 循环8次,在单色位图中,一个字节表示8个像素
{
if ( *lpbPnt++ > 127 )
val|=1< if ( (8-i)+x*8 == cx ) // 是否到达每行的最后一个点
break;
}
*lpbmonoPnt++=val;
}
}
使用上面的转换方法,可以很方便的转换一个256级的灰度图到单色位图,不过该
方法的输出效果不是很好,下面介绍一种抖动优化算法,改善位图的输出效果。
在单色位图中只有两种颜色,如何才能表现位图的灰度呢?这里介绍的方法是用
一个8*8个像素的矩阵来模拟这种的灰度,通过改变矩阵内黑点和白点的比例,就可以
很好的表现出灰度图的灰度变化了,我们把这样的一个矩阵称为一个图案。当然了,用
256种图案来表现源图的256级灰度是不现实的,也是没有必要的,通常我们可以使用32
级或者16级灰度方案。下面就是一个定义好的16级灰度图案组:
// 8*8矩阵,用这64个点的置位(白色点)或置否(黑色点)所组成的图案来模拟
灰度图的灰阶值
// 这里采用16级灰度方案(下面的每一行表示一个8*8的矩阵图案)
BYTE MONOCHROME_16[16][8]={
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
};
如何用这些图案来替代源图的像素呢?这里有几种方法:第一种方法是用这样的
一个图案替代源图的一个像素,不过这种方法使转换后的单色图比源图扩大了8倍(也
可以使用4*4或2*2的矩阵,分别比源图扩大4倍和2倍),幸好一般的打印机的分辨率
都比较大,打印出来以后就没有什么问题了。另一种方法是用一个图案替代源图的8*8
的一个矩形(取这个矩形内各点的平均值),用这种方法转换后就不会扩大源图了,然
而问题是转换后的图象看起来变得一块一块的,不能很好的表现图象的轮廓线。一种
综合的办法就是用代表该点灰度值的图案的相应位置的一个点替代该像素,比如有一
个点Pixel[25,30],灰度值是38,那么模拟该灰度值的图案是38/16(这里假设使用16
级灰度方案)第2个图案,点Pixel[25,30]换算到8*8的矩阵中位置就是[25%8,30%8],
即[1,6]第6行第1个点,比照上面定义的图案:第2个图案的第6行MONOCHROM_16[2][6]
的值是0x22,从左边数第2位是0(第1位表示第0个点),所以该点的值应该为0。
具体方法可以参考下面的源码:
int segX= cx%8 == 0 ? cx/8 : cx/8+1 ;
for ( int y=0; y {
LPBYTE lpbPnt=(LPBYTE)lpvDIBits+y*nBytesPerLine; // 源图当前行的开始处
LPBYTE lpbmonoPnt=(LPBYTE)lpvMonoDIBits+y*nMonoBytesPerLine; // 单色图当前行开始处
for ( int x=0; x {
BYTE bVal=0;
int i=8;
while ( i-- ) // 循环8次,在单色位图中,一个字节表示8个像素
{
int iVal=*lpbPnt++;
iVal/=16; // 这里用16级灰阶方案
// // 判断在矩阵图案中相应位置的点是否置位 //
// y%8 等于该点在8*8矩阵中的垂直位置, i为该点的水平位置
if ( MONOCHROME_16[iVal][y%8] & (1< bVal|=1< if ( (8-i)+x*8 == cx ) // 是否到达每行最后一个点了
break;
}
*lpbmonoPnt++=bVal; // 单色图每8个点为一个字节
}
}