在數學中我們學過線性理論,在圖像亮度和對比度調節中同樣適用,看下面這個公式:
在圖像像素中其中:
- 參數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對結果進行轉換,以確保它為有效值。
二、實例程序
tatic void ContrastAndBright(int, void *); //-----------------------------------【main( )函數】-------------------------------------------- // 描述:控制台應用程序的入口函數,我們的程序從這里開始 //----------------------------------------------------------------------------------------------- string strWindowName = "對比度亮度效果圖"; string strTraName = "對比度"; string strTraName1 = "亮度"; int g_nContraValue = 0; int g_nBrightValue = 0; Mat g_srcImage,g_dstImage; int main( ) { system("color 5E"); g_srcImage= imread("dota_jugg.jpg"); if(!g_srcImage.data ) { printf("Oh,no,讀取g_srcImage圖片錯誤~!\n"); return -1; } g_dstImage= Mat::zeros( g_srcImage.size(), g_srcImage.type() ); //創建窗口 namedWindow("【原始圖窗口】", 1); //顯示圖像 imshow("【原始圖窗口】", g_srcImage); namedWindow(strWindowName); createTrackbar(strTraName,strWindowName,&g_nContraValue,100,ContrastAndBright); createTrackbar(strTraName1,strWindowName,&g_nBrightValue,100,ContrastAndBright); /*if(MultiChannelBlending( )) { cout<<endl<<"嗯。好了,得出了你需要的混合值圖像~"; }*/ //調用回調函數 waitKey(0); return 0; } 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_nContraValue*0.01)*(g_srcImage.at<Vec3b>(y,x)[c] ) + g_nBrightValue ); } } } imshow(strWindowName, g_dstImage); }
三、代碼分析
上述代碼中流程為:
1、獲取源圖像srcImage
2、創建以個目標圖像dstImage,全是0,所以是黑色圖像。
3、創建圖像窗口
4、創建軌跡條(Trackbar),更改對比度和亮度的值,同時函數有回調函數,當移動bar函數調用。
5、對比度、亮度修改函數。
6、顯示圖像
四、軌跡條(Trackbar)的創建和使用
C++: int createTrackbar(conststring& trackbarname, conststring& winname, int* value, int count, TrackbarCallback onChange=0,void* userdata=0); //第一個參數,const string&類型的trackbarname,表示軌跡條的名字,用來代表我們創建的軌跡條。 //第二個參數,const string&類型的winname,填窗口的名字,表示這個軌跡條會依附到哪個窗口上,即對應namedWindow()創建窗口時填的某一個窗口名。 ///第三個參數,int* 類型的value,一個指向整型的指針,表示滑塊的位置。並且在創建時,滑塊的初始位置就是該變量當前的值。 //第四個參數,int類型的count,表示滑塊可以達到的最大位置的值。PS:滑塊最小的位置的值始終為0。 //第五個參數,TrackbarCallback類型的onChange,首先注意他有默認值0。這是一個指向回調函數的指針,每次滑塊位置改變時,這個函數都會進行回調。並且這個函數的原型必須為void XXXX(int,void*);其中第一個參數是軌跡條的位置,第二個參數是用戶數據(看下面的第六個參數)。如果回調是NULL指針,表示沒有回調函數的調用,僅第三個參數value有變化。 //第六個參數,void*類型的userdata,他也有默認值0。這個參數是用戶傳給回調函數的數據,用來處理軌跡條事件。如果使用的第三個參數value實參是全局變量的話,完全可以不去管這個userdata參數。
這個createTrackbar函數,為我們創建一個具有特定名稱和范圍的軌跡條(Trackbar,或者說是滑塊范圍控制工具),指定一個和軌跡條位置同步的變量。而且要指定回調函數onChange(第五個參數),在軌跡條位置改變的時候來調用這個回調函數。並且我們知道,創建的軌跡條顯示在指定的winname(第二個參數)所代表的窗口上。
C++: int getTrackbarPos(conststring& trackbarname, conststring& winname); //第一個參數,const string&類型的trackbarname,表示軌跡條的名字。 //第二個參數,const string&類型的winname,表示軌跡條的父窗口的名稱。