自動色階
自動色階就是自動定義每個通道中最亮和最暗的像素作爲白和黑,然後按比例重新分配其間的像素值。
對比度
對比度指的是一幅圖像中明暗區域最亮的白和最暗的黑之間不同亮度層級的測量,差異範圍越大代表對比越大,差異範圍越小代表對比越小,好的對比率120:1就可容易地顯示生動、豐富的色彩,當對比率高達300:1時,便可支持各階的顏色。但對比率遭受和亮度相同的困境,現今尚無一套有效又公正的標準來衡量對比率,所以最好的辨識方式還是依靠使用者眼睛。
直方圖均衡
直方圖均衡化是圖像處理領域中利用圖像直方圖對對比度進行調整的方法。通過這種方法,亮度可以更好地在直方圖上分佈。這樣就可以用於增強局部的對比度而不影響整體的對比度,直方圖均衡化通過有效地擴展常用的亮度來實現這種功能。
自動色階、自動對比度以及直方圖均衡這三個算法雖然很普通,也很簡單,但是在實際應用中有着非常高的使用率,特別是在修圖中,很多設計師打開一幅圖,首先的的操作就是Shift+Ctrl+L(自動色階)。在原理實現上,他們都屬於基於直方圖統計方面的算法,執行效率都非常之高。
先來普及下調整圖像- 自動對比度、自動色階算法。
我們以24位彩色圖像爲例說明這兩個算法。在執行兩個算法之前,我們需要確定兩個參數,大家在用PS時選擇自動色階並不會彈出什麼參數設置對話框,那是因爲PS把這個隱藏的比較深 。選擇曲線或色階命令,然後在彈出的界面中點擊選項按鈕,會出現如下界面
注意其中的裁剪(C)/裁剪(P)中的變量,這兩個參數是影響自動色階效果的重要數據,我們以變量LowCut和HighCut來記錄它。好,接着說。兩個算法的第一步,都是分別統計各通道(紅/綠/藍)的直方圖。
看了上面代碼,我很欣賞C++的++運算符,HistRed(Red)++,多麼簡單啊,而且更高效。
Dim HistRed(255) As Long, HistGreen(255) As Long
Dim HistBlue(255) As Long
For Y = 0 To Height - 1
Speed = Y * Stride
For X = 0 To Width - 1
Red = ImageData(Speed + 2): Green = ImageData(Speed + 1): Blue = ImageData(Speed)
HistRed(Red) = HistRed(Red) + 1 '統計直方圖
HistGreen(Green) = HistGreen(Green) + 1
HistBlue(Blue) = HistBlue(Blue) + 1
Speed = Speed + 3
Next
Next
算法的第二步,分別計算各通道按照給定的參數所確定的上下限值。什麼意思呢,比如對於藍色通道,我們從色階0開始向上累加統計直方圖,當累加值大於LowCut*所有像素數時,以此時的色階值計爲MinBlue。然後從色階255開始向下累計直方圖,如果累加值大於HighCut*所有像素時,以此時的色階值計爲MaxBlue。
PixelAmount = Width * Height '所有像素的數目
Sum = 0
For Y = 0 To 255
Sum = Sum + HistBlue(Y)
If Sum >= PixelAmount * LowCut * 0.01 Then '注意PS界面裏的那個百分號
MinBlue = Y '得到藍色分量的下限
Exit For '退出循環
End If
Next
Sum = 0
For Y = 255 To 0 Step -1
Sum = Sum + HistBlue(Y)
If Sum >= PixelAmount * HighCut * 0.01 Then '注意PS界面裏的那個百分號
MaxBlue = Y '得到藍色分量的上限
Exit For '退出循環
End If
Next
綠色通道和紅色通道的代碼類似,請朋友們自己補充。
下一步,自動色階和自動對比度就有所區別了,我們首先介紹自動色階。
自動色階:按照我們剛剛計算出的MinBlue/MaxBlue構建一個隱射表,隱射表的規則是,對於小於MinBlue的值,則隱射爲0(實際上這句話也不對,隱射爲多少是和那個自動顏色校正選項對話框中的陰影所設定的顏色有關,默認情況下是黑色,對應的RGB分量都爲0,所以我們這裏就隱射爲0,有興趣你們也可以指定爲其他的參數),對於大於MaxBlue的值,則隱射爲255(同理,這個值和高光的顏色設置有關),對於介於MinBlue和MaxBlue之間的值,則進行線性隱射,默認是隱射爲0到255之間(當然實際是和我們的暗調和高光的設置有關,並且這裏其實也不是線性隱射,是有一個Gamma校正,爲了簡便,用線性替代效果也沒太大的問題)。
For Y = 0 To 255
If Y <= MinBlue Then
BlueMap(Y) = 0
ElseIf Y >= MaxBlue Then
BlueMap(Y) = 255
Else
BlueMap(Y) = (Y - MinBlue) / (MaxBlue - MinBlue) * 255 '線性隱射
End If
Next
順便提一點,從程序的魯棒性上來說,在執行這段代碼前判斷下MaxBlue是否等於MinBlue很有必要,兩者相等,這圖就無需校正了,直接結束程序。
用同樣的道理,計算出綠色和紅色通道的隱射表GreenMap和RedMap。
最後一步,對各通道圖像數據進行隱射。
For Y = 0 To Height - 1
Speed = Y * Stride
For X = 0 To Width - 1
ImageData(Speed) = BlueMap(ImageData(Speed))
ImageData(Speed + 1) = GreenMap(ImageData(Speed + 1))
ImageData(Speed + 2) = RedMap(ImageData(Speed + 2))
Speed = Speed + 3
Next
Next
自動色階完成,下面我們談談自動對比度的不同。
在計算完各通道對應的上下限值後,自動對比度算法首先獲取三個通道下限值的最小值,以及上限值的最大值,如下代碼:
If MinBlue < MinGreen Then
Min = MinBlue
Else
Min = MinGreen
End If
If Min > MinRed Then Min = MinRed
If MaxBlue > MaxGreen Then
Max = MaxBlue
Else
Max = MaxGreen
End If
If Max < MaxRed Then Max = MaxRed
然後以此爲新的上下限,計算隱射表。
For Y = 0 To 255
If Y <= Min Then
Map(Y) = 0
ElseIf Y > Max Then
Map(Y) = 255
Else
Map(Y) = (Y - Min) / (Max - Min) * 255 '線性隱射
End If
Next
最後,以這個隱射表分別對紅綠藍通道進行處理。
For Y = 0 To Height - 1
Speed = Y * Stride
For X = 0 To Width - 1
ImageData(Speed) = Map(ImageData(Speed))
ImageData(Speed + 1) = Map(ImageData(Speed + 1))
ImageData(Speed + 2) = Map(ImageData(Speed + 2))
Speed = Speed + 3
Next
Next
有的時候我們發現自動色階或對比度等等會過調整或欠調整,我們發現了對他們的一些改進方式,其核心改進如下:
for (int Y = 0; Y < 256; Y++)
{
if (Y < Min)
Table[Y] = 0;
else if (Y > Max)
Table[Y] = 255;
else
Table[Y] = IM_ClampToByte((float)(Y - Min) / (Max - Min) * 255);
}
改進後的方式:
float Avg = 0, Mean = 0, Sum = 0;
for (int Y = 0; Y < 256; Y++)
{
Sum += Histgram[Y];
Avg += Y * Histgram[Y];
}
Mean = Avg / Sum;
float Gamma = log(0.5f) / log((float)(Mean - Min) / (Max - Min));
if (Gamma < 0.1f)
Gamma = 0.1f;
else if (Gamma > 10)
Gamma = 10;
for (int Y = 0; Y < 256; Y++)
{
if (Y < Min)
Table[Y] = 0;
else if (Y > Max)
Table[Y] = 255;
else
Table[Y] = IM_ClampToByte(pow((float)(Y - Min) / (Max - Min), Gamma) * 255);
}
其中的Max和Min的意思請參考其他的文章。
改進後的查找表考慮到全圖的一個平均值信息,根據這個平局值來決定調整的一個Gamma值,相當於他同時結合了Gamma校正和自動色階的思想,普通的自動色階對應Gamma=1,還是拿一些我常用的測試圖舉例吧。
似乎改進後的更爲合理。
對於直方圖均衡化的改進,核心原理:
改變圖像的色調曲線。
它應該在圖像的平坦區域中顯示細節。
直方圖均衡可以增強無意義的細節並隱藏重要但小巧的高對比度特徵。這種方法使用了類似算法,但使用直方圖值的平方根,因此其效果不太極端。
核心代碼修改如下:
private void equalize(ImageProcessor ip, int[] histogram)
{
ip.resetRoi();
if (ip instanceof ShortProcessor) { // Short
max = 65535;
range = 65535;
} else { //bytes
max = 255;
range = 255;
}
double sum;
sum = getWeightedValue(histogram, 0);
for (int i=1; i<max; i++)
sum += 2 * getWeightedValue(histogram, i);
sum += getWeightedValue(histogram, max);
double scale = range/sum;
int[] lut = new int[range+1];
lut[0] = 0;
sum = getWeightedValue(histogram, 0);
for (int i=1; i<max; i++) {
double delta = getWeightedValue(histogram, i);
sum += delta;
lut[i] = (int)Math.round(sum*scale);
sum += delta;
}
lut[max] = max;
applyTable(ip, lut);
private double getWeightedValue(int[] histogram, int i) {
int h = histogram[i];
if (h<2 || classicEqualization) return (double)h;
return Math.sqrt((double)(h));
}
在這裏,他主要打開直方圖數據的根,這可以減少直方圖對校正圖像的一些特殊顏色等級的影響。他可以解決普通的直方圖均衡,有時也可以處理圖像變換。大問題,我們也用上面的兩個數字來說明。
可以清楚地看出,標準直方圖均衡對於兩幅圖像的處理來說太多了,並且改善的效果更加和諧自然。
這種改進也可以嵌入到許多其他直方圖相關的算法中,例如CLAHE等,並且可以有效地改善它們的效果。
更多論文源碼關注微信公衆號:“圖像算法”或者微信搜索賬號imalg_cn關注公衆號