自動色階
第一步,分別統計各通道(紅/綠/藍)的直方圖。
第二步,分別計算各通道按照給定的參數所確定的上下限值。什么意思呢,比如對於藍色通道,我們從色階0開始向上累加統計直方圖,當累加值大於LowCut所有像素數時,以此時的色階值計為BMin。然后從色階255開始向下累計直方圖,如果累加值大於HighCut所有像素時,以此時的色階值計為BMax。
第三步,按照我們剛剛計算出的MinBlue/MaxBlue構建一個隱射表,隱射表的規則是,對於小於MinBlue的值,則隱射為0(實際上這句話也不對,隱射為多少是和那個自動顏色校正選項對話框中的陰影所設定的顏色有關,默認情況下是黑色,對應的RGB分量都為0,所以我們這里就隱射為0,有興趣你們也可以指定為其他的參數),對於大於MaxBlue的值,則隱射為255(同理,這個值和高光的顏色設置有關),對於介於MinBlue和MaxBlue之間的值,則進行線性隱射,默認是隱射為0到255之間(當然實際是和我們的暗調和高光的設置有關,並且這里其實也不是線性隱射,是有一個Gamma校正,為了簡便,用線性替代效果也沒太大的問題)。
最后一步,對各通道圖像數據進行隱射。
1 // AutoLevel.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。 2 // 3
4 #include <iostream>
5 #include<opencv2\core\core.hpp>
6 #include<opencv2\highgui\highgui.hpp>
7 #include<opencv2\imgproc\imgproc.hpp>
8 #include<iostream>
9 #include<vector>
10 #include <algorithm>
11 using namespace cv; 12
13 void AutoLevelsAdjust(cv::Mat& src, cv::Mat& dst) 14 { 15 CV_Assert(!src.empty() && src.channels() == 3); 16
17 //統計灰度直方圖
18 int BHist[256] = { 0 }; //B分離
19 int GHist[256] = { 0 }; //G分量
20 int RHist[256] = { 0 }; //R分量
21 cv::MatIterator_<Vec3b> its, ends; 22 for (its = src.begin<Vec3b>(), ends = src.end<Vec3b>(); its != ends; its++) 23 { 24 BHist[(*its)[0]]++; 25 GHist[(*its)[1]]++; 26 RHist[(*its)[2]]++; 27 } 28
29 //設置LowCut和HighCut
30 float LowCut = 0.5; 31 float HighCut = 0.5; 32
33 //根據LowCut和HighCut查找每個通道最大值最小值
34 int BMax = 0, BMin = 0; 35 int GMax = 0, GMin = 0; 36 int RMax = 0, RMin = 0; 37
38 int TotalPixels = src.cols * src.rows; 39 float LowTh = LowCut * 0.01 * TotalPixels; 40 float HighTh = HighCut * 0.01 * TotalPixels; 41
42 //B通道查找最小最大值
43 int sumTempB = 0; 44 for (int i = 0; i < 256; i++) 45 { 46 sumTempB += BHist[i]; 47 if (sumTempB >= LowTh) 48 { 49 BMin = i; 50 break; 51 } 52 } 53 sumTempB = 0; 54 for (int i = 255; i >= 0; i--) 55 { 56 sumTempB += BHist[i]; 57 if (sumTempB >= HighTh) 58 { 59 BMax = i; 60 break; 61 } 62 } 63
64 //G通道查找最小最大值
65 int sumTempG = 0; 66 for (int i = 0; i < 256; i++) 67 { 68 sumTempG += GHist[i]; 69 if (sumTempG >= LowTh) 70 { 71 GMin = i; 72 break; 73 } 74 } 75 sumTempG = 0; 76 for (int i = 255; i >= 0; i--) 77 { 78 sumTempG += GHist[i]; 79 if (sumTempG >= HighTh) 80 { 81 GMax = i; 82 break; 83 } 84 } 85
86 //R通道查找最小最大值
87 int sumTempR = 0; 88 for (int i = 0; i < 256; i++) 89 { 90 sumTempR += RHist[i]; 91 if (sumTempR >= LowTh) 92 { 93 RMin = i; 94 break; 95 } 96 } 97 sumTempR = 0; 98 for (int i = 255; i >= 0; i--) 99 { 100 sumTempR += RHist[i]; 101 if (sumTempR >= HighTh) 102 { 103 RMax = i; 104 break; 105 } 106 } 107
108 //對每個通道建立分段線性查找表 109 //B分量查找表
110 int BTable[256] = { 0 }; 111 for (int i = 0; i < 256; i++) 112 { 113 if (i <= BMin) 114 BTable[i] = 0; 115 else if (i > BMin && i < BMax) 116 BTable[i] = cvRound((float)(i - BMin) / (BMax - BMin) * 255); 117 else
118 BTable[i] = 255; 119 } 120
121 //G分量查找表
122 int GTable[256] = { 0 }; 123 for (int i = 0; i < 256; i++) 124 { 125 if (i <= GMin) 126 GTable[i] = 0; 127 else if (i > GMin && i < GMax) 128 GTable[i] = cvRound((float)(i - GMin) / (GMax - GMin) * 255); 129 else
130 GTable[i] = 255; 131 } 132
133 //R分量查找表
134 int RTable[256] = { 0 }; 135 for (int i = 0; i < 256; i++) 136 { 137 if (i <= RMin) 138 RTable[i] = 0; 139 else if (i > RMin && i < RMax) 140 RTable[i] = cvRound((float)(i - RMin) / (RMax - RMin) * 255); 141 else
142 RTable[i] = 255; 143 } 144
145 //對每個通道用相應的查找表進行分段線性拉伸
146 cv::Mat dst_ = src.clone(); 147 cv::MatIterator_<Vec3b> itd, endd; 148 for (itd = dst_.begin<Vec3b>(), endd = dst_.end<Vec3b>(); itd != endd; itd++) 149 { 150 (*itd)[0] = BTable[(*itd)[0]]; 151 (*itd)[1] = GTable[(*itd)[1]]; 152 (*itd)[2] = RTable[(*itd)[2]]; 153 } 154 dst = dst_; 155 } 156
157 int main() 158 { 159 Mat image = imread("E:\\picture\\wutian.jpg"); 160 Mat dst = Mat::zeros(image.rows, image.cols, CV_8UC1); 161 AutoLevelsAdjust(image, dst); 162 imshow("src", image); 163 imshow("dst", dst); 164 while (char(waitKey(1)) != 'q') {} 165 }
自動色階用來做去霧,還是最為穩定的,雖然現在有很多種其他增強方法,例如暗通道去霧,優化對比度去霧,基於Color-Lines的去霧,但是這些算法還不是很穩定,當先驗知識失效時,處理失真比較嚴重。但自動色階相對就比較穩定。