版權(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025標(biāo)準(zhǔn)工程工程合同
- 2025住房裝修設(shè)計(jì)合同范本
- 2025無(wú)固定期限勞動(dòng)合同解除條件有些
- 2025項(xiàng)目場(chǎng)外分銷(xiāo)代理合同
- 2025有關(guān)學(xué)術(shù)著作出版合同樣本
- cad綜合實(shí)訓(xùn)教案
- 4年級(jí)下冊(cè)體育進(jìn)度計(jì)劃安排
- 酒吧臨時(shí)演員招聘協(xié)議
- 婚慶公司前臺(tái)接待協(xié)議
- 商業(yè)園區(qū)廣場(chǎng)施工合同
- 產(chǎn)業(yè)園運(yùn)營(yíng)合作協(xié)議
- 16J607-建筑節(jié)能門(mén)窗
- 理解詞語(yǔ)句子的方法PPT
- 作文開(kāi)頭與結(jié)尾PPT課件ppt(共42張PPT)
- 重癥醫(yī)學(xué)科運(yùn)用PDCA循環(huán)提高消毒棉簽開(kāi)啟時(shí)間標(biāo)注的執(zhí)行率品管圈成果匯報(bào)
- 云南面向東南亞、南亞區(qū)域物流系統(tǒng)優(yōu)化研究的開(kāi)題報(bào)告
- 高效課堂教學(xué)流程和課堂常規(guī)公開(kāi)課一等獎(jiǎng)市賽課獲獎(jiǎng)?wù)n件
- 浙江寧波廣播電視集團(tuán)發(fā)射中心招考聘用筆試參考題庫(kù)答案解析
- 2024年航天知識(shí)總結(jié)
- 公立醫(yī)院章程范本(中國(guó)醫(yī)院協(xié)會(huì)2019版)
- 江蘇小高考(物理化學(xué)生物)真題及答案
評(píng)論
0/150
提交評(píng)論