2008年6月14日 星期六

GUI介面的製作-圖片開啟

在這裡,要放的焦點就是"highgui.h"這個函式庫啦,"highgui.h"提供了許多基本的功能,實際上用起來也不麻煩,主要是簡單好用吧,但功能性沒有很強大,不比Visual C++的mfc及C++ Builder的vcl好用的多,如果真的需要強大的GUI功能,則就把OpenCV引入Visual C++及C++ Builder的函式庫吧.這邊所提供,OpenCV的GUI介面功能分為圖片(Image),視訊(Capture),視窗(Form),拉軸(TrackBar),滑鼠,鍵盤,AVI檔案的播放,有點少,不過夠用,我們拿前面的RainMan實例來做GUI介面的說明.

RainMan灰階(視窗,圖形操作)
#include <stdio.h>
#include <cv.h>
#include <highgui.h>


void * WindowHandle1;
char FileName[]="rain.jpg";
IplImage *Image1;

int main()
{
    Image1 = cvLoadImage(FileName,CV_LOAD_IMAGE_GRAYSCALE);

    cvNamedWindow("Show Image",0);
    cvMoveWindow("Show Image",0,0);
    cvResizeWindow("Show Image",300,400);

    WindowHandle1=cvGetWindowHandle("Show Image");
    printf("The Window Handle is : %d\n",WindowHandle1);
    printf("The Window Name is : %s\n",cvGetWindowName(WindowHandle1));
    cvShowImage("Show Image",Image1);

    cvWaitKey(0);

    cvDestroyWindow("Show Image");
    cvReleaseImage(&Image1);
}

執行結果:


這裡用了許多"highgui.h"現有的程式庫,如
cvLoadImage()
cvNamedWindow()
cvMoveWindow()
cvResizeWindow()
cvGetWindowHandle()
cvGetWindowName()
cvShowImage()
cvWaitKey()
cvDestroyWindow()
cvReleaseImage()
也可以說,除了IplImage及printf()其他都是"highgui.h"內建的函式庫,程式碼的一開始,就用(void *)型別建立了一個Window Handle的視窗ID變數,接著就對視窗作位移及縮放,再來拿到widow handle ID,再用handle ID去搜尋視窗字串名稱,顯視圖片,執行鍵盤事件,清空記憶體,這邊搜尋Window Handle的好處是,可以對視窗做一些基本的溝通,但這是在Win32裡面的教學,不在OpenCV的範疇之內.接著來逐一說明各函式的功用

cvLoadImage()
顧名思義,就是載入圖片的意思,它的使用方法為
IplImage* cvLoadImage("檔案名稱",參數);
參數的部份可以參考命名規則的說明,回傳的訊息是IplImage資料結構,它的參數分類有

#define CV_LOAD_IMAGE_UNCHANGED -1        原圖影像
#define CV_LOAD_IMAGE_GRAYSCALE 0             灰階
#define CV_LOAD_IMAGE_COLOR 1                    彩色
#define CV_LOAD_IMAGE_ANYDEPTH 2           任何彩度
#define CV_LOAD_IMAGE_ANYCOLOR 4           任何彩色

原始影像如果是灰階圖的話會因為檔案格式的關係而不可能變成彩色,但彩色圖片卻可以任意變成灰階,參數的部份可以用大寫英文或數字來替代.

cvNamedWindow()
這個副程式,是給視窗化介面命名的副程式,用法如下
cvNamedWindow("視窗名稱",參數);
而它的參數實質上只有一個

#define CV_WINDOW_AUTOSIZE 1            自動調整圖形大小

AUTOSIZE可以讓圖片便成原圖大小,也就是圖形維度不做調整,但是圖片太大很容易造成困擾,就如RainMan,維度為1200*1600,會造成圖片佔據了整個螢幕的版面.如果要微調就要用到cvResizeWindow()這個函式,但是,cvNamedWindow()不能設成1(CV_WINDOW_AUTOSIZE),務必將參數設為0或其他非1的數字.

cvMoveWindow()
移動GUI視窗到"螢幕座標"上的位置.當我們移動滑鼠時,實際上是在Windows作業系統底下的座標軸在變換,這個函式可以將做出來的GUI視窗以右上角為準移動到指定螢幕座標的地方.使用方法:
cvMoveWindow("視窗名稱",螢幕X軸數字,螢幕Y軸數字);

cvResizeWindow()
將視窗作縮放的動作,圖片將會等比例的變換,但cvNamedWindow()參數必須設為非1.
cvResizeWindow("視窗名稱",縮放寬度,縮放高度);

cvGetWindowHandle()
一個視窗所產生的ID,當我們創立一個視窗的時候,作業系統個隨機給予一個視窗ID,每次重新打開這個ID都會不一樣,可以把它視為純數字,不過,通常都是用(void *)資料型態來設立,這邊就是用視窗名稱來找視窗ID
cvGetWindowHandle("視窗名稱");

cvGetWindowName()
用ID來反查視窗名稱.
cvGetWindowName(視窗ID);

cvShowImage()
顯視圖片.
cvShowImage("視窗名稱",IplImage資料結構或CvArr資料結構);

cvWaitKey()
鍵盤事件,秀出圖形必備,這邊如果有開啟cvShowImage()的話,就必須要用到cvWaitKey(),而不能用"stdlib.h"裡的system("pause")取代,cvWaitKey()是專門在在OpenCV專用的GUI介面底下等待鍵盤命令的,而不是在黑白環境下的命令提示字元,鍵盤事件有兩種,阻斷式(block)的跟非阻斷式(unblock)的可以在作業系統原理的相關書籍讀到,阻斷式的就是cvWaitKey(0),它會一直等待到鍵盤事件發生為止,另一種就是在裡面輸入大於零的數字cvWaitKey(10),它將會等到10毫秒後自動輸出-1,代表10毫秒內沒接收到鍵盤敲擊事件,因此,典型的副程式表示法為
cvWaitKey(數字);

cvDestroyWindow()
清除視窗記憶體
cvDestroyWindow("視窗名稱')

cvReleaseImage()
清除IplImage圖形資料結構記憶體
cvReleaseImage(IplImage資料結構名稱)



5 意見:

风在竹林 提到...

您好,看了您的文章,很受教,多谢了!有个小问题:
在您此篇的例子中,main函数需要返回一个int型的数,而您没有返回,是不是有什么用意?

yester 提到...

呵呵 可以把return刪掉這算是基本常識了,不過很少人會想去了解這是什麼意義,在c語言的driver(main)程式執行結束後要返回這支程式執行結果是否正確(true or false),也就是說~當你.EXE檔程式執行完之後會還傳一個值,0或是1代表著程式是否執行成功,當然啦~這個聽起來似乎是沒什麼意義,因為在執行執行檔完之後,就可以直接知道這支程式到底是否執行成功(每一行程式碼全部跑完)了,而且如果要回傳結果的話也需要一個變數去接,一般編譯成執行檔很少人會刻意設計一個在.EXE檔前面用變數接,尤其在windows的環境底下用滑鼠點擊開啟執行檔的方式更顯得沒有意義= =當然~不寫return這在程式的邏輯設計是不好的,由於dev-c++規定一定要用int來宣告main(),所以說~有return 0 (執行成功)會是一個比較好的習慣

在stdlib.h裡面程式結束回傳結果是這樣定義的

#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1

所以會跟一般0是false,1是true是相反的.

從計算機架構的角度來講,return只是將資料存在堆疊暫存器裡面,而程式終止之後還是一樣會呼叫Re暫存器返回程式記憶體位址,並且在由下一行指令呼叫堆疊暫存器裡面的值,因此似乎有沒有寫return是沒有影響的

而從軟體工程的角度來講,這個是更顯得重要的,當在做大型系統的開發,FSM(有限狀態機)的設計就可以很快的找出哪一個子系統出了些差錯,每一個子系統可以代表著一個.EXE檔的設計,對於一個比較嚴謹的開發可以很快的debug掉很多問題

嘴砲這麼多,這種小程式我應該還是會繼續懶得寫吧XD

匿名 提到...

cvMoveWindow的解釋部份好像是以左上角為基準移到螢幕的位置不是右上角的樣子@@

匿名 提到...

請問前輩如何在一個視窗顯示兩張圖,
鼻如左圖是原圖,右圖是灰階圖。
謝謝!

匿名 提到...

#include
#include
//#include
int main(){
IplImage *src= 0; //來源影像指標
IplImage *gray= 0; //來源影像指標
src = cvLoadImage("ch11.jpg"); //載入影像
gray=cvLoadImage("ch11.jpg",CV_LOAD_IMAGE_GRAYSCALE);
printf("Width=%d height=%d",src->width,src->height);//印出轉換之後的影像寬和高
cvNamedWindow("Show Image");
cvShowImage("Show Image",src);
cvNamedWindow("Show Image1");
cvShowImage("Show Image1",gray);

cvWaitKey(0); //等待回應
cvReleaseImage(&src); //釋放來源影像佔用的記憶體
cvDestroyWindow( "src" ); //銷毀視窗「src」
return 0;
}

Copyright 2008-2009,yester