(一)HighGui是什麼?
HightGui是一個可以移植的圖形工具包。
OpenCV將與操作系統,文件系統,攝像機之類的硬件進行交互的一些函數納入HighGui(high-level graphical user interface)庫中,有了HighGui,我們可以方便的打開窗口,顯示圖像,讀出或者寫入圖像相關的文件(包含圖像與視屏),處理簡單的鼠標,光標和鍵盤事件。也可以使用HighGui創建其他一些很有用的控件,如滑動條,並把它加入窗口。
爲了好理解,我們認爲OpenCV中的HighGui可以分爲三部分-------硬件相關部分,文件部分以及圖像用戶界面部分
注:事實上,HighGui的結構實現與我們所表述的不盡相同,HighGui函數實際別劃分爲 “視屏輸入\輸出”,”圖像輸入\輸出“,”GUI工具“三部分。
(1),硬件相關部分最主要的就是對於攝像機的操作,在大多操作系統下,與攝像機交互是一件很複雜並且很痛苦的工作。HighGui提供了一種從攝像機中獲取圖像的簡單方法,所有繁瑣工作都在HighGui內部完成,這讓我們很開心。
(2),文件系統部分的主要工作是載入和保存圖像文件。HighGui一個很好的特點就是可以用讀取攝像機視屏相同的方法讀入視屏文件。這使得我們可以省去從各種特定設備中讀取數據的麻煩,而專心於我們感興趣的代碼部分,同樣地,HighGui爲我們提供了一對函數來讀入與保存圖像,這兩個函數根據文件名的後綴,自動完成所有編碼和解碼工作。
(3),HighGui的第三部分是窗口系統(或者成爲GUI),HighGui提供一些簡單的函數用來打開窗口以及將圖像顯示在窗口中。它同時給我們提供了爲窗口加入鼠標,鍵盤相應的方法。這些函數爲我們很快建立一個簡單的程序提供了很大的幫助
下面通過實現一些功能來介紹HighGui。
(1)創建窗口與釋放窗口
首先要做的就是在顯示屏上顯示一副圖像。可以使用cvNamedWindow()函數實現這個功能;釋放該窗口使用函數cvDestroyWindow()
int cvNamedWindow( const char * name, int flag = CV_WINDOW_AUTOSIZEd )
第一個參數::用來表示新窗口的名稱,這個名稱顯示在窗口的頂部,同時用作HighGui中其他函數調用窗口的句柄
/第二個參數::是一個標誌,用來表示是否需要使窗口大小自動適應讀入的圖像的大小,flag可以取0或者CV_WINDOW_AUTOSIZE
程序實例
cvNamedWindow("原圖",CV_WINDOW_AUTOSIZE );
cvDestroyWindow( const char * name); //參數爲要釋放的窗口名稱;
程序實例
cvDestroyWindow("原圖");
(2)載入圖像與保存圖像
爲了在窗口中顯示圖像,我們需要了解如何從磁盤中載入圖像,OpenCV爲我們提供了cvLoadImage()函數
IplImage* cvLoadImage(const char * name, intiscolor = CV_LOAD_IMAGE_COLOR );
第一個參數:表示該圖片顯示在那個窗口上。
第二個參數 : 第二個參數iscolor有幾個值可以選擇
1 默認情況下圖像是以每個通道8位,3個通道的形式被讀入;默認情況下是3通道,iscolor 的默認值是CV_LOAD_IMAGE_COLOR,這意味不管原始圖像的通道 數是多少,都會被轉換爲3通道讀入。
2 可以通過設置CV_LOAD_IMAGE_ANGDEPTH來讀入非8位的圖像。
3 也可以將iscolor設置爲CV_LOAD_IMAGE_GRAYSCALE將讀入圖像強制轉換爲單通道
4 也可以將iscolor設置爲CV_LOAD_IMAGE_ANYCOLOR則以保持原始圖像通道數的方式讀入
注意,當cvLoadImage()函數讀入失敗,並不會產生一個運行時錯誤,而是返回一個空指針。
對應函數cvSaveImage()實現保存圖像的功能
int cvSaveImage(const char* filename,const CvArr* image)
第一個參數:表示文件名
第二個參數:指向要存儲的圖像數據
(3)顯示圖像
cvShowImage()是顯示圖像的函數
void cvShowImage(const char* filename,const CvArr* image)
第一個參數:用來指定顯示圖像的窗口
第二個參數:用來指定要顯示的圖像
下面用前面提到的函數完成一個簡單的程序。這個程序通過命令行讀入文件名,創建窗口並且將圖像顯示在窗口中
#include<opencv2\highgui\highgui.hpp>
using namespace cv;
//-----------------------------------------------------------
//主函數:我們的程序從這裏開始運行
//-----------------------------------------------------------
int main(int argc,char** argv)
{
//創建一個名爲argv[1]的窗口
cvNamedWindow(argv[1],1); //在項目--->屬性--->配置屬性--->調試--->命令行參數---》-》-》中添加圖像的路徑“G:\Image\face2.jpg”
//從給頂的文件名稱中加載一副圖像
IplImage* img=cvLoadImage(argv[1]);
//在已命名的窗口中顯示已加載的圖像
cvShowImage(argv[1],img);
cvWaitKey(0);
//釋放窗口和圖像的內存
cvDestroyWindow(argv[1]);
cvReleaseImage(&img);
}
顯示結果::
在進入下一步之前,有一些其他函數需要了解,具體如下:
void cvMoveWindow( const char * name,int x,int y);,將窗口移動到其左上角爲x,y的位置。
void cvDestroyWindows(void); 是一個很有趣的清理函數,用來關閉所有窗口並且釋放窗口相關的內存空間。
(4)WaitKey
cvWaitKey()函數可以接受0這個參數,在這種情況下,程序會無限期的等待下去,直到用戶觸發一個按鍵。
注:這個參數就是克服了運行程序時窗口一閃而過的情況。
(5)鼠標事件
經過上述內容,現在我們已經可以顯示一張圖像給用戶,現在我們希望給用戶添加一些交互功能。由於我們在窗口環境下工作,我們已經知道如何使用cvWaitKey()函數來捕捉單次用戶觸發事件,我們下一步需要做的是如何捕捉以及響應鼠標事件。
與鍵盤事件響應不同,鼠標事件響應採用回調函數的方式來處理,即,爲了可以響應鼠標事件,首先必須創建一個回調函數,使鼠標點擊事件發生時OPenCV可以調用這個函數。創建這個函數後,需要在OpenCV中註冊這個函數,以便特定窗口被觸發鼠標事件以後,OpenCV可以正確的使用這個函數。
讓我們從這個回調函數開始,回調函數callback可以是滿足輸入參數以及返回參數類型的任何函數,這裏。這裏,我們必須清楚告訴回調函數觸發的事件以及觸發位置。函數還需要被告知,用戶是否在觸發鼠標事件時同時觸發了shift或者Alt等鍵。下面是回調函數必須符合的形式:
void CvMouseCallBack(
int event, //參數一必須爲下面表1中的一個值
int x, //被設置成事件發生時鼠標位置的x,y座標。注意:這些座標代表窗口中圖像的像素座標,與窗口的大小無關
int y,
int flags, // 參數flags,每一位指定了事件發生時的不同狀態。例如CV_EVENT_FLAG_SHIFTKTY的值爲16,表2列除了所有的標誌
void* param //改參數用來以任何方式傳遞額外的參數信息。通常情況下,當回調函數爲一個類的靜態成員函數時,在這種情況下,可能 需要傳遞這個類的指針以確定對哪一個類實例產生影響
);
現在,當回調函數被調用時,OpenCV會給函數傳入合適的值,第一個event必須爲下面表中的一個值。
事件名稱 | 數值 |
CV_EVENT_MOUSEMOVE |
0 |
CV_EVENT_LBUTTONDOWN |
1 |
CV_EVENT_RBUTTONDOWN |
2 |
CV_EVENTMBUTTONDOWN |
3 |
CV_EVENT_LBUTTONUP |
4 |
CV_EVENT_RBUTTONUP |
5 |
CV_EVENT_MBUTTONUP |
6 |
CV_EVENT_LBUTTONDBLCLK |
7 |
CV_EVENT_RBUTTONDBLCLK |
8 |
CV_EVENT_MBUTTONDBLCLK |
9 |
標誌名稱 |
數值 |
CV_EVENT_FLAG_LBUTTON |
1 |
CV_EVENT_FLAG_RBUTTON |
2 |
CV_EVENT_FLAG_MBUTTON |
4 |
CV_EVENT_FLAG_CTRLKEY |
8 |
CV_EVENT_FLAG_SHIFTKEY |
16 |
CV_EVENT_FLAG_ALTKEY |
32 |
下面,需要註冊回調函數到OpenCV中,實現註冊的函數是 cvSetMouseCallBack(),該函數需要三個參數
void cvSetMouseCallBack(
const char* window_name, //第一個函數指定了回調函數需要註冊到的窗口,也就是產生事件的窗口,只有在這個指定的窗口 //中觸發的事件纔會調用回調函數
CvMouseCallback on_mouse, //第二個參數爲回調函數
void* param = NULL //第三個參數用來傳遞額外的信息給前面提到的void* param參數
);
下面將給出一個簡單的程序,使得可以利用鼠標來畫方形,函數my_mouse_callback()用來響應鼠標事件,並且根據event來確定給出的響應。
/*----------------------------------------------------------------------------------------------------------------
*程序說明:
* 基於Opencv實現-----鼠標事件(利用鼠標在窗口畫矩形)
*開發環境:
* win7+vs2010+opencv2.4.8
*創建時間地點:
* 陝西師範大學 2017.3.20
*參考信息:
* Learning OpenCV
*作者:
* 李先生
------------------------------------------------------------------------------------------------------------------*/
#include<opencv2\highgui\highgui.hpp>
#include<cv.h>
using namespace cv;
/*------------------------------------------------------------------------------------------------------------------
* 宏定義
-------------------------------------------------------------------------------------------------------------------*/
CvRect box;
bool drawing_box = false;
/*------------------------------------------------------------------------------------------------------------------
*函數功能:
* 畫圖函數
*參數:
* IplImage* img; 畫圖的位置,及在那個圖片上畫圖
* CvRect rect 畫圖的參數信息
-------------------------------------------------------------------------------------------------------------------*/
void draw_box(IplImage* img,CvRect rect)
{
cvRectangle(img,cvPoint(box.x,box.y),cvPoint(box.x+box.width,box.y+box.height),cvScalar(0xff,0x00,0x00));/*red*/
}
/*------------------------------------------------------------------------------------------------------------------
*函數功能:
定義回掉函數my_mouse_callback();
*參數:
int event; 第一個參數必須是鼠標事件類型
int x; 事件發生時鼠標的橫座標位置
int y; 事件發生時縱座標的位置
int flags; 每一位制定了事件發生時的不同狀態
void* param; 額外參數信息
-------------------------------------------------------------------------------------------------------------------*/
void my_mouse_callback(int event,int x,int y,int flags,void* param)
{
IplImage* image = (IplImage*)param;
switch(event)
{
case CV_EVENT_MOUSEMOVE:
{
if(drawing_box)
{
box.height = x-box.x;
box.width = y-box.y;
}
}
break;
case CV_EVENT_LBUTTONDOWN:
{
drawing_box=true;
box = cvRect(x,y,0,0);
}
break;
case CV_EVENT_LBUTTONUP:
{
drawing_box=false;
if(box.width<0)
{
box.x+=box.width;
box.width*=-1;
}
if(box.height<0)
{
box.y+=box.height;
box.height*=-1;
}
draw_box(image,box);
}
break;
}
}
/********************************************************************************************************************
*函數功能:
主函數,我們的程序從這裏開始運行
********************************************************************************************************************/
int main(int argc,char* argv[])
{
box = cvRect(-1,-1,0,0); //創建一個矩陣
IplImage* image = cvCreateImage(cvSize(800,800),IPL_DEPTH_8U,3); //創建一副圖片
cvZero(image); //圖片矩陣的元素全部置0
IplImage* temp = cvCloneImage(image); //克隆一個臨時圖片
cvNamedWindow("Box Example"); //創建一個顯示窗口
cvSetMouseCallback("Box Example",my_mouse_callback,(void*)image); //調用OpenCV中的實現註冊函數,
//這個函數是OpenCV函數庫的函數
while(1)
{
cvCopyImage(image,temp); //這一步很重要,但是我現在還是說不清楚它的含義,有看過博客且明白的高手並且有時間請指教,沒加這句,顯示結果是圖二
if(drawing_box)
draw_box(temp,box);
cvShowImage("Box Example",temp);
if(cvWaitKey(15)==27)break;
}
cvReleaseImage(&image);
cvReleaseImage(&temp);
cvDestroyWindow("Box Example");
}
顯示結果
圖一
圖二