第08章 膠片和圖象管線_第1頁(yè)
第08章 膠片和圖象管線_第2頁(yè)
第08章 膠片和圖象管線_第3頁(yè)
第08章 膠片和圖象管線_第4頁(yè)
第08章 膠片和圖象管線_第5頁(yè)
已閱讀5頁(yè),還剩10頁(yè)未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第八章膠片和圖象管線

相機(jī)中的膠片類(lèi)型對(duì)入射光轉(zhuǎn)換為圖像顏色的方式有很大的影響。在pbrt中,我們用Film類(lèi)描述對(duì)模擬相機(jī)中的感光設(shè)備。在求得每條相機(jī)光線的輻射亮度以后,F(xiàn)ilm類(lèi)的實(shí)現(xiàn)確定采樣對(duì)附近像素的貢獻(xiàn)值,并更新圖像表示。當(dāng)程序退出主渲染循環(huán)時(shí),F(xiàn)ilm通常還要把最終的圖像寫(xiě)入磁盤(pán)文件。

本章值描述一個(gè)Film類(lèi)的實(shí)現(xiàn)。它使用像素重構(gòu)公式來(lái)計(jì)算最終的像素值,并把圖像的浮點(diǎn)顏色值寫(xiě)入磁盤(pán)。對(duì)于一個(gè)基于物理的渲染器而言,用浮點(diǎn)格式來(lái)創(chuàng)建圖像要比其它用8位無(wú)符號(hào)整數(shù)的圖像格式要靈活得多,因?yàn)楦↑c(diǎn)數(shù)格式可以避免在圖像量化過(guò)程中丟失重要信息。

然而,為了在現(xiàn)代顯示設(shè)備上顯示這樣的圖像,必須把這些浮點(diǎn)像素值映射到顯示器上的離散值。

因此,本章還描述了一個(gè)圖像處理管線,對(duì)圖像進(jìn)行一系列的變換,來(lái)應(yīng)付顯示設(shè)備的局限性。例如,計(jì)算機(jī)顯示器通常需要像素的顏色由RGB三元色來(lái)表示,而不需要一個(gè)任意的光譜功率分布的表示。所以,用基函數(shù)系數(shù)表示的光譜值要被轉(zhuǎn)換為RGB值才能被顯示在顯示器上。在第8.4節(jié)中我們要討論一個(gè)相關(guān)的問(wèn)題,即如何將圖像顯示在可顯示的輻射亮度范圍(跟真實(shí)世界相比較)非常有限的顯示器上。所以,我們?cè)谙袼刂档挠成溥^(guò)程中要使得所顯示的圖像和跟在理想顯示設(shè)備上所顯示的圖像盡可能地接近。

8.1Film接口

Film基類(lèi)定義了Film實(shí)現(xiàn)的抽象接口:

<FilmDeclarations>=

classFilm{

public:

<FilmInterface>

<FilmPublicData>

}

Film的構(gòu)造器需要整個(gè)圖像在x,y方向上的分辨率,并將它們分別存放在共用成員變量Film::xResolution和Film::yResolution中。在第6章所介紹的Camera類(lèi)在進(jìn)行某些相機(jī)變換時(shí)(例如,光柵到相機(jī)空間的變換)將要用對(duì)到這些值。

<FilmInterface>

Film(intxres,intyres)

:xResolution(xres),yResolution(yres){

}

<FilmPublicData>

constintxResolution,yResolution;

Film類(lèi)的第一個(gè)函數(shù)是Film::AddSample(),它以一個(gè)采樣及其對(duì)應(yīng)的相機(jī)光線,輻射亮度值和alpha值做為參數(shù)來(lái)更新圖像。

<FilmInterface>+=

virtualvoidAddSample(constSample&sample,constRay&ray,

constSpectrum&L,floatalpha)=0;

當(dāng)退出渲染主循環(huán)時(shí),Scene::Render()調(diào)用Film::WriteImage(),該函數(shù)可以允許Film對(duì)圖像任意的處理工作,然后再顯示或存盤(pán)。

<FilmInterface>+=

virtualvoidWriteImage()=0;

Film的最后的一項(xiàng)責(zé)任是負(fù)責(zé)決定采樣器進(jìn)行采樣所需要的整數(shù)像素值的范圍。雖然對(duì)于簡(jiǎn)單的Film實(shí)現(xiàn)而言,像素范圍是從(0,0)到(xResolution-1,yResolution-1),但是由于像素重構(gòu)濾波器的范圍的有限,通常需要在圖像邊界稍微靠外的地方采樣。

<FilmInterface>+=

virtualvoidGetSampleExtent(int*xstart,int*xend,

int*ystart,int*yend)const=0;

8.2圖像膠片

在pbrt中,我們只提供一個(gè)Film類(lèi)的實(shí)現(xiàn):ImageFilm。這個(gè)類(lèi)用給定的重構(gòu)濾波器對(duì)圖像采樣值進(jìn)行濾波,并把結(jié)果圖像寫(xiě)入磁盤(pán)。

<ImageFilmDeclarations>=

classImageFilm:publicFilm{

public:

<ImageFilmpublicMethod>

private:

<ImageFilmPrivateData>

}

除了整個(gè)圖像的分辨率以外,ImageFilm構(gòu)造器的參數(shù)還有一個(gè)濾波器函數(shù),一個(gè)裁剪窗口(即一個(gè)位于[0,1]x[0,1]區(qū)域內(nèi)的矩形),輸出圖像文件名,一個(gè)是否將像素顏色乘以alpha值的布爾參數(shù),以及將部分圖像寫(xiě)入磁盤(pán)的頻率。

<ImageFilmMethodDefinitions>=

ImageFilm::ImageFilm(intxres,intyres,

Filter*filt,constfloatcrop[4],

conststring&fn,boolpremult,intwf)

:Film(xres,yres){

filter=filt;

memcpy(cropWindow,crop,4*sizeof(float));

filename=fn;

premultiplyAlpha=premult;

writeFrequency=sampleCount=wf;

<Computefilmimageextent>

<Allocatefilmimagestorage>

<Precomputefilterweighttable>

}

<ImageFilmPrivateData>=

Filter*filter;

intwriteFrequency,sampleCount;

stringfilename;

boolpremultiplyAlpha;

floatcropWindow[4];

裁剪窗口和總體的圖像分辨率給出了實(shí)際被存儲(chǔ)或?qū)懭氪疟P(pán)的像素范圍。裁剪窗口對(duì)查找錯(cuò)誤很有用,也有利于將大的圖像分成若干份,并在不同的計(jì)算機(jī)上渲染再整合。裁剪窗口是用NDC空間來(lái)定義的,坐標(biāo)范圍是從0到1。ImageFilm::xPixelStart和ImageFilm::yPixelStart存放裁剪窗口的左上角的坐標(biāo)位置,ImageFilm::xPixelCount和ImageFilm::yPixelCount分別給出了在各個(gè)方向的像素總數(shù)。它們的計(jì)算如下:

<Computefilmimageextent>=

xPixelStart=Ceil2Int(xResolution*cropWindow[0]);

xPixelCount=Ceil2Int(xResolution*cropWindow[1])-xPixelStart;

yPixelStart=Ceil2Int(yResolution*cropWindow[2]);

yPixelCount=Ceil2Int(yResolution*cropWindow[3])-yPixelStart;

<ImageFilmPrivateData>+=

int

xPixelStart,xPixelCount,yPixelStart,yPixelCount;

有了(可能被裁剪過(guò)的)圖像的像素分辨率,構(gòu)造器就申請(qǐng)一個(gè)Pixel結(jié)構(gòu)數(shù)組,每個(gè)數(shù)組元素放一個(gè)像素。像素的輻射亮度值被存放在Pixel::L里,alpha值被存放在Pixel::alpha,而Pixel::weightSum存放采樣對(duì)像素的貢獻(xiàn)值的濾波權(quán)值之和。這個(gè)值被用于像素濾波公式(7.3)。因?yàn)槊總€(gè)圖像采樣要存取一個(gè)小區(qū)域中的像素信息,所以ImageFilm用BlockedArray來(lái)存放像素,這樣可以降低緩存訪問(wèn)失敗次數(shù)。

<Allocatefilmimagestorage>=

pixels=newBlockedArray<Pixel>(xPixelCount,yPixelCount);

<ImageFilmPrivateData>+=

structPixel{

Pixel():L(0.f){

alpha=0.f;

weightSum=0.f;

}

SpectrumL;

floatalpha,weightSum;

};

BlockedArray<Pixel>*pixels;

根據(jù)pbrt對(duì)像素濾波器的缺省設(shè)置,每個(gè)圖像采樣大約對(duì)16個(gè)像素有貢獻(xiàn)值。特別是對(duì)于簡(jiǎn)單的場(chǎng)景,雖然花在光線求交測(cè)試和著色計(jì)算上的時(shí)間很少,但花在為每個(gè)采樣更新圖像的時(shí)間卻很長(zhǎng)。因此,ImageFilm預(yù)先計(jì)算好一個(gè)濾波器值的表,這樣Film::AddSample()函數(shù)就可以避免對(duì)Filter::Evalulate()的虛函數(shù)調(diào)用以及濾波器求值的開(kāi)銷(xiāo),而是直接使用表里的值進(jìn)行濾波。這里沒(méi)有使用精確的采樣位置來(lái)濾波,但是所帶來(lái)的誤差在實(shí)際應(yīng)用中幾乎可以忽略不計(jì)。

這里的實(shí)現(xiàn)合理地假設(shè)濾波器可以滿足f(x,y)=f(|x|,|y|),這樣表格只需存放濾波器偏置值的正值部分(即第一象限)。這個(gè)假設(shè)對(duì)pbrt中所介紹的所有濾波器都是成立的,這樣只需要四分之一的表格大小,從而提高了內(nèi)存存取的一致性和緩存效率。

<Precomputefilterweighttable>=

#defineFILTER_TABLE_SIZE16

filterTable=newfloat[FILTER_TABLE_SIZE*FILTER_TABLE_SIZE];

float*ftp=filterTable;

for(inty=0;y<FILTER_TABLE_SIZE;++y){

floatfy=((float)y+.5f)*

filter->yWidth/FILTER_TABLE_SIZE;

for(intx=0;x<FILTER_TABLE_SIZE;++x){

floatfx=((float)x+.5f)*

filter->xWidth/FILTER_TABLE_SIZE;

*ftp++=filter->Evaluate(fx,fy);

}

}

(ImageFilmPrivateData)+=

float*filterTable;

為了理解ImageFilm::AddSample(),我們回憶一下像素濾波公式:

該公式用附近采樣的輻射亮度值的加權(quán)和來(lái)計(jì)算每個(gè)像素值I(x,y),其中利用了濾波函數(shù)f來(lái)計(jì)算權(quán)值。因?yàn)閜brt用到的所有濾波器具有有限的范圍,所以該函數(shù)先要計(jì)算有哪些像素會(huì)被當(dāng)前采樣所影響。然后,利用像素濾波公式為每個(gè)像素計(jì)算兩個(gè)累加和:第一個(gè)累加和是對(duì)公式的分子部分的累加計(jì)算,另一個(gè)是對(duì)公式的分母部分的累加計(jì)算,最后像素值是兩個(gè)累加和相除所得到的結(jié)果。

<ImageFilmMethodDefinitions>+=

voidImageFilm::AddSample(constSample&sample,constRay&ray,

constSpectrum&L,floatalpha){

<Computesample'srasterextent>

<Loopoverfiltersupportandaddsampletopixelarrays>

<Possiblywriteoutin-progressimage>

}

為了找出那些被采樣所影響的像素,F(xiàn)ilm::AddSample()將采樣的連續(xù)坐標(biāo)轉(zhuǎn)換為離散坐標(biāo),即把x,y坐標(biāo)分別減去0.5。然后,將兩個(gè)坐標(biāo)值分別在x,y方向上偏移,偏移量為濾波器的寬度,然后對(duì)最小的坐標(biāo)值取Ceiling值,對(duì)最大的坐標(biāo)值取floor值,因?yàn)槌鰹V波器范圍的像素肯定不會(huì)受當(dāng)前采樣的影響。如圖:

<Computesample'srasterextent>=

floatdImageX=sample.imageX-0.5f;

floatdImageY=sample.imageY-0.5f;

intx0=Ceil2Int(dImageX-filter->xWidth);

intx1=Floor2Int(dImageX+filter->xWidth);

inty0=Ceil2Int(dImageY-filter->yWidth);

inty1=Floor2Int(dImageY+filter->yWidth);

x0=max(x0,xPixelStart);

x1=min(x1,xPixelStart+xPixelCount-1);

y0=max(y0,yPixelStart);

y1=min(y1,yPixelStart+yPixelCount-1);

有了該采樣的像素范圍(x0,y0)到(x1,y1),該函數(shù)對(duì)所有這些像素進(jìn)行循環(huán),然后對(duì)采樣值進(jìn)行相應(yīng)的濾波:

<Loopoverfiltersupportandaddsampletopixelarrays>=

<Precomputexandyfiltertableoffsets>

for(inty=y0;y<=y1;++y)

for(intx=0;x<=x1;++x){

<Evaluatefiltervalueat(x,y)pixel>

<Updatepixelvalueswithfilteredsamplecontribution>

}

每個(gè)像素(x,y)使用以其為中心的濾波函數(shù)。為了計(jì)算某個(gè)采樣的過(guò)濾器權(quán)值,需要得到像素到采樣位置的整數(shù)偏移值,然后求濾波器的值。如果我們直接求濾波器的值,需要做下面的計(jì)算:

fliterWt=filter->Evaluate(x-dImageX,y-dimageY);

實(shí)際上,我們是從一個(gè)預(yù)先計(jì)算好的表格里取值的。

給定了采樣位置(x,y)和像素位置(x',y'),該例程計(jì)算偏置量(x'-x,y'-y),并把它轉(zhuǎn)換為在濾波權(quán)值表里的相應(yīng)位置,以便在表中取值。我們可以直接這樣做:將采樣偏置值除以其所在方向上的濾波器寬度,從而得到一個(gè)0到1之間的值,然后在乘以表格的大小。如果注意到對(duì)于x方向上的每行像素y的偏置量是常量(相似地,y方向上x(chóng)偏置量也是常量),就可以做進(jìn)一步的優(yōu)化。因此,我們?cè)谧鲅h(huán)之前,預(yù)先計(jì)算好這些索引值,省去了循環(huán)中的重復(fù)工作。

<Precomputexandyfiltertableoffsets>=

int*ifx=(int*)alloca((x1-x0+1)*sizeof(int));

for(intx=x0;x<=x1;++x){

floatfx=fabsf((x-dImageX)*

filter->invXWidth*FILTER_TABLE_SIZE);

ifx[x-x0]=min(Floor2Int(fx),FILTER_TABLE_SIZE-1);

}

int*ify=(int*)alloca((y1-y0+1)*sizeof(int));

for(inty=y0;y<=y1;++y){

floatfy=fabsf((y-dImageY)*

filter->invYWidth*FILTER_TABLE_SIZE);

ifx[y-y0]=min(Floor2Int(fy),FILTER_TABLE_SIZE-1);

}

這樣對(duì)于每個(gè)像素而言,它的關(guān)于濾波器權(quán)值表的x和y偏置值可以通過(guò)查表求得,同時(shí)也就知道了它對(duì)于濾波器權(quán)值表的偏置值以及濾波器權(quán)值:

<Evaluatefiltervalueat(x,y)pixel>=

intoffset=ify[y-y0]*FILTER_TABLE_SIZE+ifx[x-x0];

folatfilterWt=filterTable[offset];

<Updatepixelvalueswithfilteredsamplecontribution>=

Pixel&pixel=(*pixels)(x-xPixelStart,y-yPixelStart);

pixel.L.AddWeighted(filterWt,L);

pixel.alpha+=alpha*filterWt;

pixel.weightSum+=filterWt;

因?yàn)橄袼刂貥?gòu)濾波器跨越多個(gè)像素,所以Sampler必須生成實(shí)際像素范圍之外的采樣。這樣圖像邊界上的像素更其它的像素相比,在每個(gè)方向上都有相同的采樣密度。這對(duì)于利用裁剪窗口來(lái)渲染子圖像的方法同樣重要,因?yàn)檫@可以避免在子圖像邊緣產(chǎn)生的人為缺陷。

<ImageFilmMethodDefinitions>+=

voidImageFilm::GetSampleExtent(int*xstart,int*xend,

int*ystart,int*yend)const{

*xstart=Floor2Int(xPixelStart-filter->xWidth);

*xend

=Ceil2Int(xPixelStart+xPixelCount+

filter->xWidth);

*ystart=Floor2Int(yPixelStart-filter->yWidth);

*yend

=Ceil2Int(yPixelStart+yPixelCount+

filter->yWidth);

有些圖像需要很長(zhǎng)的渲染時(shí)間,如果渲染器可以定期地輸出圖像,就對(duì)用戶有所助益。如果ImageFilm:writeFrequency被設(shè)為非零值,則要重復(fù)地調(diào)用Imagefilm:WriteImage():

<Possiblywriteoutin-progressimage>=

if(--sampleCount==0){

WriteImage();

sampleCount=writeFrequency;

}

8.2.1圖像輸出

主渲染循環(huán)結(jié)束后,Scene::Render()調(diào)用Film::WriteImage()來(lái)將最終的圖像結(jié)果寫(xiě)入一個(gè)文件。如前所述,ImageFilm類(lèi)也用它定期地將部分圖像結(jié)果寫(xiě)盤(pán)。

<ImageFilmMethodDefinitions>+=

voidImageFilm::WriteImage(){

<ConvertimagetoRGBandcomputefinalpixelvalues>

<WriteRGBAimage>

<Releasetemporaryimagememory>

}

首先,該函數(shù)將像素值做一個(gè)備份,使得膠片上的圖像值不再受濾波器的影響。這樣在渲染過(guò)程中,該函數(shù)可以被重復(fù)調(diào)用,將部分圖像寫(xiě)盤(pán)。

<ConvertimagetoRGBandcomputefinalpixelvalues>=

intnPix=xPixelCount*yPixelCount;

float*rgb=newfloat[3*nPix],*alpha=newfloat[nPix];

intoffset=0;

for(inty=0;y<yPixelCount;++y){

for(intx=0;x<xPixelCount;++x){

<ConvertpixelspectralradiancetoRGB>

alpha[offset]=(*pixels)(x,y).aphla;

<Normalizepixelwithweightsum>

<Computepremultipliedalphacolor>

++offset;

}

}

如果有了顯示設(shè)備的反應(yīng)特征信息,就可以將像素值轉(zhuǎn)換為設(shè)備相關(guān)的RGB值。首先,先將它們轉(zhuǎn)換為設(shè)備無(wú)關(guān)的XYZ三刺激值,然后在轉(zhuǎn)換為RGB值。這相當(dāng)于又換了一次光譜基函數(shù),新的基函數(shù)是有顯示設(shè)備的RGB光譜反應(yīng)曲線來(lái)決定的。這里,我們將XYZ值轉(zhuǎn)換為基于HDTV標(biāo)準(zhǔn)的設(shè)備RGB值。這對(duì)于大多數(shù)的現(xiàn)代顯示設(shè)備而言都是很合適的。

<ConvertpixelspectralradiancetoRGB>=

floatxyz[3];

(*pixels)(x,y).L.XVZ(xyz);

constfloatrWeight[3]={3.240479f,-1.537150f,-0.498535f};

constfloatgWeight[3]={-0.969256f,1.875991f,

0.041556f};

constfloatbWeight[3]={0.055648f,-0.204043f,1.05731tf};

rgb[3*offset]

=rWelght[0]*xyz[0]+

=rWeight[1]*xyz[1]+

rWeight[2]*xyz[2];

rgb[3*offset+1]

=gWelght[0]*xyz[0]+

=gWeight[1]*xyz[1]+

gWeight[2]*xyz[2];

rgb[3*offset+2]

=bWelght[0]*xyz[0]+

=bWeight[1]*xyz[1]+

bWeight[2]*xyz[2];

在初始化像素值的時(shí)候,由像素濾波函數(shù)所算出的最終值是將每個(gè)像素采樣值除以Pixel::weightSum來(lái)得到的。由于重構(gòu)濾波函數(shù)存在負(fù)值區(qū)域,故像素值可能出現(xiàn)負(fù)值,這時(shí)將需要負(fù)值改為0.

<Normalizepixelwithweightsum>=

floatweightSum=(*pixels)(x,y).weightSum;

if(weightSum!=0.f){

floatinvWt=1.f/weightSum;

rgb[3*offset]=Clamp(rgb[3*offset]*invWt,0.f,INFINITY);

rgb[3*offset+1]=Clamp(rgb[3*offset+1]*invWt,0.f,INFINITY);

rgb[3*offset+2]=Clamp(rgb[3*offset+2]*invWt,0.f,INFINITY);

alpha[offset]=Clamp(alpha[offset]*invWt,0.f,INFINITY);

作為一種選擇,每個(gè)像素值可以乘上其alpha值,這個(gè)步驟被稱(chēng)為預(yù)乘alpha(又稱(chēng)關(guān)聯(lián)alpha).

<Computepremutipliedalphacolor>=

if(premultiplyAlpha){

rgb[3*offset]*=alpha[offset];

rgb[3*offset+1]*=alpha[offset];

rgb[3*offset+2]*=alpha[offset];

WriteRGBAImage()函數(shù)處理將圖像寫(xiě)盤(pán)的細(xì)節(jié):

<WriteRGBAImage>=

WriteRGBAImage(filename,rgb,alpha,xPixelCount,yPixelCount,

xResolution,yResolution,xPixelStart,yPixelStart);

存盤(pán)之后,就可以釋放工作內(nèi)存空間了:

<Releasetemporaryimagememory>=

delete[]alpha;

delete[]rgb;

8.3圖像管線

把通用的浮點(diǎn)數(shù)圖像轉(zhuǎn)換為適合于顯示的格式需要一系列的圖像變換。這些變換是在函數(shù)ApplyImagingPipeLine()里實(shí)現(xiàn)的。有些頗為棘手的問(wèn)題需要仔細(xì)地加以對(duì)待,例如顯示器局限問(wèn)題,人類(lèi)視覺(jué)系統(tǒng)的行為,等等。pbrt本身(包括插件)并不使用這個(gè)函數(shù),而是用于外圍的程序,如toos/exrtotiff.cpp程序。該函數(shù)還可以用于另一種Film的實(shí)現(xiàn),該實(shí)現(xiàn)直接用整數(shù)像素格式來(lái)創(chuàng)建圖像。傳給該函數(shù)的參數(shù)被用來(lái)指導(dǎo)這個(gè)轉(zhuǎn)換過(guò)程,其意義在本章會(huì)陸續(xù)被介紹。

圖像管線有4個(gè)階段。首先,選擇性地使用色調(diào)重現(xiàn)算法將像素輻射亮度值范圍映射到有局限的便于顯示的范圍中。再者,使用gamma校正,負(fù)責(zé)調(diào)整被顯示的顏色值和其在顯示器上的亮度值之間的非線性關(guān)系。然后,將像素值進(jìn)行比例變換來(lái)覆蓋顯示器所需的輸入值范圍。最后,使用抖動(dòng)(dithering)對(duì)像素值加上少許的隨機(jī)噪聲,這樣可以打斷不同區(qū)域中出現(xiàn)的不同顏色的過(guò)渡。

<ImagePipelineFunctionDefinitions>=

voidApplyImagingPipeline(float*rgb,intxResolution,

intyResolution,float*yWeight,

floatbloomRadius,floatbloomWeight,

constchar*toneMapName,constParamSet*toneMapParams,

floatgamma,floatdither,intmaxDisplayValue){

intnPix=xResolution*yResolution;

<Possiblyapplybloomeffecttoimage>

<Applytonereproductiontoimage>

<Handleout-of-gamutRGBvalues>

<Applygammacorrectiontoimage>

<Mapimagetodisplayrange>

<Ditherimage>

}

8.4感知上的問(wèn)題和色調(diào)映射

在計(jì)算機(jī)圖形學(xué)的早期年代,著色模型總是返回位于0到1之間的顏色值,跟實(shí)際的物理量沒(méi)有什么聯(lián)系。因此,像素值也是位于這個(gè)范圍,只要對(duì)像素值進(jìn)行比例變換,就可以將圖像直接顯示到帶有RGB(范圍0~255)幀緩存的CRT顯示器上。在真實(shí)世界中,經(jīng)常存在輻射度值等級(jí)大約從0.01到1000(即從最亮部分到最暗部分的變化有5個(gè)等級(jí))的場(chǎng)景。值得注意的是,人類(lèi)視覺(jué)系統(tǒng)(HVS)卻可以很好地處理這種亮度的極端變化,因?yàn)槿说难劬?duì)局部的亮度對(duì)比要比絕對(duì)的亮度更敏感。而計(jì)算機(jī)顯示設(shè)備不僅不接受輻射亮度值作為輸入,而且還不能夠顯示非常亮或非常暗的顏色。在理想觀看條件下,這些設(shè)備通常只能顯示兩個(gè)亮度變化的級(jí)別。

因?yàn)榛谖锢淼匿秩舅惴ㄋ傻恼鎸?shí)感圖像同樣受制于輻射亮度值和設(shè)備顯示能力的不匹配,所以,為了使所顯示的圖像跟實(shí)際場(chǎng)景盡可能地接近,解決這個(gè)圖像顯示問(wèn)題至關(guān)重要。這是一個(gè)非?;钴S的研究領(lǐng)域,即所謂的色調(diào)映射(tonemapping),來(lái)尋找將多出來(lái)的亮度等級(jí)加以壓縮以便顯示的好方法。該領(lǐng)域深入到對(duì)人類(lèi)視覺(jué)系統(tǒng)的研究,以引導(dǎo)對(duì)圖像顯示技術(shù)的開(kāi)發(fā)。通過(guò)對(duì)HVS性質(zhì)的探討,人們開(kāi)發(fā)出了能夠?qū)︼@示設(shè)備能力進(jìn)行良好補(bǔ)償(compensatingfordisplaylimitation)的色調(diào)映射算法。本節(jié)將介紹幾個(gè)這類(lèi)算法。

8.4.1光亮度和光度學(xué)(luminanceandPhotometry)

因?yàn)樯{(diào)映射算法通?;谌搜蹖?duì)明亮度的感知,所有大多數(shù)色調(diào)映射算子使用光亮度(luminance)單位,光亮度用來(lái)測(cè)量一個(gè)光譜功率分布對(duì)人類(lèi)觀察者而言的明亮程度。例如,光亮度可以用來(lái)說(shuō)明這樣的事實(shí):對(duì)于人類(lèi),有一定綠色波長(zhǎng)能量的SPD要比具有相同的藍(lán)色波長(zhǎng)能量的SPD看上去要明亮一些。

光亮度跟輻射亮度有緊密的聯(lián)系:給定一個(gè)光譜輻射亮度值,其光亮度值可以用一個(gè)簡(jiǎn)單的轉(zhuǎn)換公式計(jì)算出來(lái)。實(shí)際上,第5章所定義的輻射度學(xué)中的所有量在光度學(xué)里都有對(duì)應(yīng)。光度學(xué)(photometry)是研究可見(jiàn)的電磁輻射以及HVS對(duì)其感知的學(xué)問(wèn)。每一個(gè)輻射度學(xué)中的量都可以轉(zhuǎn)換成相應(yīng)的光度學(xué)中的量,而所依據(jù)的就是對(duì)光譜分布和光譜反應(yīng)曲線V(λ)的乘積進(jìn)行積分,其中光譜反應(yīng)曲線描述了人眼對(duì)不同波長(zhǎng)的相對(duì)敏感度。

我們用Y來(lái)表示光亮度,它跟光譜輻射亮度L(λ)的關(guān)系是:

Y=?λL(λ)V(λ)dλ

光亮度和光譜反應(yīng)曲線V(λ)跟顏色的XYZ表示有緊密的聯(lián)系(見(jiàn)第5.1.2節(jié))。CIE所選定的三刺激值曲線Y(λ)跟V(λ)成比例關(guān)系,使得:

Y=683?λL(λ)Y(λ)dλ

這樣一來(lái),給定了XYZ表示,就可以用一個(gè)比例因子而得到每個(gè)圖像像素的光亮度。光亮度的單位是坎德拉/平方米(cd/m2),坎德拉(candela)是跟輻射強(qiáng)度相對(duì)應(yīng)的色度學(xué)里的量。坎德拉/平方米又常被叫做尼特(nit)。下面是常見(jiàn)的一些光亮度值:

光亮度(nits)

600,000

地平線上的太陽(yáng)

120,000

60瓦燈泡

8000

晴朗的天空

100-1000

典型的辦公室

1-100

典型的計(jì)算機(jī)顯示器

1-10

街道上的照明

0.25

有云遮掩的月光

人眼有兩種類(lèi)型的感光細(xì)胞:桿體細(xì)胞和錐體細(xì)胞。桿體細(xì)胞有助于在較暗的環(huán)境(即微光水平:10-6到10cd/m2)中的視覺(jué)活動(dòng)。桿體細(xì)胞對(duì)顏色不敏感,并且不擅長(zhǎng)分辨微小的細(xì)節(jié)。而錐體細(xì)胞可以處理從10-2到108cd/m2的光照范圍(明視水平)。有三類(lèi)錐體細(xì)胞,分別對(duì)不同的波長(zhǎng)的光敏感。計(jì)算機(jī)顯示器通??梢燥@示的光亮度范圍大約是1到100cd/m2。

8.4.2光暈(Bloom)

在介紹色調(diào)映射算法之前,我們介紹一種可以欺騙HVS的技術(shù),使HVS看到顯示器上的圖像要比實(shí)際的圖像更明亮一些。如果人眼所看到的環(huán)境中的某一部分比其它部分明亮得太多,就會(huì)出現(xiàn)一種稱(chēng)為"光暈"(Bloom)的效果,即在明亮物體的周?chē)鷧^(qū)域有模糊的輝光。人們還不明白這其中的原因,但一般認(rèn)為這是光在人眼內(nèi)發(fā)生散射而產(chǎn)生的現(xiàn)象。計(jì)算機(jī)圖形學(xué)研究者們發(fā)現(xiàn)在圖像渲染中模擬這種效果會(huì)極大地提高圖像的真實(shí)感。當(dāng)圖像的某一部分出現(xiàn)這種輝光時(shí),HVS就很自然地感知到這部分圖像要比其它部分明亮許多。

作為一種選項(xiàng),我們所介紹的圖像管線可以對(duì)圖像施加bloom效果。這個(gè)效果所使用的濾波器是以實(shí)驗(yàn)為依據(jù)的,而非基于人類(lèi)視覺(jué)系統(tǒng)的模型,但是在實(shí)際應(yīng)用中效果良好。其基本的理念是使用一個(gè)非常寬的濾波器,可以作用到圖像中所有的像素上,并且快速衰減。因?yàn)檫@個(gè)濾波器有很寬的支撐,那些很亮的像素對(duì)周?chē)脑S多像素都有能量貢獻(xiàn)。因?yàn)樗p很快,就不會(huì)改變那些有相似亮度值的圖像區(qū)域,但非常亮的像素可以使低濾波權(quán)值失效并影響到其它像素。然后我們?cè)儆靡粋€(gè)用戶提供的權(quán)值,將這個(gè)bloom圖像跟原圖像合成。

該濾波器有兩個(gè)參數(shù):bloomRadius,指定濾波器覆蓋圖像的比例值;bloomWeight,指定用來(lái)混合bloom圖像和原圖像的權(quán)值。如果bloomRadius為0,濾波器就被視為關(guān)閉。在實(shí)際應(yīng)用中,使用大約為0.1或0.2的值是使用該濾波器很好的初始選擇。

<Possiblyapplybloomeffecttoimage>=

if(bloomRadius>0.f&&bloomWeight>0.f){

<Computeimagespaceextentofbloomeffect>

<Initializebloomfiltertable>

<Applybloomfiltertoimagepixels>

<Mixbloomeffectintoeachpixel>

<Freememoryallocatedforbloomeffect>

}

首先,要確定濾波器的寬度(以像素個(gè)數(shù)計(jì))。用bloomRadius乘以圖像x,y方向的分辨率的最大值,就得到這個(gè)值。

<Computeimagespaceextentofbloomeffect>=

intbloomSupport=Float2Int(bloomRadius*max(xResolution,yResolution));

intbloomWidth=bloomSupport

/2;

因?yàn)橐獙?duì)濾波器函數(shù)做很多次求值,有必要預(yù)先計(jì)算好一個(gè)表。這里的實(shí)現(xiàn)所用的是一個(gè)放射式對(duì)稱(chēng)的濾波函數(shù):

f(x,y)=(1-(x2+y2)1/2/d)4

其中d是濾波器的寬度。該濾波器是不可分的(即不可分離成兩個(gè)1D濾波函數(shù)的乘積),所以每個(gè)要輸出的像素需要濾波的像素個(gè)數(shù)等于濾波器寬度的平方,這樣一來(lái),預(yù)先計(jì)算好濾波值表就更有必要了。

<Initializebloomfiltertable>=

float*bloomFilter=newfloat[bloomWidth*bloomWidth];

for(inti=0;i<bloomWidth*bloomWidth;++i){

floatdist=sqrtf(float(i))/float(bloomWidth);

bloomFilter=powf(max(0.f,1.f-dist),4.f);

}

這里的實(shí)現(xiàn)先計(jì)算一個(gè)暫時(shí)的圖像來(lái)存放bloom貢獻(xiàn)值。我們不能在計(jì)算bloom值的時(shí)候更新原圖像,這一點(diǎn)很重要:因?yàn)檫@樣做會(huì)使用已經(jīng)累加過(guò)bloom值的像素值來(lái)計(jì)算附近像素的bloom值,從而得到錯(cuò)誤的結(jié)果。

<Applybloomfiltertoimagepixels>=

float*bloomImage=newfloat[3*nPix];

for(inty=0;y<yResolution;++y)

for(intx=0;x<xResolution;++x){

<Computebloomforpixel(x,y)>

}

}

為了計(jì)算bloom圖像中的像素值,先要找到所有的可能對(duì)其有貢獻(xiàn)值的那些像素。然后,對(duì)這些像素進(jìn)行濾波計(jì)算:

<Computebloomforpixel(x,y)>=

<Computeextentofpixelscontributingbloom>

intoffset=y*xResolution+x;

floatsumWt=0.;

for(intby=y0;by<=y1;++by)

for(intbx=x0;bx<=x1;++bx){

<Accumulatebloomfrompixel(bx,by)>

}

bloomImage[3*offset

]/=sumWt;

bloomImage[3*offset+1]/=sumWt;

bloomImage[3*offset+2]/=sumWt;

我們以當(dāng)前像素位置為中心,在每個(gè)方向上偏移,偏移量為濾波器寬度,就可以找到有貢獻(xiàn)值的像素范圍:

<Computeextentofpixelcontributingbloom>=

intx0=max(0,x-bloomWidth);

intx1=min(x+bloomWidth,xResolution-1);

inty0=max(0,y-bloomWidth);

inty1=min(y+bloomWidth,yResolution-1);

當(dāng)前像素并不參與bloom計(jì)算,因?yàn)槲覀円?jì)算的是其它像素對(duì)該像素的貢獻(xiàn)值。

<Accumlatebloomfrompixel(bx,by)>=

intdx=x-bx,dy=y-by;

if(dx==0&&dy==0)continue;

intdist2=dx*dx+dy*dy;

if(dist2<bloomWidth*bloomWidth){

intbloomOffset=bx+by*xResolution;

floatwt=bloomFilter[dist2];

sumWt+=wt;

for(intj=0;j<3;++j)

bloomImage[3*offset+j]+=wt*rgb[3*bloomOffset+j];

}

計(jì)算出bloom圖像之后,就可以用bloomWeight值跟原圖像進(jìn)行混合:

<Mixbloomeffectintoeachpixel>=

for(inti=0;i<3*nPix;++i)

rgb=Lerp(bloomWeight,rgb,bloomImage);

<Freememoryallocatedforbloomeffect>=

delete[]bloomFilter;

delete[]bloomImage;

8.4.3色調(diào)映射接口

色調(diào)重現(xiàn)的基本方法是求出一個(gè)將像素值映射到可顯示的范圍的比例變換函數(shù)。對(duì)于簡(jiǎn)單的色調(diào)映射算子而言,常常使用一個(gè)簡(jiǎn)單的作用于圖像中所有像素的函數(shù)。這類(lèi)算子被稱(chēng)為空間均勻(或全局)算子。它們給出了從圖像光亮度到顯示光亮度的單調(diào)映射。更復(fù)雜的方法所使用的函數(shù)會(huì)根據(jù)每個(gè)像素的亮度和附近像素的亮度而發(fā)生變化,稱(chēng)之為有空間變化的(或局部)算子,它們并不一定是單調(diào)映射。

有空間變化的算子會(huì)比空間均勻算子更具成效,這一點(diǎn)很有趣。這些算子之所以效果良好,是因?yàn)槿搜蹖?duì)局部的對(duì)比要比絕對(duì)的光亮度更敏感。由于這個(gè)特性,就可以對(duì)圖像中不同部分賦予不同的像素值,雖然這些圖像不同的部分原有相同的絕對(duì)光亮度值(卻被算子賦給了不同的值),但對(duì)于人類(lèi)觀察者而言,應(yīng)用算子后的結(jié)果并沒(méi)有什么不妥。

因此,許多色調(diào)映射算子的基本目標(biāo)就是要盡可能地在被顯示的圖像中保留局部對(duì)比,而不是保留絕對(duì)的亮度。在所有圖像(亮的,或暗的)區(qū)域中確保使用有足夠差別的顏色至關(guān)重要,觀察者這樣才能看到不同的顏色,并且保證大范圍的圖像強(qiáng)度不會(huì)被映射到相同的像素值上。所以說(shuō),一個(gè)在場(chǎng)景中比另一個(gè)物體明亮兩倍的物體在顯示器上并不一定也有兩倍的亮度。再重復(fù)一下,有對(duì)比的局部變化對(duì)人類(lèi)視覺(jué)系統(tǒng)來(lái)說(shuō)是最為重要的。

HVS的對(duì)光亮度變化的總體上的敏感性是根據(jù)適應(yīng)亮度(adaptionluminance)來(lái)變化的,我們用記號(hào)Ya來(lái)表示適應(yīng)亮度。適應(yīng)亮度在圖像不同的部分可以有不同的值。在下面的討論中,我們用顯示器適應(yīng)亮度(displayadaptionluminance,Yad)來(lái)表示人類(lèi)觀察者觀看計(jì)算機(jī)顯示器時(shí)的適應(yīng)亮度,用世界適應(yīng)亮度(worldadaptionluminance,yaw)來(lái)表示人觀察實(shí)際場(chǎng)景時(shí)的適應(yīng)亮度。

由于在昏暗環(huán)境下桿體細(xì)胞占主導(dǎo)地位,HVS在黑暗中的特性就全然不同。例如,對(duì)顏色的感知減弱了,所有的東西看上去只是不同程度的深灰色。還有,空間視敏度(spatialacuity)也被減弱了:在1000nits的適應(yīng)亮度下,HVS可以分辨50周/視角的空間細(xì)節(jié),而在0.001nits的情況下,只能分辨大約2.2周/視角。用于微光水平的色調(diào)重現(xiàn)算子常常用一些模糊技術(shù)來(lái)模擬這個(gè)效果。

所有的色調(diào)映射算子都是從ToneMap基類(lèi)繼承下來(lái)的,該基類(lèi)提供了TopMap::Map()接口函數(shù):

<ToneMapDeclarations>=

classToneMap{

public:

<ToneMapInterface>

};

TopMap::Map()函數(shù)的參數(shù)包括一個(gè)指向圖像像素光亮度值的數(shù)組指針,圖像分辨率,顯示器可顯示的最大光亮度值。該函數(shù)負(fù)責(zé)為每個(gè)像素計(jì)算出一個(gè)比例因子,并把它存放在scale數(shù)組中。所計(jì)算出的比例因子要使被因子乘過(guò)的像素光亮度值位于[0,maxDisplayY]范圍之內(nèi)。

<ToneMapInterface>=

virtualvoidMap(constfloat*y,intxRes,intyRes,

floatmaxDisplayY,float*scale)const=0;

如果用戶為ApplyImaingPipeLine()提供了色調(diào)映射算子,我們就用下列片段來(lái)實(shí)施色調(diào)重現(xiàn)操作。首先,要為每個(gè)像素計(jì)算光亮度值,然后為每個(gè)像素計(jì)算比例因子,然后對(duì)圖像進(jìn)行比例變換。顯示器所能顯示的最大光亮度值maxDisplayY被設(shè)為固定值100.f。

<Applytonereproductiontoimage>=

Tone*toneMap=NULL;

if(toneMapName)

toneMap=MakeToneMap(toneMapName,

toneMapParams?*toneMapParams:ParamSet());

if(toneMap){

floatmaxDisplayY=100.f;

float*scale=newfloat[nPix],*lum=newfloat[nPix];

<Computepixelluminancevalues>

toneMap->Map(lum,xResolution,yResolution,

maxDisplayY,scale);

<Applyscaletopixelsfortonemappingandmapto[0.1]>

delete[]scale;

delete[]lum;

}

ApplyImagingPipeLine()的yWeight變量給出了計(jì)算RGB像素值的權(quán)值。如果它為NULL,就用標(biāo)準(zhǔn)值。

<Computepixelluminancevalues>=

floatstdYWeight[3]={0.212671f,0.715160f,0.072169f};

if(!yWeight)yWeight=stdYWeight;

for(inti=0;i<nPix;++i)

lum=683.f*(yWeight[0]*rgb[3*i]+

(yWeight[1]*rgb[3*i+1]+

(yWeight[2]*rgb[3*i+2]);

因?yàn)樯{(diào)映射算子返回的比例因子使像素光亮度值lum落在范圍[0,maxDisplayY]之間,而顯示設(shè)備并不用光亮度值為輸入值,所以我們還要把結(jié)果再縮比為[0,1]之間的值。另外,乘683這個(gè)因子很重要,因?yàn)樯{(diào)映射算子假定該因子已經(jīng)被乘到像素值上了。

<Applyscaletopixelsfortonemappingandmapto[0,1]>=

floatdisplayTo01=683.f/MaxDisplayY;

for(inti=0;i<xResolution*yResolution;++i){

rgb[3*i]*=scale*displayTo01;

rgb[3*i+1]*=scale*displayTo01;

rgb[3*i+2]*=scale*displayTo01;

}

8.4.4映射最大值為白色

最直截了當(dāng)?shù)纳{(diào)重現(xiàn)算子就是“最大值到白色”算子。它對(duì)所有像素進(jìn)行循環(huán),尋找最大的光亮度值,并把所有像素值進(jìn)行均勻地映射,使得最大光亮度值被映射到顯示器的最大光亮度值。

<MaxWhiteOpDeclarations>=

classMaxWhiteOp:publicToneMap{

public:

<MaxWhiteOpPublicMethods>

};

<MaxWhiteOpPublicMethods>=

voidMap(constfloat*y,intxRes,intyRes,floatmaxDisplayY,float*scale)const{

<Computemaximumluminanceofallpixels>

floats=maxDisplayY/maxY;

for(inti

=0;i<xRes*yRes;++i)

scale=s;

<Computemaximumluminanceofallpixels>=

floatmaxY=0;

for(inti=0;i<xRes*yRes;++i)

maxY=max(maxY,y);

在實(shí)際應(yīng)用中,該算子有兩個(gè)缺點(diǎn)、首先,它根本沒(méi)有考慮人類(lèi)視覺(jué)系統(tǒng):如果將場(chǎng)景中的光加亮100倍再重新渲染,使用該算子所得到的結(jié)果全然相同。其次,如果有很少的幾個(gè)極亮的像素,也會(huì)導(dǎo)致圖像的其它部分非常暗淡,以至于看不見(jiàn)。但是,它對(duì)那些沒(méi)有太多的動(dòng)態(tài)范圍的圖像而言還是效果不錯(cuò)的,也可以作為一種基準(zhǔn),來(lái)評(píng)估更復(fù)雜的算子所帶來(lái)的改進(jìn)。

8.4.5基于對(duì)比的比例因子

我們所介紹的下一個(gè)算子注重保留圖像中的亮度對(duì)比。它是由GregWard(1994a)研發(fā)出來(lái)的。該算子基于那些研究HVS并開(kāi)發(fā)出仿真模型的研究者們的工作,所使用的模型描述了在給定適應(yīng)亮度的情況下觀察者所能察覺(jué)到的最小的光亮度變化--最小可覺(jué)差(JND,justnoticeabledifference)。適應(yīng)亮度越大,能夠被察覺(jué)到的光亮度變化也就越大。該算子是這樣設(shè)置圖像光亮度值的:即試圖將被顯示圖像的一個(gè)JND和實(shí)際環(huán)境中的一個(gè)JND相對(duì)應(yīng)。

這個(gè)均勻比例因子是這樣試圖保留對(duì)比可見(jiàn)性的--給定一個(gè)原圖像中剛好能被觀察者區(qū)分開(kāi)來(lái)的區(qū)域,它試圖對(duì)顯示像素值進(jìn)行比例變換,使得觀看顯示器的人剛好分辨出兩個(gè)像素值的不同。一個(gè)能夠增大JND的比例因子是對(duì)寶貴的顯示動(dòng)態(tài)范圍的一種浪費(fèi),而減小JND的比例因子又會(huì)丟失掉視覺(jué)上可以觀察出的特征。

研究者們發(fā)現(xiàn),對(duì)于給定的一個(gè)在明視范圍中的一個(gè)適應(yīng)亮度,最小的可見(jiàn)的光亮度變化可以由下面的模型給出:

ΔY(Ya)=0.0594(1.219+(Ya)0.4)2.5

該算子的比例因子s要滿足:

ΔY(Yad)=sΔY(Yaw)

其中Yad是顯示適應(yīng)亮度,而Yaw是世界適應(yīng)亮度。

解出s值,就得到:

s=((1.219+(Yad)0.4)/(1.219+(Yaw)0.4))2.5

<ContrastOpDeclarations>=

classContrastOp(floatday){displayAdapationY=day;}

voidMap(constfloat*y,intxRes,intyRes,floatmaxDisplayY,float*scale)const;

floatdisplayAdaptionY;

}

<ContrastOpMethodDefinitions>=

voidMap(constfloat*y,intxRes,intyRes,floatmaxDisplayY,float*scale)const

{

<Computeworldadaptationluminance>

<Computecontrast-preservingscalefactor,s>

for(inti=0;i<xRes*yRes;++i)

scale=s;

}

有一個(gè)沒(méi)有解決的問(wèn)題就是如何計(jì)算世界適應(yīng)亮度Yaw。最為理想的情況是,該值的計(jì)算基于觀察者所看到的那一部分場(chǎng)景和觀看的時(shí)間(HVS要花時(shí)間適應(yīng)亮度的變化)。由于缺乏這類(lèi)信息,算子只是計(jì)算原圖像所有光亮度值的log平均值。使用log平均值而非一般平均值的原因是防止很小的明亮區(qū)域?qū)D像的其它部分起壓倒性的作用。

<Computeworldadaptationluminance,Ywa>=

floatYwa=0;

for(inti=0;i<xRes*yRes;++i)

if(y>0)Ywa+=logf(y);

Ywa=expf(Ywa/(xRes*yRes));

利用上面的公式可以算出比例因子s:

<Computecontrast-preservingscalefactor,s>=

floats=powf((1.219f+powf(displayAdaptationY,0.4f))/

(1.219f+powf(Ywa,0.4f)),2.5f);

該算子一般情況下工作良好,但在維持明亮區(qū)域的細(xì)節(jié)方面卻麻煩多多。這也難怪,因?yàn)槿魏问褂脝我槐壤蜃拥乃阕佣加羞@個(gè)缺點(diǎn)。該算子適合于室內(nèi)場(chǎng)景,而且計(jì)算效率高。

8.4.6可變化的適應(yīng)亮度

前面提到過(guò),我們常常可以使用一個(gè)可變的比例因子對(duì)顯示器的動(dòng)態(tài)范圍加以更好地利用。這里我們將實(shí)現(xiàn)一個(gè)色調(diào)重現(xiàn)算子,使之特別適用于具有許多亮度變化等級(jí)的高對(duì)比的場(chǎng)景。它計(jì)算一個(gè)在整個(gè)圖像上面平滑變化的局部適應(yīng)亮度。然后使用這個(gè)局部適應(yīng)亮度和一個(gè)保持亮度對(duì)比的色調(diào)重現(xiàn)算子來(lái)計(jì)算一個(gè)比例因子,其方法跟前面定義的ContrastOp算子相似。

計(jì)算空間上可變的局部適應(yīng)亮度的困難之處在于,在非常明亮的區(qū)域和非常暗淡的區(qū)域之間的邊界上,很容易產(chǎn)生人為的缺陷。如果色調(diào)重現(xiàn)算子使用了受明亮像素影響的適應(yīng)亮度,那么暗淡的像素值就被映射為黑色,就會(huì)在邊界上產(chǎn)生光圈效應(yīng)(haloeffect)。

所以,更好的方法是要保證暗淡的像素所使用的適應(yīng)亮度是僅僅基于附近的暗淡像素的。本節(jié)所介紹的算子使用了一種圖像處理技術(shù)來(lái)探測(cè)那些適應(yīng)亮度相差過(guò)大的區(qū)域之間的邊界。在那些適應(yīng)亮度變化緩慢的局部區(qū)域,該算子使用一個(gè)局部的可以保持亮度對(duì)比的比例因子--即明亮的區(qū)域不至于被變?yōu)榘咨档膮^(qū)域不至于被映射為黑色。

<HighContrastOpDeclarations>=

classHighContrastOp:publicToneMap{

public:

voidMap(constfloat*y,intxRes,intyRes,floatmaxDisplayY,float*scale)const;

private:

<HighContrastOpUtilityMethods>

};

這個(gè)色調(diào)映射函數(shù)基于亮度感知門(mén)限(ThresholdVersusIntensity,TVI)函數(shù),由它可以得出給定適應(yīng)水平的最小可覺(jué)差,TVI(Ya)。在本質(zhì)上,它跟ContrastOp所用的JND函數(shù)相似,但是它基于更復(fù)雜的人類(lèi)視覺(jué)系統(tǒng)模型上的,其中包括在微光水平下的反應(yīng)模型。

從TVI函數(shù)出發(fā),我們可以定義感知容量(perceptualcapacity),即對(duì)于給定的適應(yīng)水平Y(jié)a,光亮度范圍(Ya,Yb)所覆蓋的JND數(shù):

(Ya

-Yb)/TVI(Ya)

稍后我們會(huì)利用上述關(guān)系式來(lái)重新映射圖像的局部區(qū)域,使得被顯示的圖像仍然保持了感知容量。

為了快速地計(jì)算出一對(duì)亮度值的感知容量,我們定義下面的輔助容量函數(shù)C(Y):

C(Y)=?Y0(1/TVI(Y'))dY'

其中我們做了這樣的近似:對(duì)于給定光亮值,計(jì)算感知容量微分的適應(yīng)水平值等于該光亮度值。這樣,C(Ya)-C(Yb)就是從Ya到Y(jié)b的感知容量。

Ashikhmin對(duì)廣泛使用的TVI函數(shù)做了某些簡(jiǎn)化,使得更容易計(jì)算C(Y),其結(jié)果如下:

<HighContrastOpUtilityMethods>=

staticfloatC(floaty){

if(y<0.0034f)

returny/0.0014f;

elseif(y<1)

return2.4483f+log10f(y/0.0034f)/0.4027f;

elseif(y<7.2444f)

return16.563f+(y-1)/0.4027f;

else

return32.0693f+log10f(y/7.2444f)/0.0556f;

}

有了C(Y)函數(shù)后,我們就可以用給定的光亮度值Y作為參數(shù),確定它與圖像中的最小光亮度之間有多少個(gè)JND:

C(Y)-C(Ymin)

我們也可以計(jì)算出該JND數(shù)占整個(gè)圖像的JND數(shù)的比率:

(C(Y)-C(Ymin))/(C(Ymax)-C(Ymin))

我們就會(huì)知道這個(gè)世界光亮度要被映射到顯示光亮度范圍的位置。這樣,總體上的色調(diào)映射算子如下:

T(Y)=Ymaxd(C(Y)-C(Ymin))/(C(Ymax)-C(Ymin))

<HighContrastOpUtilityMethods>+=

staticfloatT(floaty,floatCYmin,floatCYmax,floatmaxDisplayY){

returnmaxDisplay*(C(y)-CYmin)/(CYmax-CYmin);

}

現(xiàn)在我們可以定義色調(diào)映射主函數(shù)了。該函數(shù)計(jì)算圖像中所有像素的最大(Ymax)、最小光亮度值(Ymin)。在計(jì)算適應(yīng)亮度時(shí),為了加快搜索速度,該算法還要建立一個(gè)金字塔型的圖像數(shù)據(jù)結(jié)構(gòu),其中原圖像要被逐步地被濾波到更低分辨率的自身拷貝中。當(dāng)算子對(duì)所有像素進(jìn)行循環(huán)并計(jì)算每個(gè)像素的比例因子時(shí),就要用到這個(gè)金字塔結(jié)構(gòu)。

<HighContrastOpMethodDefinitions>=

voidMap(constfloat*y,intxRes,intyRes,floatmaxDisplayY,float*scale)const{

<Findminimumandmaximumimageluminances>

<Buildluminanceimagepyramid>

<Applyhigh-contrasttonemappingoperator>

}

<Findminimumandmaximumimageluminances>=

floatminY=y[0],maxY=y[0];

for(inti=0;i<xRes*yRes;++i){

minY=min(minY,y);

maxY=max(maxY,y);

}

floatCYmin=C(minY),CYmax=C(maxY);

前面所介紹的計(jì)算局部適應(yīng)亮度的大多數(shù)方法使用了原圖像的一個(gè)模糊版本,這樣會(huì)引起光圈效應(yīng)。這里所實(shí)現(xiàn)方法的背后的洞見(jiàn)是,適應(yīng)亮度不應(yīng)該基于圍繞像素(x,y)的大小固定的區(qū)域,而應(yīng)該是一個(gè)可變的區(qū)域:只要光亮度在局部大致保持相等,就可以擴(kuò)張這個(gè)區(qū)域,直到遇到明顯的光亮度變化為止。這就擁有了兩方面的優(yōu)點(diǎn):當(dāng)光亮度變化緩慢時(shí),就可以為一個(gè)較大的區(qū)域計(jì)算適應(yīng)亮度,就給出了遠(yuǎn)離具有高對(duì)比度的特征的適應(yīng)亮度的平滑變化。當(dāng)對(duì)比度快速變化時(shí),也能夠探測(cè)出這種變化,通過(guò)計(jì)算更局部的適應(yīng)亮度來(lái)避免人為的缺陷。

圖像處理的一個(gè)標(biāo)準(zhǔn)技術(shù)是定義像素的局部對(duì)比度lc(x,y),即像素在兩個(gè)被模糊的圖像版本中的像素值差,其中一個(gè)版本的濾波器寬度是另一個(gè)的兩倍:

lc(s,x,y)=(Bs(x,y)-B2s(x,y))/Bs(x,y)

這里s是用于模糊圖像的濾波器寬度(以像素個(gè)數(shù)計(jì)),Bs(x,y)是像素(x,y)在模糊圖像中的值。我們希望找到圍繞每個(gè)像素(x,y)的最大局部范圍半徑s,使得|lc(s,x,y)|小于某個(gè)固定值。當(dāng)它比這個(gè)固定值大時(shí),就說(shuō)明我們超過(guò)了可以接受的局部對(duì)比度。找到了滿足條件的s后,就可以用下面的公式計(jì)算適應(yīng)亮度:

Ya(x,y)=Bs(x,y)

為了快速地找到在模糊圖像中的像素值Bs(x,y),該算子用MIPMap類(lèi)創(chuàng)建一個(gè)圖像金字塔,該類(lèi)在第11.4.2節(jié)中被介紹。在本節(jié)中,我們只需知道它可以精確而且有效地計(jì)算出任意s值所對(duì)應(yīng)的Bs(x,y)。

<Buildluminanceimagepyramid>=

MIPMap<float>pyramid(xRes,yRes,y,false,4.f,TEXTURE_CLAMP);

下一步計(jì)算每個(gè)像素的適應(yīng)亮度和比例因子。注意我們需要把離散像素坐標(biāo)轉(zhuǎn)換為連續(xù)的像素坐標(biāo),用來(lái)MIPMap查找。

<Applyhigh-contrasttonemappingoperator>=

for(inty=0;y<yRes;++y){

floatyc=(float(y)+.5f)/float(yRes);

for(intx=0;x<xRes;++x){

floatxc=(float(x)+.5f)/float(xRes);

<Computelocaladaptationluminanceat(x,y)>

<Applytonemappingbasedonlocaladaptationluminance>

}

}

為了計(jì)算適應(yīng)亮度,該函數(shù)在模糊圖像中查找像素值,并計(jì)算局部對(duì)比度函數(shù)值lc。如果該值大于maxLocalContrast,0.5(一個(gè)任意常量,根據(jù)經(jīng)驗(yàn)而定),適應(yīng)亮度就設(shè)置為稍微小一些的區(qū)域的平均值,并結(jié)束循環(huán)。否則,模糊半徑就增加一個(gè)像素的跨度dwidth,并計(jì)算lc的新值。一旦該過(guò)程到達(dá)了一個(gè)很大的模糊半徑卻沒(méi)有找到足夠大的對(duì)比度,就終止循環(huán)。

<Computelocaladaptationluminance>=

floatdwith=1.f/float(max(xRes,yRes));

floatmaxWidth=32.f/float(max(xRes,yRes));

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論