【OpenCV入門教程之六】 創建Trackbar & 圖像對比度、亮度值調整

轉自http://blog.csdn.net/poem_qianmo/article/details/21479533

本系列文章由@淺墨_毛星雲 出品,轉載請註明出處。  

文章鏈接: http://blog.csdn.net/poem_qianmo/article/details/21479533

作者:毛星雲(淺墨)    郵箱: [email protected] 

寫作當前博文時配套使用的OpenCV版本: 2.4.8


 

 

這篇文章中我們一起學習瞭如何在OpenCV中用createTrackbar函數創建和使用軌跡條,以及圖像對比度、亮度值的動態調整。

文章首先詳細講解了OpenCV2.0中的新版創建軌跡條的函數createTrackbar,並給上一個詳細註釋的示例。

然後講解圖像的對比度、亮度值調整的細節,最後放出了一個利用createTrackbar函數創建軌跡條來輔助進行圖像對比度、亮度值調整的程序源碼。

依然是先放一張運行截圖:

 


好了,下面正式開始我們的講解。






一、OpenCV中軌跡條(Trackbar)的創建和使用

 




<1>創建軌跡條——createTrackbar函數詳解



createTrackbar這個函數我們以後會經常用到,它創建一個可以調整數值的軌跡條,並將軌跡條附加到指定的窗口上,使用起來很方便。首先大家要記住,它往往會和一個回調函數配合起來使用。先看下他的函數原型:

 

  1. C++: int createTrackbar(conststring& trackbarname, conststring& winname,  
  2.  int* value, int count, TrackbarCallback onChange=0,void* userdata=0);  
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(第二個參數)所代表的窗口上。

 

看完函數講解,先給大家一個函數使用小示例:

 

     

  1.  //創建軌跡條  
  2.        createTrackbar("對比度:""【效果圖窗口】",&g_nContrastValue,  
  3. 300,ContrastAndBright );// g_nContrastValue爲全局的整型變量,ContrastAndBright爲回調函數的函數名(即指向函數地址的指針)  
 //創建軌跡條
       createTrackbar("對比度:", "【效果圖窗口】",&g_nContrastValue,
300,ContrastAndBright );// g_nContrastValue爲全局的整型變量,ContrastAndBright爲回調函數的函數名(即指向函數地址的指針)


 

然給大家一個完整的使用示例。這是OpenCV官方的sample示例程序,一個演示了用軌跡條來控制輪廓檢測,輪廓填充的程序。淺墨將其修改、代碼簡潔化和詳細註釋,放出來供大家消化研習。稍後更新的博文會有關於輪廓檢測更詳細的講解。

 

  1. //-----------------------------------【頭文件包含部分】---------------------------------------  
  2. //  描述:包含程序所依賴的頭文件  
  3. //----------------------------------------------------------------------------------------------   
  4. #include "opencv2/imgproc/imgproc.hpp"  
  5. #include "opencv2/highgui/highgui.hpp"  
  6. #include <iostream>  
  7.   
  8. //-----------------------------------【命名空間聲明部分】---------------------------------------  
  9. //  描述:包含程序所使用的命名空間  
  10. //-----------------------------------------------------------------------------------------------     
  11. using namespace cv;  
  12. using namespace std;  
  13.   
  14. //-----------------------------------【全局函數聲明部分】--------------------------------------  
  15. //  描述:全局函數聲明  
  16. //-----------------------------------------------------------------------------------------------  
  17. Mat img;  
  18. int threshval = 160;            //軌跡條滑塊對應的值,給初值160  
  19.   
  20. //-----------------------------【on_trackbar( )函數】------------------------------------  
  21. //  描述:軌跡條的回調函數  
  22. //-----------------------------------------------------------------------------------------------  
  23. static void on_trackbar(intvoid*)  
  24. {  
  25.     Mat bw = threshval < 128 ? (img < threshval) : (img > threshval);  
  26.   
  27.     //定義點和向量  
  28.     vector<vector<Point> > contours;  
  29.     vector<Vec4i> hierarchy;  
  30.   
  31.     //查找輪廓  
  32.     findContours( bw, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );  
  33.     //初始化dst  
  34.     Mat dst = Mat::zeros(img.size(), CV_8UC3);  
  35.     //開始處理  
  36.     if( !contours.empty() && !hierarchy.empty() )  
  37.     {  
  38.         //遍歷所有頂層輪廓,隨機生成顏色值繪製給各連接組成部分  
  39.         int idx = 0;  
  40.         for( ; idx >= 0; idx = hierarchy[idx][0] )  
  41.         {  
  42.             Scalar color( (rand()&255), (rand()&255), (rand()&255) );  
  43.             //繪製填充輪廓  
  44.             drawContours( dst, contours, idx, color, CV_FILLED, 8, hierarchy );  
  45.         }  
  46.     }  
  47.     //顯示窗口  
  48.     imshow( "Connected Components", dst );  
  49. }  
  50.   
  51.   
  52. //-----------------------------------【main( )函數】--------------------------------------------  
  53. //  描述:控制檯應用程序的入口函數,我們的程序從這裏開始  
  54. //-----------------------------------------------------------------------------------------------  
  55. int main(  )  
  56. {  
  57.     system("color 5F");    
  58.     //載入圖片  
  59.     img = imread("1.jpg", 0);  
  60.     if( !img.data ) { printf("Oh,no,讀取img圖片文件錯誤~! \n"); return -1; }  
  61.   
  62.     //顯示原圖  
  63.     namedWindow( "Image", 1 );  
  64.     imshow( "Image", img );  
  65.   
  66.     //創建處理窗口  
  67.     namedWindow( "Connected Components", 1 );  
  68.     //創建軌跡條  
  69.     createTrackbar( "Threshold""Connected Components", &threshval, 255, on_trackbar );  
  70.     on_trackbar(threshval, 0);//軌跡條回調函數  
  71.   
  72.     waitKey(0);  
  73.     return 0;  
  74. }  
//-----------------------------------【頭文件包含部分】---------------------------------------
//	描述:包含程序所依賴的頭文件
//---------------------------------------------------------------------------------------------- 
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>

//-----------------------------------【命名空間聲明部分】---------------------------------------
//	描述:包含程序所使用的命名空間
//-----------------------------------------------------------------------------------------------   
using namespace cv;
using namespace std;

//-----------------------------------【全局函數聲明部分】--------------------------------------
//	描述:全局函數聲明
//-----------------------------------------------------------------------------------------------
Mat img;
int threshval = 160;			//軌跡條滑塊對應的值,給初值160

//-----------------------------【on_trackbar( )函數】------------------------------------
//	描述:軌跡條的回調函數
//-----------------------------------------------------------------------------------------------
static void on_trackbar(int, void*)
{
	Mat bw = threshval < 128 ? (img < threshval) : (img > threshval);

	//定義點和向量
	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;

	//查找輪廓
	findContours( bw, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
	//初始化dst
	Mat dst = Mat::zeros(img.size(), CV_8UC3);
	//開始處理
	if( !contours.empty() && !hierarchy.empty() )
	{
		//遍歷所有頂層輪廓,隨機生成顏色值繪製給各連接組成部分
		int idx = 0;
		for( ; idx >= 0; idx = hierarchy[idx][0] )
		{
			Scalar color( (rand()&255), (rand()&255), (rand()&255) );
			//繪製填充輪廓
			drawContours( dst, contours, idx, color, CV_FILLED, 8, hierarchy );
		}
	}
	//顯示窗口
	imshow( "Connected Components", dst );
}


//-----------------------------------【main( )函數】--------------------------------------------
//	描述:控制檯應用程序的入口函數,我們的程序從這裏開始
//-----------------------------------------------------------------------------------------------
int main(  )
{
	system("color 5F");  
	//載入圖片
	img = imread("1.jpg", 0);
	if( !img.data ) { printf("Oh,no,讀取img圖片文件錯誤~! \n"); return -1; }

	//顯示原圖
	namedWindow( "Image", 1 );
	imshow( "Image", img );

	//創建處理窗口
	namedWindow( "Connected Components", 1 );
	//創建軌跡條
	createTrackbar( "Threshold", "Connected Components", &threshval, 255, on_trackbar );
	on_trackbar(threshval, 0);//軌跡條回調函數

	waitKey(0);
	return 0;
}


原圖:


運行效果圖:



拖動滾動條,改變threshval(閾值)的值,得到效果迥異的圖片:





想要下載這個程序源工程的童鞋請點擊這裏:

 

createTrackbarDemo【 淺墨優化&詳細註釋版】下載

 


另外,在OpenCV路徑opencv_source_code/samples/cpp/connected_components.cpp下,可以找到原版的官方代碼。

 

接着順便講一個配合createTrackbar使用的函數,用於獲取當前軌跡條的位置的getTrackbarPos函數吧。

 




<2>獲取當前軌跡條的位置——getTrackbarPos函數



這個函數用於獲取當前軌跡條的位置並返回。

 

  1. C++: int getTrackbarPos(conststring& trackbarname, conststring& winname);  
C++: int getTrackbarPos(conststring& trackbarname, conststring& winname);

  • 第一個參數,const string&類型的trackbarname,表示軌跡條的名字。
  • 第二個參數,const string&類型的winname,表示軌跡條的父窗口的名稱。

 這部分大概就是這些了。馬不停蹄地向下一部分進發吧:)

 

 

 



二、亮度和對比度調整的理論依據

 



首先我們給出算子的概念。一般的圖像處理算子都是一個函數,它接受一個或多個輸入圖像,併產生輸出圖像。下式給出了算子的一般形式:


或者

 

今天我們所講解的圖像亮度和對比度的調整操作,其實屬於圖像處理變換中比較簡單的一種——點操作(pointoperators)。點操作有一個特點,僅僅根據輸入像素值(有時可加上某些全局信息或參數),來計算相應的輸出像素值。這類算子包括亮度(brightness和對比度contrast調整,以及顏色校正(colorcorrection)和變換(transformations)。

 

最兩種常用的點操作(或者說點算子),很顯然,是乘上一個常數(對應對比度的調節)以及加上一個常數(對應亮度值的調節)。用公式表示出來就是這樣:


 

 

看到這個式子,我們關於圖像亮度和對比度調整的策略就呼之欲出了。


其中:

  • 參數f(x)表示源圖像像素。
  • 參數g(x) 表示輸出圖像像素。
  • 參數a(需要滿足a>0)被稱爲增益(gain),常常被用來控制圖像的對比度。
  • 參數b通常被稱爲偏置(bias),常常被用來控制圖像的亮度。

 

而更近一步,我們這樣改寫這個式子:


其中,i 和 j 表示像素位於第i行 和 第j列 。

那麼,這個式子就可以用來作爲我們在OpenCV中控制圖像的亮度和對比度的理論公式了。

 





三、關於訪問圖片中的像素

 


訪問圖片中的像素有很多種方式,以後有機會淺墨會用個專題來講解。目前我們可以先了解下面的這一種。

 

而爲了執行  這個運算  ,我們需要訪問圖像的每一個像素。因爲是對GBR圖像進行運算,每個像素有三個值(G、B、R),所以我們必須分別訪問它們(PS:OpenCV中的圖像存儲模式爲GBR)。以下是訪問像素的代碼片段,三個for循環解決問題:

 

  1. //三個for循環,執行運算 new_image(i,j) =a*image(i,j) + b  
  2. for(int y = 0; y < image.rows; y++ )  
  3. {  
  4.        for(int x = 0; x < image.cols; x++ )  
  5.        {  
  6.               for(int c = 0; c < 3; c++ )  
  7.               {  
  8.                      new_image.at<Vec3b>(y,x)[c]= saturate_cast<uchar>( (g_nContrastValue*0.01)*(image.at<Vec3b>(y,x)[c] ) + g_nBrightValue );  
  9.               }  
  10.        }  
  11. }  
       //三個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對結果進行轉換,以確保它爲有效值。
  • 這裏的a也就是對比度,一般爲了觀察的效果,取值爲0.0到3.0的浮點值,但是我們的軌跡條一般取值都會整數,所以在這裏我們可以,將其代表對比度值的nContrastValue參數設爲0到300之間的整型,在最後的式子中乘以一個0.01,這樣就可以完成軌跡條中300個不同取值的變化。所以在式子中,我們會看到saturate_cast<uchar>( (g_nContrastValue*0.01)*(image.at<Vec3b>(y,x)[c] ) + g_nBrightValue )中的g_nContrastValue*0.01。





四、圖像對比度、亮度值調整示例程序





依然是每篇文章都會配給大家的一個詳細註釋的博文配套示例程序,把這篇文章中介紹的知識點以代碼爲載體,展現給大家。

這個示例程序用兩個軌跡條分別控制對比度和亮度值,有一定的可玩性。廢話不多說,上代碼吧:

 

  1. //-----------------------------------【程序說明】----------------------------------------------  
  2. //  程序名稱::【OpenCV入門教程之四】 創建Trackbar&圖像對比度、亮度值調整 配套博文源碼  
  3. // VS2010版  OpenCV版本:2.4.8  
  4. //  2014年3月18 日 Create by 淺墨  
  5. //------------------------------------------------------------------------------------------------  
  6.    
  7.    
  8. //-----------------------------------【頭文件包含部分】---------------------------------------  
  9. //     描述:包含程序所依賴的頭文件  
  10. //----------------------------------------------------------------------------------------------  
  11. #include <opencv2/core/core.hpp>  
  12. #include<opencv2/highgui/highgui.hpp>  
  13. #include"opencv2/imgproc/imgproc.hpp"  
  14. #include <iostream>  
  15.    
  16. //-----------------------------------【命名空間聲明部分】---------------------------------------  
  17. //     描述:包含程序所使用的命名空間  
  18. //-----------------------------------------------------------------------------------------------    
  19. using namespace std;  
  20. using namespace cv;  
  21.    
  22.    
  23. //-----------------------------------【全局函數聲明部分】--------------------------------------  
  24. //     描述:全局函數聲明  
  25. //-----------------------------------------------------------------------------------------------  
  26. static void ContrastAndBright(intvoid *);  
  27.    
  28. //-----------------------------------【全局變量聲明部分】--------------------------------------  
  29. //     描述:全局變量聲明  
  30. //-----------------------------------------------------------------------------------------------  
  31. int g_nContrastValue; //對比度值  
  32. int g_nBrightValue;  //亮度值  
  33. Mat g_srcImage,g_dstImage;  
  34. //-----------------------------------【main( )函數】--------------------------------------------  
  35. //     描述:控制檯應用程序的入口函數,我們的程序從這裏開始  
  36. //-----------------------------------------------------------------------------------------------  
  37. int main(  )  
  38. {  
  39.        //改變控制檯前景色和背景色  
  40.        system("color5F");   
  41.    
  42.        //讀入用戶提供的圖像  
  43.        g_srcImage= imread( "pic1.jpg");  
  44.               if(!g_srcImage.data ) { printf("Oh,no,讀取g_srcImage圖片錯誤~!\n"); return false; }  
  45.        g_dstImage= Mat::zeros( g_srcImage.size(), g_srcImage.type() );  
  46.    
  47.        //設定對比度和亮度的初值  
  48.        g_nContrastValue=80;  
  49.        g_nBrightValue=80;  
  50.    
  51.        //創建窗口  
  52.        namedWindow("【效果圖窗口】", 1);  
  53.    
  54.        //創建軌跡條  
  55.        createTrackbar("對比度:""【效果圖窗口】",&g_nContrastValue,300,ContrastAndBright );  
  56.        createTrackbar("亮   度:","【效果圖窗口】",&g_nBrightValue,200,ContrastAndBright );  
  57.         
  58.        //調用回調函數  
  59.        ContrastAndBright(g_nContrastValue,0);  
  60.        ContrastAndBright(g_nBrightValue,0);  
  61.         
  62.        //輸出一些幫助信息  
  63.        cout<<endl<<"\t嗯。好了,請調整滾動條觀察圖像效果~\n\n"  
  64.                      <<"\t按下“q”鍵時,程序退出~!\n"  
  65.                      <<"\n\n\t\t\t\tby淺墨";  
  66.    
  67.        //按下“q”鍵時,程序退出  
  68.    while(char(waitKey(1)) != 'q') {}  
  69.        return0;  
  70. }  
  71.    
  72.    
  73. //-----------------------------【ContrastAndBright( )函數】------------------------------------  
  74. //     描述:改變圖像對比度和亮度值的回調函數  
  75. //-----------------------------------------------------------------------------------------------  
  76. static void ContrastAndBright(intvoid *)  
  77. {  
  78.    
  79.        //創建窗口  
  80.        namedWindow("【原始圖窗口】", 1);  
  81.    
  82.        //三個for循環,執行運算 g_dstImage(i,j) =a*g_srcImage(i,j) + b  
  83.        for(int y = 0; y < g_srcImage.rows; y++ )  
  84.        {  
  85.               for(int x = 0; x < g_srcImage.cols; x++ )  
  86.               {  
  87.                      for(int c = 0; c < 3; c++ )  
  88.                      {  
  89.                             g_dstImage.at<Vec3b>(y,x)[c]= saturate_cast<uchar>( (g_nContrastValue*0.01)*(g_srcImage.at<Vec3b>(y,x)[c] ) + g_nBrightValue );  
  90.                      }  
  91.               }  
  92.        }  
  93.    
  94.        //顯示圖像  
  95.        imshow("【原始圖窗口】", g_srcImage);  
  96.        imshow("【效果圖窗口】", g_dstImage);  
  97. }  
//-----------------------------------【程序說明】----------------------------------------------
//  程序名稱::【OpenCV入門教程之四】 創建Trackbar&圖像對比度、亮度值調整 配套博文源碼
// VS2010版  OpenCV版本:2.4.8
//  2014年3月18 日 Create by 淺墨
//------------------------------------------------------------------------------------------------
 
 
//-----------------------------------【頭文件包含部分】---------------------------------------
//     描述:包含程序所依賴的頭文件
//----------------------------------------------------------------------------------------------
#include <opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include"opencv2/imgproc/imgproc.hpp"
#include <iostream>
 
//-----------------------------------【命名空間聲明部分】---------------------------------------
//     描述:包含程序所使用的命名空間
//-----------------------------------------------------------------------------------------------  
using namespace std;
using namespace cv;
 
 
//-----------------------------------【全局函數聲明部分】--------------------------------------
//     描述:全局函數聲明
//-----------------------------------------------------------------------------------------------
static void ContrastAndBright(int, void *);
 
//-----------------------------------【全局變量聲明部分】--------------------------------------
//     描述:全局變量聲明
//-----------------------------------------------------------------------------------------------
int g_nContrastValue; //對比度值
int g_nBrightValue;  //亮度值
Mat g_srcImage,g_dstImage;
//-----------------------------------【main( )函數】--------------------------------------------
//     描述:控制檯應用程序的入口函數,我們的程序從這裏開始
//-----------------------------------------------------------------------------------------------
int main(  )
{
       //改變控制檯前景色和背景色
       system("color5F"); 
 
       //讀入用戶提供的圖像
       g_srcImage= imread( "pic1.jpg");
              if(!g_srcImage.data ) { printf("Oh,no,讀取g_srcImage圖片錯誤~!\n"); return false; }
       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);
      
       //輸出一些幫助信息
       cout<<endl<<"\t嗯。好了,請調整滾動條觀察圖像效果~\n\n"
                     <<"\t按下“q”鍵時,程序退出~!\n"
                     <<"\n\n\t\t\t\tby淺墨";
 
       //按下“q”鍵時,程序退出
   while(char(waitKey(1)) != 'q') {}
       return0;
}
 
 
//-----------------------------【ContrastAndBright( )函數】------------------------------------
//     描述:改變圖像對比度和亮度值的回調函數
//-----------------------------------------------------------------------------------------------
static void ContrastAndBright(int, void *)
{
 
       //創建窗口
       namedWindow("【原始圖窗口】", 1);
 
       //三個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);
}


最後看一下運行截圖,運行這個程序會得到兩個圖片顯示窗口。第一個爲原圖窗口,第二個爲效果圖窗口。在效果圖窗口中可以調節兩個軌跡條,來改變當前圖片的對比度和亮度。

原圖:



可調節的效果圖:







本篇文章到這裏就基本結束了,最後放出本篇文章配套示例程序的下載地址。



本篇文章的配套源代碼請點擊這裏下載:



【淺墨OpenCV入門教程之六】配套源代碼下載



OK,本節的內容大概就是這些,我們下篇文章見:)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章