关于自动白平衡的算法问题

上一次写PHOTOSHOP教程的时候,感觉对白平衡的理解相当的肤浅。今天看到有人又在论坛上问到这个问题,于是搜了搜,居然搜到一篇好文。摘引如下:
白平衡

白平衡并不是数码摄影特有的东西,胶片也有白平衡,只不过胶片的白平衡是预设的,如钨丝灯型胶卷在钨丝灯光源的照射下拍出的照片就可以有正确的白平衡。换句话说,在胶片时代,摄影师根据光源的不同而选择不同的胶片就是在“调节”白平衡了。但数码技术允许我们后期几乎不受限制地的白平衡,无疑它要灵活得多,给摄影师提供了更多的创作手段。

白平衡的本质是让白色的物体在任何颜色的光源下都显示为白色。这一点对人眼来说很容易办到,因为人眼有自适应的能力,只要光源的色彩不超出一定的限度,就可以自动还原白色。但相机就不同了,无论是图像传感器还是胶卷都会记录光源的颜色,白色的物体就会带上光源的颜色,白平衡所要做的就是把这个偏色去掉。

自动白平衡是一个很复杂的问题,目前还没有一个万能的方法可以解决所有场景的白平衡问题。有些统计数据表明大部分场景的颜色平均值是中性的,即没有颜色【见注一】。一种自动白平衡算法就是依据这个统计数据设计的,它计算出图像颜色的均值,再算出均值和同等亮度的中性灰的差别,这个差别就是偏色值,对每个像素的颜色值用这个偏色值校正就得到正确的自动白平衡了。网上有一个开源的RAW处理软件——dcraw——就是用这种方法自动校正白平衡的(要知道dcraw有多牛,你只要知道早期很多商业软件、包括Adobe Camera Raw都是基于它的源代码的就够了)。

自动白平衡的问题是有时会给出同拍摄者希望相反的结果,比如拍摄一个金色阳光的早晨的场景,自动白平衡可能会算出光线偏红黄而减少照片中的红黄,结果我们美丽的朝阳变成了苍白的阴天。同样,如果一个场景中某种颜色占了很大的比重,如大面积的緑树,相机的自动白平衡算法也有可能被糊弄从而产生不可预知的结果。

问题的根本是我们有时候想保留光源的色彩(如日落、日出),有时候则想去掉光源的色彩,再牛的软件也不知道拍摄者的意图是什么,因此所有的数码单反都允许用户预设白平衡光源,如晴天、多云、日光灯等,拍摄者只要根据不同的光源预设不同的白平衡值就可以得到很好的色彩还原了。对于户外的风光摄影,很多人都喜欢偏暖的色调。胶片时代的摄影师通常用一些暖色的滤镜(如红、黄滤镜等)来解决这个问题,在数码时代有些摄影师则建议把白平衡一直设置在多云上,实际上我的很多风光片都是这么设置的。

预设白平衡可以较好地还原色彩,可以满足我们的大多数要求。我们有时候希望更准确的色彩还原,不同厂商的预设白平衡也可能并不相同,,这时候就需要手动调整白平衡了。手动调整白平衡的原理很简单,我们只要告诉相机或图像处理软件图片中的某个物体是中性的就可以了,软件会根据实际拍摄的物体的颜色值自动地计算出光源的颜色,进而调整整个画面的色彩。一种常见的手动调整白平衡方法是在拍摄的场景中放置一块白卡或灰卡,后期告诉软件它是中性的。

有人可能会问,我们应该用白卡还是灰卡?理论上两者都可以,只要白卡是真正的中性色,灰卡是真正的中性灰。实际使用时,很多市面上的灰卡(如柯达灰卡)并不是中性灰的,这是因为这些灰卡是给测光用的,只要保证它们反射18%的入射光就行了,而不用考虑它们是否是真正的中性灰。目前市面上有售专门为数码白平衡设计的白卡,它们是真正的中性的,如果需要的话,是个不错的选择。我推荐使用白卡手动调整白平衡的另外一个理由是白色的曝光值高(假设拍摄者能正确地曝光),曝光值高的像素有更好的信号和噪声的比例,因而得到的感光数据能更好地用来测试白平衡。

说到这里,是不是我们解决了所有的白平衡问题? Not so fast!最后我要提一下的是白板法仅适用于光照均匀的情形。设想一下,一位拍摄者站在阴影底下拍摄一座阳光照射的大山,这时白板法显然不行,因为拍摄者和拍摄物所处的光源不一样。在这种情况下,没有什么好的机械的手动白平衡调整方法,只有靠记忆后期调整光源的色温了。

【注一】在色彩科学里,中性色是指没有其它颜色,而只有灰色、白色或黑色的颜色。在计算机里面,红(R)、绿(G)、蓝(B)三个值相等就是中性色,白色的(R,G,B)是(255,255,255),是一种中性色;黑色是(0,0,0),也是一种中性色。 不幸的是,我们常常看到中性灰的提法,似乎白和黑不是中性色,这是不对的。

再附上两种处理原代码:
先是VC的
BYTE RGB[3]   =   {0},   YCbCr[3]   =   {0};
DOUBLE dbHSV[3]   =   {0},   dbStdRGB[3]   =   {0},   dbStdYCbCr[3]   =   {0};
PWBRESULTTYPE   pResult   =   &(pResultInfo->WBResult);
DWORD dwBlockSize   =   pResultInfo->Setting.dwBlockSize;
DWORD dwPicture   =   pResultInfo->DocInfo.BmpInfo.bmiHeader.biWidth;
DWORD dwDataSize   =   GETPICTUREWIDTH(dwPicture);
DWORD dwHeight   =   pResultInfo->DocInfo.BmpInfo.bmiHeader.biHeight;
CRect       rect;

rect.top   =   pResultInfo->Setting.ptArray.x;
rect.left   =   pResultInfo->Setting.ptArray.x;
rect.bottom   =   rect.top   +   dwBlockSize;
rect.right   =   rect.left   +   dwBlockSize;

//   calcult   mean   RGB   and   standard   RGB
CalculateRGB(pResultInfo->DocInfo.BmpInfo,   pResultInfo->DocInfo.lpFileData,   rect,   RGB);
pResult->Blue   =   RGB[2];
pResult->Green   =   RGB[1];
pResult->Red   =   RGB[0];

//   calculate   YCbCr   and   standard   luminance
CalculateYCbCr(pResultInfo->DocInfo.BmpInfo,   pResultInfo->DocInfo.lpFileData,   rect,   YCbCr);
pResult->Luminance   =   YCbCr[0];

pResult->dbRG   =   (RGB[0]*1.0)/RGB[1];
pResult->dbGG   =   1;
pResult->dbBG   =   (RGB[2]*1.0)/RGB[1];

再是VB的。
Public   Function   AutoColorPoise(mBitmap   As   cDIB,   AutoInfo   As   Label)   As   Boolean
Dim   i   As   Long,   j   As   Long
Dim   JumpLines   As   Long,   JumpBits   As   Long,   StepLines   As   Long,   StepBits   As   Long
Dim   MeanColor(0   To   2)   As   Single,   SumColor(0   To   2)   As   Long,   Bits   As   Long,   GreyColor   As   Long
Dim   RedTable(0   To   255)   As   Long,   GreenTable(0   To   255)   As   Long,   BlueTable(0   To   255)   As   Long
Dim   RedValue   As   Long,   GreenValue   As   Long,   BlueValue   As   Long
Dim   dWidth   As   Long,   dHeight   As   Long,   Scanline   As   Long,   SizeImage   As   Long
Dim   dPtr   As   Long

dWidth   =   mBitmap.mWidth   –   1:   dHeight   =   mBitmap.mHeight   –   1
SizeImage   =   mBitmap.SizeImage
Scanline   =   mBitmap.Scanline
dPtr   =   mBitmap.ImagePtr

JumpLines   =   Int(dHeight   /   1500)   *   Scanline
StepLines   =   Int(dHeight   /   1500)   +   1

JumpBits   =   Int(dWidth   /   1500)   *   4   +   4
StepBits   =   Int(dWidth   /   1500)   +   1

p4Byte0Ptr(0)   =   dPtr

For   i   =   0   To   dHeight   Step   StepLines
For   j   =   0   To   dWidth   Step   StepBits
SumColor(0)   =   SumColor(0)   +   p4Byte0(0)
SumColor(1)   =   SumColor(1)   +   p4Byte0(1)
SumColor(2)   =   SumColor(2)   +   p4Byte0(2)
Bits   =   Bits   +   1
p4Byte0Ptr(0)   =   p4Byte0Ptr(0)   +   JumpBits
Next   j
p4Byte0Ptr(0)   =   p4Byte0Ptr(0)   +   JumpLines
Next   i

MeanColor(0)   =   SumColor(0)   \   Bits
MeanColor(1)   =   SumColor(1)   \   Bits
MeanColor(2)   =   SumColor(2)   \   Bits

GreyColor   =   (MeanColor(0)   *   11   +   MeanColor(1)   *   59   +   MeanColor(2)   *   30)   \   100

BlueValue   =   GreyColor   –   MeanColor(0)
GreenValue   =   GreyColor   –   MeanColor(1)
RedValue   =   GreyColor   –   MeanColor(2)

AutoInfo.Caption   =   “红色:”   &   CStr(RedValue)   &   ”   |   绿色:”   &   CStr(GreenValue)   &   ”   |   蓝色:”   &   CStr(BlueValue)

For   i   =   0   To   255
RedTable(i)   =   i   +   RedValue
If   RedTable(i)   >   &HFF   Then   RedTable(i)   =   &HFF
If   RedTable(i)   <   &H0   Then   RedTable(i)   =   &H0
GreenTable(i)   =   i   +   GreenValue
If   GreenTable(i)   >   &HFF   Then   GreenTable(i)   =   &HFF
If   GreenTable(i)   <   &H0   Then   GreenTable(i)   =   &H0
BlueTable(i)   =   i   +   BlueValue
If   BlueTable(i)   >   &HFF   Then   BlueTable(i)   =   &HFF
If   BlueTable(i)   <   &H0   Then   BlueTable(i)   =   &H0
Next   i

p4Byte0Ptr(0)   =   dPtr

FrmMain.AutoPBar.Max   =   dHeight
For   i   =   0   To   dHeight
For   j   =   0   To   dWidth
p4Byte0(0)   =   BlueTable(p4Byte0(0))
p4Byte0(1)   =   GreenTable(p4Byte0(1))
p4Byte0(2)   =   RedTable(p4Byte0(2))
p4Byte0Ptr(0)   =   p4Byte0Ptr(0)   +   4
Next   j
FrmMain.AutoPBar.Value   =   i
Next   i

FrmMain.AutoPBar.Max   =   100:   FrmMain.AutoPBar.Value   =   100

AutoColorPoise   =   True
End   Function

发表评论

电子邮件地址不会被公开。 必填项已用*标注