1. 程式人生 > 其它 >Opencv影象增強演算法(對比度增強)-opencv

Opencv影象增強演算法(對比度增強)-opencv

技術標籤:OpenCVopencv演算法計算機視覺c++

由於專案需要,這幾天找了網上一個基於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


零BUG是原則性問題。