學習如何在opencv 中用trackbar 函數創建和使用 軌跡條,以及圖像對比度,亮度值的動態調整
一、OpenCV中軌跡條(Trackbar)的創建和使用
【1】創建軌跡條-----createTrackbar 函數詳解
createTrackbar這個函數我們以后會經常用到,它創建一個可以調整的軌跡條,並將軌跡條附加到指定的窗口上,使用起來方便,首先,它往往會和一個回調函數 配合起來使用,先看它的函數原型:
1 int createTrackbar(conststring &trackerbarname,conststring &winname,int *value,int count ,TrackerbarCallback 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_nContrastVaule,300,ContrasAndBright); 3 //g_nContrastVaule為全局的整形變量,ContrasAndBright為回調函數的函數名(即指向函數地址的指針)
然給大家一個完整的使用示例。這是OpenCV官方的sample示例程序,
1 #include <opencv2/core/utility.hpp>
2 #include "opencv2/imgproc.hpp"
3 #include "opencv2/imgcodecs.hpp"
4 #include "opencv2/highgui.hpp"
5 #include <iostream>
6
7 using namespace cv; 8 using namespace std; 9
10 Mat img; 11 int threshval = 100; 12
13 static void on_trackbar(int, void*) 14 { 15 Mat bw = threshval < 128 ? (img < threshval) : (img > threshval); 16 Mat labelImage(img.size(), CV_32S); 17 int nLabels = connectedComponents(bw, labelImage, 8); 18 std::vector<Vec3b> colors(nLabels); 19 colors[0] = Vec3b(0, 0, 0);//background
20 for(int label = 1; label < nLabels; ++label){ 21 colors[label] = Vec3b( (rand()&255), (rand()&255), (rand()&255) ); 22 } 23 Mat dst(img.size(), CV_8UC3); 24 for(int r = 0; r < dst.rows; ++r){ 25 for(int c = 0; c < dst.cols; ++c){ 26 int label = labelImage.at<int>(r, c); 27 Vec3b &pixel = dst.at<Vec3b>(r, c); 28 pixel = colors[label]; 29 } 30 } 31
32 imshow( "Connected Components", dst ); 33 } 34
35 static void help() 36 { 37 cout << "\n This program demonstrates connected components and use of the trackbar\n"
38 "Usage: \n"
39 " ./connected_components <image(../data/stuff.jpg as default)>\n"
40 "The image is converted to grayscale and displayed, another image has a trackbar\n"
41 "that controls thresholding and thereby the extracted contours which are drawn in color\n"; 42 } 43
44 const char* keys =
45 { 46 "{help h||}{@image|../data/stuff.jpg|image for converting to a grayscale}"
47 }; 48
49 int main( int argc, const char** argv ) 50 { 51 CommandLineParser parser(argc, argv, keys); 52 if (parser.has("help")) 53 { 54 help(); 55 return 0; 56 } 57 string inputImage = parser.get<string>(0); 58 img = imread(inputImage.c_str(), 0); 59
60 if(img.empty()) 61 { 62 cout << "Could not read input image file: " << inputImage << endl; 63 return -1; 64 } 65
66 namedWindow( "Image", 1 ); 67 imshow( "Image", img ); 68
69 namedWindow( "Connected Components", 1 ); 70 createTrackbar( "Threshold", "Connected Components", &threshval, 255, on_trackbar ); 71 on_trackbar(threshval, 0); 72
73 waitKey(0); 74 return 0; 75 }
這是3 版本的代碼,因為按照淺墨大神的優化代碼移植到這里有點問題,所以先放官方代碼
<2>獲取當前軌跡條的位置——getTrackbarPos函數
這個函數用於獲取當前軌跡條的位置並返回
1 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 (int y = 0; y < g_srcImage.rows; y++) 2 { 3 for (int x = 0; x < g_srcImage.cols; x++) 4 { 5 for (int c = 0; c < 3; c++) 6 { 7 g_dstImage.at<Vec3b>(y, x)[c] = saturate_cast<uchar>((g_nContrasaValue*0.01) * (g_srcImage.at<Vec3b>(y,x)[c]) + g_nBrightValue); 8 } 9 } 10 }
讓我們分3個方面進行講解:
-----為了訪問圖像的每一個像素,我們使用這樣的語法:image.at<Vec3b>(y,x)[c].其中,y是像素所在的行, x是像素所在的列, c是R、G、B(對應0、1、2)其中之一。
-----因為我們的運算結果可能超出像素取值范圍(溢出),還可能是非整數(如果是浮點數的話),所以我們要用staurate_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 創建Trackbar && 圖像對比度,亮度值調整 3 -----------------------------------------------------*/
4
5 #include <opencv2/core/core.hpp>
6 #include <opencv2/highgui/highgui.hpp>
7 #include "opencv2/imgproc/imgproc.hpp" //圖像處理模塊
8 #include <iostream>
9
10 using namespace cv; 11 using namespace std; 12
13 static void ContrastAndBright(int, void *); //全局函數,回調函數,對比 &&亮度 14
15 //全局變量
16 int g_nContrastValue; //對比度值
17 int g_nBrightValue; //亮度
18 Mat g_srcImage, g_dstImage; 19
20
21 /*---------------------------------------------------------- 22 描述:改變圖像對比度和亮度值的回調函數 23 -----------------------------------------------------------*/
24 static void ContrastAndBright(int, void*) 25 { 26 //創建窗口
27 namedWindow("【原始窗口】",1); 28
29 //3個for循環,執行運算 g_Image(i,j) = a*g_srcImage(i,j) + b
30 for (int y = 0; y < g_srcImage.rows; y++) 31 { 32 for (int x = 0; x < g_srcImage.cols; x++) 33 { 34 for (int c = 0; c < 3; c++) 35 { 36 g_dstImage.at<Vec3b>(y, x)[c] = saturate_cast<uchar>((g_nContrasaValue*0.01) * (g_srcImage.at<Vec3b>(y,x)[c]) + g_nBrightValue); 37 } 38 } 39 } 40
41 //顯示圖像
42 imshow("【原始圖像 窗口】",g_srcImage); 43 imshow("【效果圖像 窗口】",g_dstImage); 44
45
46 } 47
48
49 int main() 50 { 51 //改變控制台前景色和背景色
52 system("color 5F"); 53
54 //讀入用戶提供的圖像
55 g_srcImage = imread("pic1.jpg"); 56 if (!g_srcImage.data) 57 { 58 printf("Oh,no,讀取g_srcImage圖片錯誤~! \n"); 59 return false; 60 } 61 //這個函數還不是太清楚,zeros是什么鬼???輸出圖像
62 g_dstImage = Mat::zeros(g_srcImage.size(), g_srcImage.type()); 63
64 //設定對比度和亮度的初值
65 g_nContrastValue = 80; 66 g_nBrightValue = 80; 67
68 //創建窗口
69 namedWindow("【效果圖窗口】", 1); 70
71 //創建軌跡條
72 createTrackbar("對比度:", "【效果圖窗口】", &g_nContrastValue, 300, ContrastAndBright); 73 createTrackbar("亮 度:", "【效果圖窗口】", &g_nBrightValue, 200, ContrastAndBright); 74
75 //調用回調函數
76 ContrastAndBright(g_nContrastValue, 0); 77 ContrastAndBright(g_nBrightValue, 0); 78
79 //輸出一些幫助信息
80 cout << endl << "\t嗯。好了,請調整滾動條觀察圖像效果~\n\n"
81 << "\t按下“q”鍵時,程序退出~!\n"
82 << "\n\n\t\t\t\t byhehheh"; 83
84 //按下 q 時,程序退出
85 while (char (waitKey(1)) != 'q'); 86 return 0; 87}