第4章图象的半影调和抖动技术 在介绍本章内容之前,先提出一个问题?普通的黑白针式打印机能打出灰度图来吗?如果说 能,从针式打印机的打印原理来分析,似乎是不可能的。因为针打是靠撞针击打色带在纸上 形成黑点的,不可能打出灰色的点来:如果说不能,可是我们的确见过用针式打印机打印出 来的灰色图象。到底是怎么回事呢? 你再仔细看看那些打印出来的所谓的灰色图象,最好用放大镜看。你会发现,原来这些灰色 图象都是由一些黑点组成的,黑点多一些,图象就暗一些;黑点少一些,图案就亮一些。下 面这几张图就很能说明这一点。 图41用黑白两种颜色打印出灰度效果 图41中最左边的是原图,是一幅真正的灰度图,另外三张图都是黑白二值图。容易看出, 最左的那幅和原图最接近 由二值图象显示出灰度效果的方法,就是我们今天要讲的半影调 Halftone)技术,它的一个主 要用途就是在只有二值输出的打印机上打印图象。我们介绍两种方法:图案法和抖动法。 41图案法 图案法( patterning)是指灰度可以用一定比例的黑白点组成的区域表示,从而达到整体图象的 灰度感。黑白点的位置选择称为图案化 在具体介绍图案法之前,先介绍一下分辨率的概念。计算机显示器,打印机,扫描仪等设备 的一个重要指标就是分辨率,单位是dpi( dot per inch),即每英寸点数,点数越多,分辨率就 越高,图象就越清晰。让我们来计算一下,计算机显示器的分辨率有多高。设显示器为15 英寸(指对角线长度),最多显示1280×1024个点。因为宽高比为4:3,所以宽有12英寸, 高有9英寸,则该显示器的水平分辨率为106dpi,垂直分辨率为1138dpi。一般的激光打印 机的分辨率有300dpi×30odpi,600dpi×600dpi,720dpi×720dpi。所以打出来的图象要比计 算机显示出来的清晰的多。扫描仪的分辨率要高一些,数码相机的分辨率更高。 言归正传,前面讲了,图案化使用图案来表示象素的灰度,那么我们来做一道计算题。假设 有一幅240×180×8bit的灰度图,当用分辨率为300dpi×300dpi的激光打印机将其打印到 128×9.6英寸的纸上时,每个象素的图案有多大? 这道题很简单,这张纸最多可以打(300×12.8)×(300×96=3840×2880个点,所以每个象 素可以用(3840/240)×(2880180)=16×16个点大小的图案来表示,即一个象素256个点。如 果这16×16的方块中一个黑点也没有,就可以表示灰度256:有一个黑点,就表示灰度255: 依次类推,当都是黑点时,表示灰度0。这样,16×16的方块可以表示257级灰度,比要求 的8b共256级灰度还多了一个。所以上面的那幅图的灰度级别完全能够打印出来
第 4 章 图象的半影调和抖动技术 在介绍本章内容之前,先提出一个问题?普通的黑白针式打印机能打出灰度图来吗?如果说 能,从针式打印机的打印原理来分析,似乎是不可能的。因为针打是靠撞针击打色带在纸上 形成黑点的,不可能打出灰色的点来;如果说不能,可是我们的确见过用针式打印机打印出 来的灰色图象。到底是怎么回事呢? 你再仔细看看那些打印出来的所谓的灰色图象,最好用放大镜看。你会发现,原来这些灰色 图象都是由一些黑点组成的,黑点多一些,图象就暗一些;黑点少一些,图案就亮一些。下 面这几张图就很能说明这一点。 图 4.1 用黑白两种颜色打印出灰度效果 图 4.1 中最左边的是原图,是一幅真正的灰度图,另外三张图都是黑白二值图。容易看出, 最左的那幅和原图最接近。 由二值图象显示出灰度效果的方法,就是我们今天要讲的半影调(halftone)技术,它的一个主 要用途就是在只有二值输出的打印机上打印图象。我们介绍两种方法:图案法和抖动法。 4.1 图案法 图案法(patterning)是指灰度可以用一定比例的黑白点组成的区域表示,从而达到整体图象的 灰度感。黑白点的位置选择称为图案化。 在具体介绍图案法之前,先介绍一下分辨率的概念。计算机显示器,打印机,扫描仪等设备 的一个重要指标就是分辨率,单位是 dpi(dot per inch),即每英寸点数,点数越多,分辨率就 越高,图象就越清晰。让我们来计算一下,计算机显示器的分辨率有多高。设显示器为 15 英寸(指对角线长度),最多显示 1280×1024 个点。因为宽高比为 4:3,所以宽有 12 英寸, 高有 9 英寸,则该显示器的水平分辨率为 106dpi,垂直分辨率为 113.8dpi。一般的激光打印 机的分辨率有 300dpi×300dpi,600dpi×600dpi,720dpi×720dpi。所以打出来的图象要比计 算机显示出来的清晰的多。扫描仪的分辨率要高一些,数码相机的分辨率更高。 言归正传,前面讲了,图案化使用图案来表示象素的灰度,那么我们来做一道计算题。假设 有一幅 240×180×8bit 的灰度图,当用分辨率为 300dpi×300dpi 的激光打印机将其打印到 12.8×9.6 英寸的纸上时,每个象素的图案有多大? 这道题很简单,这张纸最多可以打(300×12.8) ×(300×9.6)=3840×2880 个点,所以每个象 素可以用(3840/240)×(2880/180)=16×16 个点大小的图案来表示,即一个象素 256 个点。如 果这 16×16 的方块中一个黑点也没有,就可以表示灰度 256;有一个黑点,就表示灰度 255; 依次类推,当都是黑点时,表示灰度 0。这样,16×16 的方块可以表示 257 级灰度,比要求 的 8bit 共 256 级灰度还多了一个。所以上面的那幅图的灰度级别完全能够打印出来
这里有一个图案构成的问题,即黑点打在哪里?比如说,只有一个黑点时,我们可以打在正 中央,也可以打16×16的左上角。图案可以是规则的,也可以是不规则的。一般情况下, 有规则的图案比随即图案能够避免点的从集,但有时会导致图象中有明显的线条 如图41中,2×2的图案可以表示5级灰度,当图象中有一片灰度为的1的区域时,如图 42所示,有明显的水平和垂直线条。 图422×2的图案 图43规则图案导致线条 如果想存储256级灰度的图案,就需要256×16×16的二值点阵,占用的空间还是相当可观 的。有一个更好的办法是:只存储一个整数矩阵,称为标准图案,其中的每个值从0到255 图象的实际灰度和阵列中的每个值比较,当该值大于等于灰度时,对应点打一黑点。下面举 个25级灰度的例子加以说明 0142258 62416 10154n17 图44标准图案举例 图44中,左边为标准图案,右边为灰度为5的图案,共有10个黑点,15个白点。其实道 理很简单,灰度为0时全是黑点,灰度每增加1,减少一个黑点。要注意的是,5×5的图案 可以表示26种灰度,当灰度是25才是全白点,而不是灰度为24时。 下面介绍一种设计标准图案的算法,是由Limb在1969年提出的。 先以一个2×2的矩阵开始:设M1= 通过递归关系有Mn+1= 4M、+2U 4M+x4M+乙 以x],其中M和Un均为2×2n的方阵,Uh的所有元素都是1 08210 24146 311 根据这个算法,可以得到M2=157135 ,为16级灰度的标准图案 M3(8×8阵)比较特殊,称为 Bayer抖动表。M是一个16×16的矩阵 根据上面的算法,如果利用M3一个象素要用8×8的图案表示,则一幅N×N的图将变成 8N×8N大小。如果利用M4,就更不得了,变成16N×16N了。能不能在保持原图大小的
这里有一个图案构成的问题,即黑点打在哪里?比如说,只有一个黑点时,我们可以打在正 中央,也可以打 16×16 的左上角。图案可以是规则的,也可以是不规则的。一般情况下, 有规则的图案比随即图案能够避免点的丛集,但有时会导致图象中有明显的线条。 如图 4.1 中,2×2 的图案可以表示 5 级灰度,当图象中有一片灰度为的 1 的区域时,如图 4.2 所示,有明显的水平和垂直线条。 图 4.2 2×2 的图案 图 4.3 规则图案导致线条 如果想存储 256 级灰度的图案,就需要 256×16×16 的二值点阵,占用的空间还是相当可观 的。有一个更好的办法是:只存储一个整数矩阵,称为标准图案,其中的每个值从 0 到 255。 图象的实际灰度和阵列中的每个值比较,当该值大于等于灰度时,对应点打一黑点。下面举 一个 25 级灰度的例子加以说明。 图 4.4 标准图案举例 图 4.4 中,左边为标准图案,右边为灰度为 15 的图案,共有 10 个黑点,15 个白点。其实道 理很简单,灰度为 0 时全是黑点,灰度每增加 1,减少一个黑点。要注意的是,5×5 的图案 可以表示 26 种灰度,当灰度是 25 才是全白点,而不是灰度为 24 时。 下面介绍一种设计标准图案的算法,是由 Limb 在 1969 年提出的。 先以一个 2 × 2 的矩阵开始:设 M1= ,通过递归关系有 Mn+1= ,其中 Mn 和 Un 均为 2 n×2 n 的方阵,Un 的所有元素都是 1。 根据这个算法,可以得到 M2= ,为 16 级灰度的标准图案。 M3(8×8 阵)比较特殊,称为 Bayer 抖动表。M4 是一个 16×16 的矩阵。 根据上面的算法,如果利用 M3 一个象素要用 8×8 的图案表示,则一幅 N×N 的图将变成 8N×8N 大小。如果利用 M4,就更不得了,变成 16N×16N 了。能不能在保持原图大小的
情况下利用图案化技术呢?一种很自然的想法是:如果用M2阵,则将原图中每8×8个点 中取一点,即重新采样,然后再应用图案化技术,就能够保持原图大小。实际上,这种方法 并不可行。首先,你不知道这8×8个点中找哪一点比较合适,另外,8×8的间隔实在太大 了,生成的图象和原图肯定相差很大,就象图4.1最右边的那幅图一样 我们可以采用这样的做法:假设原图是256级灰度,利用 Bayer抖动表,做如下处理 f(gyx]>>2)> bayer&7[x&7then打一白点else打一黑点 其中,xy代表原图的象素坐标,gyx代表该点灰度。首先将灰度右移两位,变成64级, 然后将x,y做模8运算,找到 Bayer表中的对应点,两者做比较,根据上面给出的判据做 处理 我们可以看到,模8运算使得原图分成了一个个8×8的小块,每个小块和8×8的 Bayer 表相对应。小块中的每个点都参与了比较,这样就避免了上面提到的选点和块划分过大的问 题。模8运算实质上是引入了随机成分,这就是我们下面要讲到的抖动技术 图45就是利用了这个算法,使用M( Bayer抖动表)阵得到的;图6是使用M阵得到的 可见两者的差别并不是很大,所以一般用 Bayer表就可以了 图45利用M3抖动生成的图 图46利用M4抖动生成的图
情况下利用图案化技术呢?一种很自然的想法是:如果用 M2 阵,则将原图中每 8×8 个点 中取一点,即重新采样,然后再应用图案化技术,就能够保持原图大小。实际上,这种方法 并不可行。首先,你不知道这 8×8 个点中找哪一点比较合适,另外,8×8 的间隔实在太大 了,生成的图象和原图肯定相差很大,就象图 4.1 最右边的那幅图一样。 我们可以采用这样的做法:假设原图是 256 级灰度,利用 Bayer 抖动表,做如下处理 if (g[y][x]>>2) > bayer[y&7][x&7] then 打一白点 else 打一黑点 其中,x,y 代表原图的象素坐标,g[y][x]代表该点灰度。首先将灰度右移两位,变成 64 级, 然后将 x,y 做模 8 运算,找到 Bayer 表中的对应点,两者做比较,根据上面给出的判据做 处理。 我们可以看到,模 8 运算使得原图分成了一个个 8×8 的小块,每个小块和 8×8 的 Bayer 表相对应。小块中的每个点都参与了比较,这样就避免了上面提到的选点和块划分过大的问 题。模 8 运算实质上是引入了随机成分,这就是我们下面要讲到的抖动技术。 图 4.5 就是利用了这个算法,使用 M3(Bayer 抖动表)阵得到的;图 6 是使用 M4 阵得到的, 可见两者的差别并不是很大,所以一般用 Bayer 表就可以了。 图 4.5 利用 M3 抖动生成的图 图 4.6 利用 M4 抖动生成的图
下面是算法的源程序,是针对 Bayer表的。因为它是个常用的表,我们不再利用Lmb公式, 而是直接给出。针对M阵的算法是类似的,不同的地方在于,要用Limb公式得到M阵, 灰度也不用右移2位。要注意的是,为了处理的方便,我们的结果图仍采用256级灰度图, 不过只用到了0和255两种灰度 BYTE BayerPatternl8[8j={0,32,8,402,34,10,42, 48,16,56,24,50,18,58,26, 12,44,4,36,1446,6,38, 60,28,52,20,62,30,5422 3,35,11,43,1,33,941 1,19,59,27,49,17,57,25, 15,47,7,39,13,45,5,37, BOOL LimbPattern M3(HWND hwnd) DWORD OffBits Bufsize LPBITMAPINFOHEADER Iplmg Data; LPSTR HLOCAL hTemplmgData LPBITMAPINFOHEADER Ip Templmg Data LPSTR lpTempPtr HDC HFILE LONG num OffBits-bf. bfOffBits-sizeof(BITMAPFILEHEADER) Bufsize= OffBits+bi. biHeight*Line Bytes,/要开的缓冲区大小 if(hTemplmg DataLocalAlloc(LHND, BufSize))==NULL) Message Box(hWnd, "Error alloc memory!","Error Message", MB OK MB ICONEXCLAMATION) return False. Iplmg Data=(LPBITMAPINFOHEADER)GlobalLock(hlmg Data); IpTemplmg Data(LPBITMAPINFOHEADER)LocalLock(hTemplmg Data); ∥拷贝头信息和位图数据 memcpy(lpTemplmg Data, Iplmg Data, BufSize) for(y=0; y<bi. biHeight; y++)i pPr为指向原图位图数据的指针
下面是算法的源程序,是针对 Bayer 表的。因为它是个常用的表,我们不再利用 Limb 公式, 而是直接给出。针对 M4 阵的算法是类似的,不同的地方在于,要用 Limb 公式得到 M4 阵, 灰度也不用右移 2 位。要注意的是,为了处理的方便,我们的结果图仍采用 256 级灰度图, 不过只用到了 0 和 255 两种灰度。 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}; BOOL LimbPatternM3(HWND hWnd) { DWORD OffBits,BufSize LPBITMAPINFOHEADER lpImgData; LPSTR lpPtr; HLOCAL hTempImgData; LPBITMAPINFOHEADER lpTempImgData; LPSTR lpTempPtr; HDC hDc; HFILE hf; LONG x,y; unsigned char num; OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER); BufSize=OffBits+bi.biHeight*LineBytes;//要开的缓冲区大小 if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL) { MessageBox(hWnd,"Error alloc memory!","Error Message",MB_OK| MB_ICONEXCLAMATION); return FALSE; } lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData); lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData); //拷贝头信息和位图数据 memcpy(lpTempImgData,lpImgData,BufSize); for(y=0;y<bi.biHeight;y++){ //lpPtr 为指向原图位图数据的指针
IpPtr=(char *)Iplmg Data( BufSize-LineBytes-y * Line Bytes) pTempPtr为指向新图位图数据的指针 lpTempPtr=(char *)Ip Templmg Data+ ( BufSize-LineBytes-y*Line Bytes) for(x=0 X<bi bi width; x++)4 f((num>2)> BayerPattern[y&x&刀)∥右移两位后做比较 ( pTempPtr++)=( unsigned char)255,/白点 else*( IpTempPtr++)=( unsigned char)O,∥打黑点 if(hBitmap l=NULL) DeleteObject(hBitmap) hDc=GetDC(hWnd) 形成新的位图 hBitmap=CreateDI Bitmap(hDc, (LPBITMAPINFOHEADER)Ip Templmg Data, (LONG)CBM INIT. sizeof( BITMAPINFOHEADER)+ umColors*sizeof(RGBQUAD) (LPBITMAPINFO)IpTemplmg Data, DIB RGB COLORS) hf- creat("c: Wimbm3 bmp", 0); Iwrite(hf, (LPSTR)&bf, sizeof( BI TMAPFILEHEADER): Iwrite(hf, (LPSTR)Ip Templmg Data, BufSize) Iclose(hf); ∥释放内存和资源 ReleaseDC(hWnd, hDc) LocalUnlock(hTemplmg Data Local Free(hTemplmg Data) GlobalUnlock(hmg Data) return TRUE. 42抖动法 让我们考虑更坏的情况:即使使用了图案化技术,仍然得不到要求的灰度级别。举例说明: 假设有一幅600×450×8bt的灰度图,当用分辨率为30dpi×30odpi的激光打印机将其打 印到8×6英寸的纸上时,每个象素可以用(2400600×(1800/450=4×4个点大小的图案来
lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes); //lpTempPtr 为指向新图位图数据的指针 lpTempPtr=(char *)lpTempImgData+(BufSize-LineBytes-y*LineBytes); for(x=0;x<bi.biWidth;x++){ num=(unsigned char)*lpPtr++; if ( (num>>2) > BayerPattern[y&7][x&7]) //右移两位后做比较 *(lpTempPtr++)=(unsigned char)255; //打白点 else *(lpTempPtr++)=(unsigned char)0; //打黑点 } } if(hBitmap!=NULL) DeleteObject(hBitmap); hDc=GetDC(hWnd); //形成新的位图 hBitmap=CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpTempImgData, (LONG)CBM_INIT, (LPSTR)lpTempImgData+ sizeof(BITMAPINFOHEADER)+ NumColors*sizeof(RGBQUAD), (LPBITMAPINFO)lpTempImgData, DIB_RGB_COLORS); hf=_lcreat("c:\\limbm3.bmp",0); _lwrite(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER)); _lwrite(hf,(LPSTR)lpTempImgData,BufSize); _lclose(hf); //释放内存和资源 ReleaseDC(hWnd,hDc); LocalUnlock(hTempImgData); LocalFree(hTempImgData); GlobalUnlock(hImgData); return TRUE; } 4.2 抖动法 让我们考虑更坏的情况:即使使用了图案化技术,仍然得不到要求的灰度级别。举例说明: 假设有一幅 600×450×8bit 的灰度图,当用分辨率为 300dpi×300dpi 的激光打印机将其打 印到 8×6 英寸的纸上时,每个象素可以用(2400/600)×(1800/450)=4×4 个点大小的图案来