1.自動色階調整
算法原理:
(2)對每個通道,利用LowCut和HighCut,計算灰度最小值min和最大值max;
(3)對每個通道分別建立分段線性拉伸查找表,
f(g) = 0 g<=min
f(g) = 255 g>=max
f(g) = ((g-min)/(max-min)) * 255 min<g<max
(4)對每個通道利用相應的查找表進行分段線性拉伸,得到效果圖。
2.自動對比度調整
算法原理:
(1)分別統計每個通道的灰度直方圖;
(2)對每個通道,利用LowCut和HighCut,分別計算灰度最小值和最大值,取三個通道最小值中最小者作爲最小值min,取三個通道最大值中最大者爲最大值max;
(3)對每個通道建立統一的分段線性拉伸查找表,
f(g) = 0 g<=min
f(g) = 255 g>=max
f(g) = ((g-min)/(max-min)) * 255 min<g<max
(4)對每個通道利用統一的查找表進行分段線性拉伸,得到效果圖。
3.區別
兩者的區別主要是取最大值最小值的不同,自動色階每個通道取的是當前通道的最大值最小值,而自動對比度取的是三個通道之最。
根據這個區別,不難推測出它們在效果上的一個區別:自動對比度不會改變RGB之間的大小順序,而自動色階有可能改變RGB之間的大小順序。
參考博客:
http://www.cnblogs.com/Imageshop/archive/2011/11/13/2247614.html
博客中提到這兩個算法主要是參考PS中的功能。做圖像處理的,不會PS,確實有點說不過去,有時間我還是要學習一下PS。
然後針對其中的一些功能,用opencv實現一下,肯定能有很多收穫。(待做事件)
4.OpenCV實現
(1)自動色階
/*
*函數功能:自動色階調整(仿照PS功能)
*輸入參數:src 輸入彩色圖像
*輸出參數:dst 輸出調整色階之後的彩色圖像
*返回值:void
*算法步驟:
(1)分別統計每個通道的灰度直方圖;
(2)對每個通道,利用LowCut和HighCut,計算灰度最小值min和最大值max;
(3)對每個通道分別建立分段線性拉伸查找表,
f(g) = 0 g<=min
f(g) = 255 g>=max
f(g) = ((g-min)/(max-min)) * 255 min<g<max
(4)對每個通道利用相應的查找表進行分段線性拉伸,得到效果圖。
http://www.cnblogs.com/Imageshop/archive/2011/11/13/2247614.html
*/
void AutoLevelsAdjust(cv::Mat &src, cv::Mat &dst)
{
CV_Assert(!src.empty() && src.channels() == 3);
//統計灰度直方圖
int BHist[256] = { 0 }; //B分離
int GHist[256] = { 0 }; //G分量
int RHist[256] = { 0 }; //R分量
cv::MatIterator_<Vec3b> its, ends;
for (its = src.begin<Vec3b>(), ends = src.end<Vec3b>(); its != ends; its++)
{
BHist[(*its)[0]]++;
GHist[(*its)[1]]++;
RHist[(*its)[2]]++;
}
//設置LowCut和HighCut
float LowCut = 0.5;
float HighCut = 0.5;
//根據LowCut和HighCut查找每個通道最大值最小值
int BMax = 0, BMin = 0;
int GMax = 0, GMin = 0;
int RMax = 0, RMin = 0;
int TotalPixels = src.cols * src.rows;
float LowTh = LowCut * 0.01 * TotalPixels;
float HighTh = HighCut * 0.01 * TotalPixels;
//B通道查找最小最大值
int sumTempB = 0;
for (int i = 0; i < 256; i++)
{
sumTempB += BHist[i];
if (sumTempB >= LowTh)
{
BMin = i;
break;
}
}
sumTempB = 0;
for (int i = 255; i >= 0; i--)
{
sumTempB += BHist[i];
if (sumTempB >= HighTh)
{
BMax = i;
break;
}
}
//G通道查找最小最大值
int sumTempG = 0;
for (int i = 0; i < 256; i++)
{
sumTempG += GHist[i];
if (sumTempG >= LowTh)
{
GMin = i;
break;
}
}
sumTempG = 0;
for (int i = 255; i >= 0; i--)
{
sumTempG += GHist[i];
if (sumTempG >= HighTh)
{
GMax = i;
break;
}
}
//R通道查找最小最大值
int sumTempR = 0;
for (int i = 0; i < 256; i++)
{
sumTempR += RHist[i];
if (sumTempR >= LowTh)
{
RMin = i;
break;
}
}
sumTempR = 0;
for (int i = 255; i >= 0; i--)
{
sumTempR += RHist[i];
if (sumTempR >= HighTh)
{
RMax = i;
break;
}
}
//對每個通道建立分段線性查找表
//B分量查找表
int BTable[256] = { 0 };
for (int i = 0; i < 256; i++)
{
if (i <= BMin)
BTable[i] = 0;
else if (i > BMin && i < BMax)
BTable[i] = cvRound((float)(i - BMin) / (BMax - BMin) * 255);
else
BTable[i] = 255;
}
//G分量查找表
int GTable[256] = { 0 };
for (int i = 0; i < 256; i++)
{
if (i <= GMin)
GTable[i] = 0;
else if (i > GMin && i < GMax)
GTable[i] = cvRound((float)(i - GMin) / (GMax - GMin) * 255);
else
GTable[i] = 255;
}
//R分量查找表
int RTable[256] = { 0 };
for (int i = 0; i < 256; i++)
{
if (i <= RMin)
RTable[i] = 0;
else if (i > RMin && i < RMax)
RTable[i] = cvRound((float)(i - RMin) / (RMax - RMin) * 255);
else
RTable[i] = 255;
}
//對每個通道用相應的查找表進行分段線性拉伸
cv::Mat dst_ = src.clone();
cv::MatIterator_<Vec3b> itd, endd;
for (itd = dst_.begin<Vec3b>(), endd = dst_.end<Vec3b>(); itd != endd; itd++)
{
(*itd)[0] = BTable[(*itd)[0]];
(*itd)[1] = GTable[(*itd)[1]];
(*itd)[2] = RTable[(*itd)[2]];
}
dst = dst_;
}
(2)自動對比度
/*
*函數功能:自動對比度調整(仿照PS功能)
*輸入參數:src 輸入彩色圖像
*輸出參數:dst 輸出調整色階之後的彩色圖像
*返回值:void
*算法步驟:
(1)分別統計每個通道的灰度直方圖;
(2)對每個通道,利用LowCut和HighCut,分別計算灰度最小值和最大值,取三個通道最小值中
最小者作爲最小值min,取三個通道最大值中最大者爲最大值max;
(3)對每個通道建立統一的分段線性拉伸查找表,
f(g) = 0 g<=min
f(g) = 255 g>=max
f(g) = ((g-min)/(max-min)) * 255 min<g<max
(4)對每個通道利用統一的查找表進行分段線性拉伸,得到效果圖。
http://www.cnblogs.com/Imageshop/archive/2011/11/13/2247614.html
*/
void AutoContrastAdjust(cv::Mat &src, cv::Mat &dst)
{
CV_Assert(!src.empty() && src.channels() == 3);
//統計灰度直方圖
int BHist[256] = { 0 };
int GHist[256] = { 0 };
int RHist[256] = { 0 };
cv::MatIterator_<Vec3b> its, ends;
for (its = src.begin<Vec3b>(), ends = src.end<Vec3b>(); its != ends; its++)
{
BHist[(*its)[0]]++;
GHist[(*its)[1]]++;
RHist[(*its)[2]]++;
}
//設置LowCut和HighCut
float LowCut = 0.5;
float HighCut = 0.5;
//根據LowCut和HighCut查找每個通道最大值最小值
int BMax = 0, BMin = 0;
int GMax = 0, GMin = 0;
int RMax = 0, RMin = 0;
int TotalPixels = src.cols * src.rows;
float LowTh = LowCut * 0.01 * TotalPixels;
float HighTh = HighCut * 0.01 * TotalPixels;
//B通道查找最小最大值
int sumTempB = 0;
for (int i = 0; i < 256; i++)
{
sumTempB += BHist[i];
if (sumTempB >= LowTh)
{
BMin = i;
break;
}
}
sumTempB = 0;
for (int i = 255; i >= 0; i--)
{
sumTempB += BHist[i];
if (sumTempB >= HighTh)
{
BMax = i;
break;
}
}
//G通道查找最小最大值
int sumTempG = 0;
for (int i = 0; i < 256; i++)
{
sumTempG += GHist[i];
if (sumTempG >= LowTh)
{
GMin = i;
break;
}
}
sumTempG = 0;
for (int i = 255; i >= 0; i--)
{
sumTempG += GHist[i];
if (sumTempG >= HighTh)
{
GMax = i;
break;
}
}
//R通道查找最小最大值
int sumTempR = 0;
for (int i = 0; i < 256; i++)
{
sumTempR += RHist[i];
if (sumTempR >= LowTh)
{
RMin = i;
break;
}
}
sumTempR = 0;
for (int i = 255; i >= 0; i--)
{
sumTempR += RHist[i];
if (sumTempR >= HighTh)
{
RMax = i;
break;
}
}
//獲取最大值,最小值,與自動色階的不同之處主要在此,取的是三通道之最
int Max = std::max(std::max(BMax, GMax), RMax);
int Min = std::min(std::min(BMin, GMin), RMin);
//建立統一的分段線性查找表
int Table[256] = { 0 };
for (int i = 0; i < 256; i++)
{
if (i <= Min)
Table[i] = 0;
else if (i > Min && i < Max)
Table[i] = (int)((float)(i - Min) / (Max - Min) * 255);
else
Table[i] = 255;
}
//對每個通道用統一的查找表進行分段線性拉伸
cv::Mat dst_ = src.clone();
cv::MatIterator_<Vec3b> itd, endd;
for (itd = dst_.begin<Vec3b>(), endd = dst_.end<Vec3b>(); itd != endd; itd++)
{
(*itd)[0] = Table[(*itd)[0]];
(*itd)[1] = Table[(*itd)[1]];
(*itd)[2] = Table[(*itd)[2]];
}
dst = dst_;
}
(3)效果
原圖
自動色階效果圖
自動對比度效果圖
單從這幅圖的測試效果來看,沒法說自動色階和自動對比度哪個效果更好。但是兩者之間確實存在區別,爲了對比一下兩者的不同,利用ImageWatch觀察一下內部像素值的變化
原圖,位置(0000,0416)處的像素值 B |G|R = 213|231|160, 大小關係 G>B>R
自動色階,位置(0000,0416)處的像素值 B |G|R = 255|254|157, 大小關係 B>G>R,改變了B和G的大小關係
自動對比度,位置(0000,0416)處的像素值 B |G|R = 226|248|160, 大小關係 G>B>R,RGB大小關係不變
小結:如果在圖像處理的過程中比較關心RGB之間的大小關係,就不能用自動色階,而應該用自動對比度。如果不關心RGB的大小關係,就根據實際測試對後續的影響來判斷使用哪種處理。
5.擴展
在查詢相關資料的時候,看到很多資料提到了彩色圖像的直方圖均衡化。於是,決定也瞭解一下彩色圖像直方圖均衡化的效果,並且與自動對比度進行一下比對。
參考博客:
https://blog.csdn.net/frank_xu_0818/article/details/39232157
(1)RGB空間下分通道直方圖均衡化
/*
*函數功能:對彩色圖像進行直方圖均衡化
*輸入參數:src 輸入彩色圖像
*輸出參數:dst 輸出均衡化之後的圖像
*返回值:void
*備註:先通道分離,然後對每一通道進行直方圖均衡化,最後融合
*/
void ColorEqualizeHist(cv::Mat &src, cv::Mat &dst)
{
CV_Assert(!src.empty() && src.channels() == 3);
//通道分離
cv::Mat channels[3];
cv::split(src, channels);
//每個通道進行直方圖均衡化
cv::Mat equalizeHistImg[3];
for (int i = 0; i < 3; i++)
cv::equalizeHist(channels[i], equalizeHistImg[i]);
//通道融合
cv::Mat dst_;
cv::merge(equalizeHistImg, 3, dst_);
dst = dst_;
}
(2)轉到YCbCr空間下單獨對Y通道直方圖均衡化
/*
*函數功能:對彩色圖像進行直方圖均衡化
*輸入參數:src 輸入彩色圖像
*輸出參數:dst 輸出均衡化之後的圖像
*返回值:void
*備註:先轉到YCbCr顏色空間,然後通道分離,只對灰度通道進行直方圖均衡化,
進行通道融合,最後轉回到BGR空間
*/
void EqualizeHistByYCbCr(cv::Mat &src, cv::Mat &dst)
{
CV_Assert(!src.empty() && src.channels() == 3);
//BGR顏色空間轉換到YCbCr顏色空間
cv::Mat srcYCbCr;
cv::cvtColor(src, srcYCbCr, CV_BGR2YCrCb);
//對YCbCr進行通道分離
cv::Mat channels[3];
cv::split(srcYCbCr, channels);
//對亮度通道進行直方圖均衡化
cv::equalizeHist(channels[0], channels[0]);
//圖像融合
cv::Mat dst_;
cv::merge(channels, 3, dst_);
//將YCbCr顏色空間轉換回BGR顏色空間
cv::cvtColor(dst_, dst_, CV_YCrCb2BGR);
dst = dst_;
}
(3)效果
RGB直方圖均衡化
YCbCr直方圖均衡化
小結:RGB分通道均衡化再融合,顏色容易失真,實用性很低。轉換爲YCbCr再進行對亮度通道均衡化,融合後轉回RGB,效果不夠穩定。參考博客中的lena圖我自己也測了,效果確實不錯,但是我測gakki還有別的一幅圖,效果不好。
根據我測得一些圖,可以發現自動對比度與直方圖均衡化相比,自動對比度效果更好,更穩定。