第4章 图象的半影调和抖动技术.docx
- 文档编号:23440949
- 上传时间:2023-05-17
- 格式:DOCX
- 页数:19
- 大小:158.12KB
第4章 图象的半影调和抖动技术.docx
《第4章 图象的半影调和抖动技术.docx》由会员分享,可在线阅读,更多相关《第4章 图象的半影调和抖动技术.docx(19页珍藏版)》请在冰豆网上搜索。
第4章图象的半影调和抖动技术
第4章图象的半影调和抖动技术
在介绍本章内容之前,先提出一个问题?
普通的黑白针式打印机能打出灰度图来吗?
如果说能,从针式打印机的打印原理来分析,似乎是不可能的。
因为针打是靠撞针击打色带在纸上形成黑点的,不可能打出灰色的点来;如果说不能,可是我们的确见过用针式打印机打印出来的灰色图象。
到底是怎么回事呢?
你再仔细看看那些打印出来的所谓的灰色图象,最好用放大镜看。
你会发现,原来这些灰色图象都是由一些黑点组成的,黑点多一些,图象就暗一些;黑点少一些,图案就亮一些。
下面这几张图就很能说明这一点。
图4.1用黑白两种颜色打印出灰度效果
图4.1中最左边的是原图,是一幅真正的灰度图,另外三张图都是黑白二值图。
容易看出,最左的那幅和原图最接近。
由二值图象显示出灰度效果的方法,就是我们今天要讲的半影调(halftone)技术,它的一个主要用途就是在只有二值输出的打印机上打印图象。
我们介绍两种方法:
图案法和抖动法。
4.1图案法
图案法(patterning)是指灰度可以用一定比例的黑白点组成的区域表示,从而达到整体图象的灰度感。
黑白点的位置选择称为图案化。
在具体介绍图案法之前,先介绍一下分辨率的概念。
计算机显示器,打印机,扫描仪等设备的一个重要指标就是分辨率,单位是dpi(dotperinch),即每英寸点数,点数越多,分辨率就越高,图象就越清晰。
让我们来计算一下,计算机显示器的分辨率有多高。
设显示器为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的左上角。
图案可以是规则的,也可以是不规则的。
一般情况下,有规则的图案比随即图案能够避免点的丛集,但有时会导致图象中有明显的线条。
如图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均为2n×2n的方阵,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抖动表,做如下处理
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表的。
因为它是个常用的表,我们不再利用Limb公式,而是直接给出。
针对M4阵的算法是类似的,不同的地方在于,要用Limb公式得到M4阵,灰度也不用右移2位。
要注意的是,为了处理的方便,我们的结果图仍采用256级灰度图,不过只用到了0和255两种灰度。
BYTEBayerPattern[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};
BOOLLimbPatternM3(HWNDhWnd)
{
DWORD OffBits,BufSize
LPBITMAPINFOHEADER lpImgData;
LPSTR lpPtr;
HLOCAL hTempImgData;
LPBITMAPINFOHEADER lpTempImgData;
LPSTR lpTempPtr;
HDC hDc;
HFILE hf;
LONG x,y;
unsignedchar num;
OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER);
BufSize=OffBits+bi.biHeight*LineBytes;//要开的缓冲区大小
if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL)
{
MessageBox(hWnd,"Errorallocmemory!
","ErrorMessage",MB_OK|
MB_ICONEXCLAMATION);
returnFALSE;
}
lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);
lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData);
//拷贝头信息和位图数据
memcpy(lpTempImgData,lpImgData,BufSize);
for(y=0;y //lpPtr为指向原图位图数据的指针 lpPtr=(char*)lpImgData+(BufSize-LineBytes-y*LineBytes); //lpTempPtr为指向新图位图数据的指针 lpTempPtr=(char*)lpTempImgData+(BufSize-LineBytes-y*LineBytes); for(x=0;x num=(unsignedchar)*lpPtr++; if((num>>2)>BayerPattern[y&7][x&7])//右移两位后做比较 *(lpTempPtr++)=(unsignedchar)255;//打白点 else*(lpTempPtr++)=(unsignedchar)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); returnTRUE; } 4.2抖动法 让我们考虑更坏的情况: 即使使用了图案化技术,仍然得不到要求的灰度级别。 举例说明: 假设有一幅600×450×8bit的灰度图,当用分辨率为300dpi×300dpi的激光打印机将其打印到8×6英寸的纸上时,每个象素可以用(2400/600)×(1800/450)=4×4个点大小的图案来表示,最多能表示17级灰度,无法满足256级灰度的要求。 可有两种解决方案: (1)减小图象尺寸,由600×450变为150×113; (2)降低图象灰度级,由256级变成16级。 这两种方案都不理想。 这时,我们就可以采用“抖动法”(dithering)的技术来解决这个问题。 其实刚才给出的算法就是一种抖动算法,称为规则抖动(regulardithering)。 规则抖动的优点是算法简单;缺点是图案化有时很明显,这是因为取模运算虽然引入了随机成分,但还是有规律的。 另外,点之间进行比较时,只要比标准图案上点的值大就打白点,这种做法并不理想,因为,如果当标准图案点的灰度值本身就很小,而图象中点的灰度只比它大一点儿时,图象中的点更接近黑色,而不是白色。 一种更好的方法是将这个误差传播到邻近的象素。 下面介绍的Floyd-Steinberg算法就采用了这种方案。 假设灰度级别的范围从b(black)到w(white),中间值t为(b+w)/2,对应256级灰度,b=0,w=255,t=127.5。 设原图中象素的灰度为g,误差值为e,则新图中对应象素的值用如下的方法得到: ifg>tthen 打白点 e=g-w else 打黑点 e=g-b 3/8×e加到右边的象素 3/8×e加到下边的象素 1/4×e加到右下方的象素 算法的意思很明白: 以256级灰度为例,假设一个点的灰度为130,在灰度图中应该是一个灰点。 由于一般图象中灰度是连续变化的,相邻象素的灰度值很可能与本象素非常接近,所以该点及周围应该是一片灰色区域。 在新图中,130大于128,所以打了白点,但130离真正的白点255还差的比较远,误差e=130-255=-125比较大。 ,将3/8×(-125)加到相邻象素后,使得相邻象素的值接近0而打黑点。 下一次,e又变成正的,使得相邻象素的相邻象素打白点,这样一白一黑一白,表现出来刚好就是灰色。 如果不传递误差,就是一片白色了。 再举个例子,如果一个点的灰度为250,在灰度图中应该是一个白点,该点及周围应该是一片白色区域。 在新图中,虽然e=-5也是负的,但其值很小,对相邻象素的影响不大,所以还是能够打出一片白色区域来。 这样就验证了算法的正确性。 其它的情况你可以自己推敲。 图4.7是利用Floyd-Steinberg算法抖动生成的图。 图4.7 利用Floyd-Steinberg算法抖动生成的图 下面我们给出Floyd-Steinberg算法的源代码。 有一点要说明,我们原来介绍的程序都是先开一个char类型的缓冲区,用来存储新图数据,但在这个算法中,因为e有可能是负数,为了防止得到的值超出char能表示的范围,我们使用了一个int类型的缓冲区存储新值。 另外,当按从左到右,从上到下的顺序处理象素时,处理过的象素以后不会再用到了,所以用这个int类型的缓冲区存储新值是可行的。 全部象素处理完后,再将这些值拷贝到char类型的缓冲区去。 BOOLSteinberg(HWNDhWnd) { DWORD OffBits,BufSize,IntBufSize; LPBITMAPINFOHEADERlpImgData; HLOCAL hTempImgData; LPBITMAPINFOHEADERlpTempImgData; LPSTR lpPtr; LPSTR lpTempPtr; HDC hDc; HFILE hf; LONG x,y; unsignedchar num; float e,f; HLOCAL hIntBuf; int *lpIntBuf,*lpIntPtr; int tempnum; //OffBits为BITMAPINFOHEADER结构长度加调色板的大小 OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER); BufSize=OffBits+bi.biHeight*LineBytes;//要开的缓冲区的大小 if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL) { MessageBox(hWnd,"Errorallocmemory! ","ErrorMessage",MB_OK| MB_ICONEXCLAMATION); returnFALSE; } IntBufSize=(DWORD)bi.biHeight*LineBytes*sizeof(int);if((hIntBuf=LocalAlloc(LHND,IntBufSize))==NULL)//int类型的缓冲区 { MessageBox(hWnd,"Errorallocmemory! ","ErrorMessage",MB_OK| MB_ICONEXCLAMATION); LocalFree(hTempImgData); returnFALSE; } lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData); lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData); lpIntBuf=(int*)LocalLock(hIntBuf); //拷贝头信息 memcpy(lpTempImgData,lpImgData,OffBits); //将图象数据拷贝到int类型的缓冲区中 for(y=0;y lpPtr=(char*)lpImgData+(BufSize-LineBytes-y*LineBytes); lpIntPtr=(int*)lpIntBuf+(bi.biHeight-1-y)*LineBytes; for(x=0;x *(lpIntPtr++)=(unsignedchar)*(lpPtr++); } for(y=0;y for(x=0;x lpIntPtr=(int*)lpIntBuf+(bi.biHeight-1-y)*LineBytes+x; num=(unsignedchar)*lpIntPtr; if(num>128){//128是中值 *lpIntPtr=255;//打白点 e=(float)(num-255.0);//计算误差 } else{ *lpIntPtr=0;//打黑点 e=(float)num;//计算误差 } if(x f=(float)*(lpIntPtr+1); f+=(float)((3.0/8.0)*e); *(lpIntPtr+1)=(int)f;//向左传播 } if(y f=(float)*(lpIntPtr-LineBytes); f+=(float)((3.0/8.0)*e); *(lpIntPtr-LineBytes)=(int)f;//向下传播 f=(float)*(lpIntPtr-LineBytes+1); f+=(float)((1.0/4.0)*e); *(lpIntPtr-LineBytes+1)=(int)f;//向右下传播 } } } //从int类型的缓冲区拷贝到char类型的缓冲区 for(y=0;y lpTempPtr=(char*)lpTempImgData+(BufSize-LineBytes-y*LineBytes); lpIntPtr=(int*)lpIntBuf+(bi.biHeight-1-y)*LineBytes; for(x=0;x tempnum=*(lpIntPtr++); if(tempnum>255)tempnum=255; elseif(tempnum<0)tempnum=0; *(lpTempPtr++)=(unsignedchar)tempnum; } } 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: \\steinberg.bmp",0); _lwrite(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER)); _lwrite(hf,(LPSTR)lpTempImgData,BufSize); _lclose(hf); //释放内存和资源 ReleaseDC(hWnd,hDc); GlobalUnlock(hImgData); LocalUnlock(hTempImgD
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第4章 图象的半影调和抖动技术 图象 半影 调和 抖动 技术