Opencv影象增強演算法(對比度增強)-opencv
由於專案需要,這幾天找了網上一個基於opencv的影象對比度增強演算法的部落格。但演算法釋出的日期太過久遠了,2012年的程式碼放到現在很多類和類方法已經不再適用於新版本的opencv庫了。所以我花了點時間重寫了一下,並加入一些個人對於演算法的理解與優化。
目標原始碼:
《Opencv 影象增強演算法 影象檢測結果》
https://blog.csdn.net/abcjennifer/article/details/7401921
《opencv 彩色影象對比度增強》
https://blog.csdn.net/abcjennifer/article/details/7428737
對等重寫:
對等重寫就是原部落格怎麼寫,我就怎麼寫,只是把一些已經棄用的類、方法、巨集給替換掉而已。相當於就是翻新,然後加入一些個人理解的註釋。
增強函式
/***********************************************************
增強演算法的原理在於先統計每個灰度值在整個影象中所佔的比例
然後以小於當前灰度值的所有灰度值在總畫素中所佔的比例,作為增益係數
對每一個畫素點進行調整。由於每一個值的增益係數都是小於它的所有值所佔
的比例和。所以就使得經過增強之後的影象亮的更亮,暗的更暗。
************************************************************/
void ImageStretchByHistogram(const Mat & src, Mat & dst)
{
//判斷傳入引數是否正常
if (!(src.size().width == dst.size().width))
{
cout << "error" << endl;
return;
}
double p[256], p1[256], num[256];
memset(p, 0, sizeof(p));
memset(p1, 0, sizeof(p1));
memset(num, 0, sizeof (num));
int height = src.size().height;
int width = src.size().width;
long wMulh = height * width;
//統計每一個灰度值在整個影象中所佔個數
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
uchar v = src.at<uchar>(y, x);
num[v]++;
}
}
//使用上一步的統計結果計算每一個灰度值所佔總畫素的比例
for (int i = 0; i < 256; i++)
{
p[i] = num[i] / wMulh;
}
//計算每一個灰度值,小於當前灰度值的所有灰度值在總畫素中所佔的比例
//p1[i]=sum(p[j]); j<=i;
for (int i = 0; i < 256; i++)
{
for (int k = 0; k <= i; k++)
p1[i] += p[k];
}
//以小於當前灰度值的所有灰度值在總畫素中所佔的比例,作為增益係數對每一個畫素點進行調整。
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++) {
uchar v = src.at<uchar>(y, x);
dst.at<uchar>(y, x) = p1[v] * 255 + 0.5;
}
}
return;
}
單通道灰度圖增強函式
//調整影象對比度
Mat AdjustContrastY(const Mat & img)
{
Mat out = Mat::zeros(img.size(), CV_8UC1);
Mat workImg = img.clone();
//對影象進行對比度增強
ImageStretchByHistogram(workImg, out);
return Mat(out);
}
三通道彩圖增強函式
//調整影象對比度
Mat AdjustContrast(const Mat & img)
{
Mat out;
Mat Y = Mat::zeros(img.size(), CV_8UC1);
Mat Cb = Mat::zeros(img.size(), CV_8UC1);
Mat Cr = Mat::zeros(img.size(), CV_8UC1);
Mat Compile_YCbCr = Mat::zeros(img.size(), CV_8UC3);
Mat dst = Mat::zeros(img.size(), CV_8UC1);
int i;
/*******************************************
對比度增強演算法本質上就是對影象Y分量進行調整。
所以需要先把影象資料從BGR轉換為YUV,YCrCb就是YUV
然後再對YUV資料進行拆分,最後單獨對Y分量進行調整。
********************************************/
cvtColor(img, Compile_YCbCr, cv::COLOR_BGR2YCrCb);
//定義一個Mat向量容器儲存拆分後的資料
vector<Mat> channels;
//進行影象通道拆分
split(Compile_YCbCr, channels);
channels.at(0).copyTo(Y);
channels.at(1).copyTo(Cb);
channels.at(2).copyTo(Cr);
//單獨對Y分量進行調整
ImageStretchByHistogram(Y, dst);
//把對比度增強後的Y分量與原來的U、V分量重組為新的影象
for (int y = 0; y < img.size().height; y++)
{
for (int x = 0; x < img.size().width; x++)
{
//拼接一個畫素的三通道
Mat cur = Mat::zeros(3, 1, CV_32FC1);
cur.at<float>(0, 0) = dst.at<uchar>(y, x);
cur.at<float>(1, 0) = Cb.at<uchar>(y, x);
cur.at<float>(2, 0) = Cr.at<uchar>(y, x);
//三通道順序寫入
for (i = 0; i < 3; i++)
{
double xx = cur.at<float>(i, 0);
(Compile_YCbCr).at<Vec3b>(y, x)[i] = xx;
}
}
}
//把重組之後的影象轉換回BGR資料進行返回
cvtColor(Compile_YCbCr, out, COLOR_YCrCb2BGR);
return Mat(out);
}
優化重寫
優化重寫注意是針對三通道彩圖增強函式“AdjustContrast()”這個函式的,這主要是由於原演算法本身就存在一定的問題和在當前的opencv版本下存在一定的改進空間。就像原部落格中的dst1定義的是3通道,但增強函式“ImageStretchByHistogram()”內部處理的時候只是使用了當通道進行賦值,雖然可以正常執行,但卻容易讓閱讀程式碼的人產生誤解(例如我),而且在新版本opencv的語法環境下會存在問題(原部落格評論區就有人遇到了這個語法問題,我上面的程式碼中通過修改部分程式碼邏輯解決了這個問題)。且在測試中發現,三通道彩圖的增強函式執行速度過慢。
所以我針對三通道彩圖增強函式“AdjustContrast()”這個函式進行了重寫。
//三通道彩圖增強函式
Mat AdjustContrast(const Mat & img)
{
Mat out = Mat::zeros(img.size(), CV_8UC3);
Mat Compile_YCbCr = Mat::zeros(img.size(), CV_8UC3);
Mat dst = Mat::zeros(img.size(), CV_8UC1);
//進行BGR轉YUV
cvtColor(img, Compile_YCbCr, cv::COLOR_BGR2YCrCb);
//定義一個Mat向量容器儲存拆分後的資料
vector<Mat> channels;
//進行影象通道拆分
split(Compile_YCbCr, channels);
//單獨對Y分量進行調整
ImageStretchByHistogram(channels.at(0), dst);
//把調整後的Y分量替換原來的Y分量
dst.copyTo(channels.at(0));
//進行通道的合併
merge(channels, Compile_YCbCr);
//把重組之後的影象轉換回BGR資料進行返回
cvtColor(Compile_YCbCr, out, COLOR_YCrCb2BGR);
return Mat(out);
}
灰度效果:
增強前
增強後
彩圖效果:
增強前
增強後
相關參考:
《CvMat用法詳解》
https://blog.csdn.net/zx3517288/article/details/51760541
《OpenCV型別CV_32F和CV_32FC1之間的差異(OpenCV型別CV_32F和CV_32FC1之間的差異)》
https://www.it1352.com/541681.html
《opencv Mat 多通道 資料讀取或賦值》
https://blog.csdn.net/xuexiaokkk/article/details/50251511
《openCv:IplImage的變數imageData解釋》
https://blog.csdn.net/aic1999/article/details/83040442
《opencv split和merge操作》
https://blog.csdn.net/omuyejingfeng1/article/details/25685141
《opencv中cvSplit函式講解》
https://blog.csdn.net/yankai0219/article/details/6630911
《opencv筆記——cvCreateImage函式說明》
https://blog.csdn.net/breeze5428/article/details/30050327
《IplImage結構體 nChannels depth含義》
https://blog.csdn.net/qq_41972382/article/details/89225522