一、理論基礎
在數學中我們學過線性理論,在圖像亮度和對比度調節中同樣適用,看下面這個公式:

在圖像像素中其中:
- 參數f(x)表示源圖像像素。
- 參數g(x) 表示輸出圖像像素。
- 參數a(需要滿足a>0)被稱為增益(gain),常常被用來控制圖像的對比度。
- 參數b通常被稱為偏置(bias),常常被用來控制圖像的亮度。
二、獲取圖像像素
在opencv中圖像數據是存放在Mat數據類型中,我們知道一個像素有rgb構成,所以Mat是個三維數組,一下就是簡單的獲取mat中圖像像素。
//三個for循環,執行運算 new_image(i,j) =a*image(i,j) + b for(int y = 0; y < image.rows; y++ ) { for(int x = 0; x < image.cols; x++ ) { for(int c = 0; c < 3; c++ ) { new_image.at<Vec3b>(y,x)[c]= saturate_cast<uchar>( (g_nContrastValue*0.01)*(image.at<Vec3b>(y,x)[c] ) + g_nBrightValue ); } } }
上述代碼中image.at<Vec3b>(y,x)[c] 其中,y是像素所在的行, x是像素所在的列, c是R、G、B(對應0、1、2)其中之一。
saturate_cast為了安全轉換,運算結果可能超出像素取值范圍(溢出),還可能是非整數(如果是浮點數的話),用saturate_cast對結果進行轉換,以確保它為有效值。
效果圖:

三、實例
#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include "opencv2/imgproc/imgproc.hpp" using namespace std; using namespace cv; static void ContrastAndBright(int, void *); int g_nContrastValue; //對比度值 int g_nBrightValue; //亮度值 Mat g_srcImage, g_dstImage; int main() { // 讀入用戶提供的圖像 g_srcImage = imread("0004.bmp"); g_dstImage = Mat::zeros(g_srcImage.size(), g_srcImage.type()); //設定對比度和亮度的初值 g_nContrastValue = 80; g_nBrightValue = 80; //創建窗口 namedWindow("【效果圖窗口】", 1); //創建軌跡條 createTrackbar("對比度:", "【效果圖窗口】", &g_nContrastValue, 300, ContrastAndBright); createTrackbar("亮 度:", "【效果圖窗口】", &g_nBrightValue, 200, ContrastAndBright); //調用回調函數 ContrastAndBright(g_nContrastValue, 0); ContrastAndBright(g_nBrightValue, 0); waitKey(0); //輸出一些幫助信息 return 0; } //-----------------------------【ContrastAndBright( )函數】------------------------------------ // 描述:改變圖像對比度和亮度值的回調函數 //----------------------------------------------------------------------------------------------- static void ContrastAndBright(int, void *) { // 三個for循環,執行運算 g_dstImage(i,j) = a*g_srcImage(i,j) + b for (int y = 0; y < g_srcImage.rows; y++) { for (int x = 0; x < g_srcImage.cols; x++) { for (int c = 0; c < 3; c++) { g_dstImage.at<Vec3b>(y, x)[c] = saturate_cast<uchar>((g_nContrastValue*0.01)*(g_srcImage.at<Vec3b>(y, x)[c]) + g_nBrightValue); } } } // 顯示圖像 imshow("【原始圖窗口】", g_srcImage); imshow("【效果圖窗口】", g_dstImage); }
注意:
saturate_cast:
功能:防止數據溢出,因為無論是加是減,乘除,都會超出一個像素灰度值的范圍(0~255)。所以,所以當運算完之后,結果為負,則轉為0,結果超出255,則為255。
四、改進
這樣已經完成了更改亮度和對比度的需求,但是用for循環執行效率有點低,圖像處理起來也不是特別流暢,opencv給出了非常合適的函數。
函數原型
void Mat::convertTo( Mat& m, int rtype, double alpha=1, double beta=0 )const;
輸入參數:
m 目標矩陣。如果m的大小與原矩陣不一樣,或者數據類型與參數不匹配,那么在函數convertTo內部會先給m重新分配空間。
rtype 指定從原矩陣進行轉換后的數據類型,即目標矩陣m的數據類型。當然,矩陣m的通道數應該與原矩陣一樣的。如果rtype是負數,那么m矩陣的數據類型應該與原矩陣一樣。
alpha 縮放因子。默認值是1。即把原矩陣中的每一個元素都乘以alpha。
beta 增量。默認值是0。即把原矩陣中的每一個元素都乘以alpha,再加上beta。
功能
把一個矩陣從一種數據類型轉換到另一種數據類型,同時可以帶上縮放因子和增量,公式如下:
m(x,y)=saturate_cast<rType>(alpha*(*this)(x,y)+beta);
由於有數據類型的轉換,所以需要用saturate_cast<rType>來處理數據的溢出。
所以上述代碼可以寫為,通過簡單拉動進度條可以看出這個速度上提升比較大
#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include "opencv2/imgproc/imgproc.hpp" using namespace std; using namespace cv; static void ContrastAndBright(int, void *); int g_nContrastValue; //對比度值 int g_nBrightValue; //亮度值 Mat g_srcImage, g_dstImage; int main() { // 讀入用戶提供的圖像 g_srcImage = imread("0004.bmp"); g_dstImage = Mat::zeros(g_srcImage.size(), g_srcImage.type()); //設定對比度和亮度的初值 g_nContrastValue = 80; g_nBrightValue = 80; //創建窗口 namedWindow("【效果圖窗口】", 1); //創建軌跡條 createTrackbar("對比度:", "【效果圖窗口】", &g_nContrastValue, 300, ContrastAndBright); createTrackbar("亮 度:", "【效果圖窗口】", &g_nBrightValue, 200, ContrastAndBright); //調用回調函數 ContrastAndBright(g_nContrastValue, 0); ContrastAndBright(g_nBrightValue, 0); waitKey(0); //輸出一些幫助信息 return 0; } //-----------------------------【ContrastAndBright( )函數】------------------------------------ // 描述:改變圖像對比度和亮度值的回調函數 //----------------------------------------------------------------------------------------------- static void ContrastAndBright(int, void *) { // 三個for循環,執行運算 g_dstImage(i,j) = a*g_srcImage(i,j) + b //for (int y = 0; y < g_srcImage.rows; y++) //{ // for (int x = 0; x < g_srcImage.cols; x++) // { // for (int c = 0; c < 3; c++) // { // g_dstImage.at<Vec3b>(y, x)[c] = saturate_cast<uchar>((g_nContrastValue*0.01)*(g_srcImage.at<Vec3b>(y, x)[c]) + g_nBrightValue); // } // } //} g_srcImage.convertTo(g_dstImage, -1, g_nContrastValue*0.01, g_nBrightValue); // 顯示圖像 imshow("【原始圖窗口】", g_srcImage); imshow("【效果圖窗口】", g_dstImage); }
