引言
一般來說,照相產生缺陷的原因主要有對焦不准、光圈選擇不合適以及暴光不足等幾種情況,前兩種情況的偶然性較強,而曝光不足雖也具有一定的偶然性但對於某些特殊情況比如暗中偵察等不方便使用閃光燈的場合則較為普遍。因此,有必要對曝光不足的照片提出一種比較通用的照片修復處理方法。
修復方法的設計
根據照相的一般原理:物體反射的光線經過透鏡投影到膠片上,膠片上的感光顆粒根據光線的強弱做出不同程度的分解從而顯現出不同的顏色。當沒有使用閃光燈或暴光時間過短而造成照片曝光不足時,照射在膠片上的光線強度不夠或照射時間過短均會使感光顆粒分解不完全,沖洗出來的照片就會發暗,下圖便是一幅實際的曝光不足照片,可以看出整幅照片很昏暗,除了位於左下的三、四個大字隱約可見外,幾乎看不出該照片到底拍了些什麼。
雖然感光顆粒由於曝光不足而使照片顯地昏暗,但由於同一膠片上的感光顆粒的感光程度仍是同真實物體所反射光線成比例的,因此在膠片上實際仍保存了真實物體的大部分信息,只是由於曝光不足造成的分解不完全使沖洗出的照片灰度分布過於集中,並超出了人眼對灰度級的分辨程度,才造成了視覺上的不可見。通過程序對照片做灰度分布統計(如下圖所示)也可以看出照片的灰度分布主要集中在0 ~ 100之內,這顯然是很暗的了。
基於以上幾點認識,在進行照片修復時只要將過於集中的灰度分布按照一定的規則將其均勻分布於整個灰度區間即可在視覺上得到相當程度的改善。在這方面的處理方法中,灰度均衡化和灰度規定化(含單映射和組映射兩種規則)應用較為廣泛,但後者需要根據圖象的不同人為規定好適當的預期灰度分布規則才能得到滿意的效果,如果預期灰度分布規定不當則處理效果會很差,因此後者的通用性不好。而前者在處理時則只需要將當前的灰度分布均衡的分布於整個灰度區間即可,雖然對於某一幅特定的圖象處理效果可能不及灰度規定化,但通用性卻要好的多,對任意的圖象均可獲得相當不錯的處理效果。
灰度均衡化處理算法
灰度均衡化處理對圖像的增強過程可用增強函數t = EH(s)來表示,其中t和s分別表示目標圖象和原始圖象上的像素點(x,y),在進行均衡化處理時對增強函數EH需要滿足兩個條件:增強函數EH(s)在0≤s≤255的范圍內是一個單調遞增函數,這個條件保證了在增強處理時沒有打亂原始圖像的灰度排列次序。另一個需要滿足的條件是對於0≤s≤255應當有0≤EH(s)≤255,它保證了變換過程灰度值的動態范圍的一致。同樣的,對於反變換過程s=EH-1(t),在0≤t≤1時也必須滿足上述兩個條件。累計分布函數(cumulative distribution function,CDF)即是滿足上述條件的一種,通過該函數可以完成s到t 的均勻分布轉換。此時的增強轉換方程為:
tk = EH(sk) = (ni/n) = ps(si) ,(k=0,1,2,……,255)
上述求和區間為0到k,根據該方程可以由源圖像的各像素灰度值直接得到直方圖均衡化後各像素的灰度值。在實際處理變換時,一般先對原始圖像的灰度情況進行統計分析,並計算出原始直方圖分布,然後根據計算出的累計直方圖分布tk按式tk=[(N-1)* tk+0.5]對其取整並得出源灰度sk到tk的灰度映射關系,其中N為灰度的級數。在重復上述步驟得到所有的源圖像各灰度級到目標圖像各灰度級的映射關系後按照新的映射關系對源圖像各點像素進行灰度轉換即可完成對源圖的直方圖均衡化。
照片修復程序的實現
根據上述算法就可以對照片進行均衡化處理了。對於照片一般是將其當作DIB(設備無關位圖)來處理的,在將其裝載到內存並獲取到其HDIB 型句柄 hDIB後,需要預先獲取該位圖的一些相關參數以便後續使用:
// 鎖定DIB並返回指向DIB的指針
LPSTR lpDIB = (LPSTR) ::GlobalLock((HGLOBAL) hDIB);
// 找到DIB圖像象素起始位置並返回指向DIB象素指針
LPSTR lpDIBBits = m_clsDIB.FindDIBBits(lpDIB);
// 獲取DIB的寬度
LONG lWidth = m_clsDIB.DIBWidth(lpDIB);
// 獲取DIB的高度
LONG lHeight = m_clsDIB.DIBHeight(lpDIB);
根據算法設計,首先需要對原始位圖進行灰度統計並對灰度分布密度進行計算,對於24位真彩位圖,一個象素是由三個字節(R、G、B三顏色分量)組成的,因此需要分別對各顏色分量單獨進行統計,同樣,在灰度均衡處理時也是各自獨立進行的。
for (i = 0; i < lHeight; i ++) // 對各像素進行灰度轉換
{
for (j = 0; j < lWidth * 3; j ++)
{
// 對各像素進行灰度統計
unsigned char R = *((unsigned char *)lpDIBBits + lWidth * 3 * i + j);
nNs_R[R]++; j++;
unsigned char G = *((unsigned char *)lpDIBBits + lWidth * 3 * i + j);
nNs_G[G]++; j++;
unsigned char B = *((unsigned char *)lpDIBBits + lWidth * 3 * i + j);
nNs_B[B]++;
}
}
for(i=0;i<256;i++) // 計算灰度分布密度
{
fPs_R[i] = nNs_R[i] / (lHeight * lWidth * 1.0f);
fPs_G[i] = nNs_G[i] / (lHeight * lWidth * 1.0f);
fPs_B[i] = nNs_B[i] / (lHeight * lWidth * 1.0f);
}
接下來就要根據前面計算得到的各顏色分量的灰度分布密度來計算R、G、B三分量各自灰度級的累計直方圖分布,並對其進行取整以得出從源到目標圖像的灰度映射關系表。這一部分實際也是本文的核心,根據這一步計算得到的映射關系表就可以確定出源圖象的某個灰度級在目的圖象中對應於哪個新的灰度:
for(i = 0; i < 256; i++)
{
//計算累計直方圖分布
if(i == 0)
{
temp_r[0] = fPs_R[0];
temp_g[0] = fPs_G[0];
temp_b[0] = fPs_B[0];
}
else
{
temp_r[i] = temp_r[i-1] + fPs_R[i];
temp_g[i] = temp_g[i-1] + fPs_G[i];
temp_b[i] = temp_b[i-1] + fPs_B[i];
}
//累計分布取整,nNs_R[]、nNs_G[]、nNs_B[]保存有計算出來的灰度映射關系
nNs_R[i] = (int)(255.0f * temp_r[i] + 0.5f);
nNs_G[i] = (int)(255.0f * temp_g[i] + 0.5f);
nNs_B[i] = (int)(255.0f * temp_b[i] + 0.5f);
}
最後需要將變換後的結果保存到DIB中,這部分處理過程相對比較簡單,只需根據計算出來的映射關系表按圖索骥,就可把原始密集分布的灰度值映射到經過均衡化的新灰度級上,從而擴大了灰度級之間的距離,改善了視覺效果:
for (i = 0; i < lHeight; i ++)
{
for (j = 0; j < lWidth * 3; j ++)
{
//對R分量進行灰度映射(均衡化)
unsigned char R = *((unsigned char *)lpDIBBits + lWidth * 3 * i + j);
*((unsigned char *)lpDIBBits + lWidth * 3 * i + j) = nNs_R[R];
j++;
//對G分量進行灰度映射(均衡化)
unsigned char G = *((unsigned char *)lpDIBBits + lWidth * 3 * i + j);
*((unsigned char *)lpDIBBits + lWidth * 3 * i + j) = nNs_G[G];
j++;
//對B分量進行灰度映射(均衡化)
unsigned char B = *((unsigned char *)lpDIBBits + lWidth * 3 * i + j);
*((unsigned char *)lpDIBBits + lWidth * 3 * i + j) = nNs_B[B];
}
}
下圖是經過上述方法處理而得到的修復圖象,從修復後的照片可以清晰地看到條幅上的六個大字以及照片上的其他一些諸如印章、落款等細節。對修復後的照片進行灰度分布統計,可從右下的灰度分布圖中看到原本密集分布在0 ~ 100 之間的象素灰度分布現在已被均勻擴展到40 ~ 255之間的區域,而且由於高亮度象素的分布密度相對較大,因此經過修復的照片圖象整體顯得較為明亮,也即對拍照時由於缺乏閃光燈而造成的暴光不足等缺陷做了成功的彌補。
結論
本文通過對曝光不足的原始照片采取灰度均衡化處理而使其在一定程度上得到了修復,使一些原本看不見的細節在經過修復處理後變的清晰可見。經筆者多次實驗,本文所介紹的方法對於曝光不足的照片有很好的修復效果,同時對從事圖像開發、數碼相片處理軟件的開發人員有很好的實用價值。本文給出的程序代碼在Windows 98下由Microsoft Visual C++ 6.0編譯通過。