2012年9月4日 星期二

EmguCV Image Process: Processing Image with Classes part2

中正紀念堂天色變化縮時

EmguCV 2.3 Application Programming : Processing Images with Classes

參考OpenCV 2 Computer Vision Application Programming Cookbook第三章

本篇介紹如下:

Using a Controller to communicate with processing modules

Using a Controller to communicate with processing modules

上一篇介紹了一個簡單的顏色偵測演算法的功能

如果想要建立一個複雜的應用程式

可能需要開發很多個不同的處理演算法

並使這些演算法能夠組合在一起

而完成一些比較進階的功能或任務

如何在應用程式上設定參數和屬性

並且能夠和所有的功能類別做有效的溝通

會變得越來越困難、越來越複雜

在單一個類別中能夠集中控制這些功能或屬性是複雜且進階的工作
(也就是如何設計一個好的人機介面!!)

這也就是一個控制器(Controller)所扮演的重要角色


首先先建立一個名為ColorDetectorController的類別

記得要參考上一篇所寫的專案或dll (ColorDetectot)、還有Emgu相關的dll

這裡需要擁有三個屬性_ColorDetector、_Image、_Result

class ColorDetectorController
{
    private ColorDetector _ColorDetector; //the algorithm class
    private Image<Bgr, Byte> _Image;      //input image
    private Image<Gray, Byte> _Result;    //output result

    public ColorDetectorController()
    {
        this._ColorDetector = new ColorDetector();
    }
}
然後還必須把一些控制屬性的方法建立一下

所需要的屬性控制大致如下:

//Sets the color distance threshold
public void SetColorDistanceThreshold(int distance)
{
    this._ColorDetector.MinDist = distance;
}

//Gets the color distance threshold
public int GetColorDistanceThreshold()
{
    return this._ColorDetector.MinDist;
}

//Sets the color to be detected
public void SetTargetColor(double red, double green, double blue)
{
    this._ColorDetector.Color = new Bgr(blue, green, red);
}

//Gets the color to be detected
public void GetTargetColor(ref double red, ref double green, ref double blue)
{
    red = this._ColorDetector.Color.Red;
    green = this._ColorDetector.Color.Green;
    blue = this._ColorDetector.Color.Blue;
}

//Sets the input image. Reads it from file.
public bool SetInputImage(string filename)
{
    this._Image = new Image<Bgr, byte>(filename);
    if (this._Image == null)
        return false;
    else
        return true;
}

//Returns the current input image.
public Image<Bgr, Byte> GetInputImage()
{
    return this._Image;
}
以上這些都是屬性的設定

透過方法的呼叫來處理
(當然這邊你也可以直接用C#的get、set的方式來實作)

當然還需要運作的方法:

//Performs image processing
public void Process()
{
    this._Result= this._ColorDetector.Process(this._Image);
}

//Returns the image result from the lastest processing.
public Image<Gray, Byte> GetLastResult()
{
    return this._Result;
}
透過上面兩個方法,就可以執行運算和取得最後結果的影像


使用這個Controller的類別

你可以輕易的替應用程式建立一個介面來執行你的演算法

應用程式的程式設計者

不必知道這些類別裡面的運作機制,之間彼此是怎麼連結

或是花時間去了解哪一個方法在哪一個類別裡,需要那些屬性等等

這些都在Controller裡面做完了

使用者只需要建立一個Controller的類別


在屬性的控制上

由於這個簡單的例子只包含一個ColorDetector的類別

所以看起來似乎有點多此一舉

但是在大部分的情況下

一個Controller不只控制單一個類別

這時候Controller的角色就要去做到透過簡單的介面

正確的把需要的屬性對應到某個類別中的屬性
(也就是redirect的動作)

像是例子中的SetTargetColor跟GetTargetColor這兩個方法

使用者不必知道Color的結構(Bgr、Gray)

而是透過三個double的變數來控制和取得


在某些時候Controller也需要提供一些介面

讓應用程式面的使用者來提供資料

像是例子中的SetInputImage,透過一個字串

來讓Controller取得目標影像

並回傳是否成功


最後來看看如何用一個簡單的dialog-based的方法

透過剛剛的ColorDetectController來運作

並以C#中的windows form來呈現

首先我們先建立一個window form的專案,名稱為:Chapter3Form

再透過工具列建立幾個按鈕

共有四個按鈕的windows form


這裡建立了四個Button

分別命名為:OpenButton、ProcessButton、OKButton、CancelButton

再分別將OpenButton的text屬性改成:Open Image

ProcessButton的text屬性改成:Process

OKButton的text屬性改成:OK

CancelButton的text屬性改成Cancel

就大致完成了如上圖的配置狀況


這時候我們還要加入一個叫做 OpenFileDialog 的類別進來

直接從工具列中找到直接拖拉到wondows form中即可

將其更改名稱為:OpenFile

拖拉進去然後修改名稱

然後再設定OpenFile的屬性Filter:Image Files (*.jpg, *.bmp)|*.jpg; *.bmp;

這樣在選擇檔案的時候就能過濾出影像的格式(jpg跟bmp)


這樣UI的配置大致上完成

剩下的就是程式碼的部分

而在程式碼中記得要參考剛剛Controller的專案或dll

把該參考的參考進去

private ColorDetectorController _Controller;
public Chapter3Form()
{
    InitializeComponent();  //Windows form initial
    this._Controller = new ColorDetectorController(); 
}
然後加入Controller這個類別

在建構式的時候先建立

這時候再來控制Open Image的按鈕

先從讀取圖片開始

//Callback method of "OpenButton" button
private void OpenButton_Click(object sender, EventArgs e)
{
    if (OpenFile.ShowDialog(this) == DialogResult.Cancel)
    {
        return;// User selects nothing
    }
    String imagePath = OpenFile.FileName;
    //Set the input image
    this._Controller.SetInputImage(imagePath);
    //Show the input image
    CvInvoke.cvShowImage("Input Image", this._Controller.GetInputImage());
}
讀取使用者選取的影像路徑

透過Controller的SetInputImage把路徑傳進去

並透過CvInvoke的函式:cvShowImage把影像呈現出來

再來看看Process的程式

private void ProcessButton_Click(object sender, EventArgs e)
{
    //Target color is hard-code here
    this._Controller.SetTargetColor(30, 30, 45);
    this._Controller.Process();
    //Show the output image
    CvInvoke.cvShowImage("Output Image", this._Controller.GetLastResult());
}
這邊透過SetTargetColor來設定偵測的顏色

這邊的顏色設定目前是寫死的程式碼中
(下一篇會改善這個問題)

然後就執行Process

再把結果呈現出來,就大功告成!!

在使用Controller的情況下

應用程式的開發者不需要知道ColorDetector所需要的屬性是什麼

或是使用特殊的類別或結構,像是Image、Bgr、Gray等等

只要建立一個Controller的類別

知道他的方法如何使用即可

使用情況如下:

按下Open Image就會跳出選擇視窗,選擇你要的影像

注意右下方的檔案類型

就是當初設定的OpenFile的Filter的屬性

選擇完後,會跳出一個新視窗,影像就會被呈現出來

這個視窗的文字就是CvInvoke.cvShowImage的第一個引數:Input Image
(CvInvoke.cvShowImage("Input Image", this._Image);)

接著再按下Porcess
 運作的結果一樣會透過一個新的視窗呈現


這樣就完成了一個簡單的顏色偵測的應用程式

下一篇會來修正GUI上的一些設計,讓介面更加的friendly

沒有留言:

張貼留言