• 沒有找到結果。

來源(Source)/內容(Content)/接觸介面(Access)設計模式

第四章 系統設計

4.1.3 來源(Source)/內容(Content)/接觸介面(Access)設計模式

請參考以下的設計:

這是一個設計用來操作影像資料的一 個 類 別 (class) , 稱 作 Image , 在 第 一 個 constructor 當中,該 image 可經由指定 width 與 height 和 bytesPerPixel,以動態記 憶體配置方式完成影像物件的建立,或是 從讀取某種特定格式的影像檔案來建立影 像物件(第二個 constructor)。

除此之外,該影像可透過 get/setPixel

Image(int width, int height, int bytesPerPixel);

Image(char* fileName);

~Image();

// methods void flip();

void clear();

void rotate(int direction);

// data access

void setPixel(int x, int y, int value);

unsigned char* getPixel(int x, int y);

// data members int _width int _height int _bytesPerPixel;

unsigned char* _pImageData;

然而,若需要延伸該物件的建構方式(例如來自其他檔案格式)、操作(例 如 加 入 一 些 影 像 處 理 的 演 算 法 實 作 ) 或 是 資 料 存 取 方 式 ( 例 如 存 取 subpixel),則維護該物件的實作將會較為困難。畢竟影像物件的用途甚廣,

就算已經擁有了相當完整的建構、存取與操作等實作,仍可能存在未知的需 求。

因此在本系統的 Image Library 當中,我們將影像的建構、資料儲存、

資料存取與操作分為三個獨立的類別(class)。Image Library 的設計以下圖的 概念為基礎,實作出一組用來處理二維與三維影像資料的類別函式庫(class library)以達成 4.1.1 所述之功能,並期望在未來出現新的應用需求時,能夠 容易地維護與延伸物件的實作。

Content

Access

creates

accesses

Source

圖 4.3 Source/Content/Access Pattern

上圖表示 Image Library 用來儲存、操作、建構影像資料的主要物件,分為 三大類: Content(內容)、Source(來源)與 Access(資料操作)。每個類 別均分為 Image 與 Volume,即 Content 分為 ImageContent 與 VolumeContent,

其餘以此類推。Image 與 Volume 分別處理二維與三維影像資料,兩者獨立。

此三類物件的功能依據性質有所不同,以下我們將個別介紹之:

a. Image/Volume Content

: 影像內容。專門用來儲存影像資料與相關屬 性,例如各個維度之尺寸與各個 row、slices 與 frames(後兩者用在 volume) 的排列方式,並提供查閱屬性資訊的方法(methods)與影像資料的起始位址 (image bits)。設計如下:

ImageContent int getW idth();

int getHeight();

short getBytesPerPixel();

int getW idthBytes();

bool rowsUpsideDown();

unsigned char* getImageData();

ImageContent(ImageSource& imageSource); short getBytesPerVoxel() int getWidthBytes() bool rowsUpsideDown() bool slicesUpsideDown() bool framesUpsideDown() unsigned char* getVolumeData()

VolumeContent(VolumeSource& volumeSource) VolumeContent(VolumeContent& volumeContent)

圖 4.4 Class ImageContent and Class VolumeContent

Image/Volume Content 本身提供記錄影像資料與相關屬性資訊的功能。

就 ImageContent 而言,屬性除了 width/height/bytesPerPixel 以外,另外提供 widthBytes 表 示 每 條 row 所 佔 用 的 位 元 組 數 以 便 利 計 算 之 用 , 以 及 rowsUpsideDown 之 true/false 值,表示影像的各 row 之排列方式,在記憶體 中是從 row 0 排列至 row height - 1 亦或順序顛倒排列(例如 Microsoft®的 bitmap 影像格式)。而 VolumeContent 則多了圖中粗體字部分,即 depth, nFrames 與 slices/framesUpsideDown,也表示各 frame 或是 slices 的排列順序。

Image/Volume Content 的建構方式相當單純。其中一種是傳入一個繼承 於 Image/Volume Source 的物件,而影像物件的建構方式則定義在傳入的 Source 物件本身。

另外一種方式則為傳入一個 Image/Volume Content 的物件,表示從來源 的 Image/Volume Content 複製一個相同的影像物件。

b. Image/Volume Source

: 影 像 來 源 , 專 門 用 來 建 立 影 像 資 料 (Image/Volume Content)的物件。在 Image Library 當中,所有和影像建立有 關的物件,都繼承於此類別。舉例而言,假設我們自行設計一個用來讀取 bmp (Microsoft® Bitmap) 圖 檔 的 物 件 稱 為 MyBMPReader , 並 繼 承 於 ImageSource,那麼,若要藉由此物件讀取一個 bitmap 格式的影像檔案至一 個 ImageContent 當中,則以下的程式片段即可達成。

01 MyBMPReader reader(“image.bmp”);

02 ImageContent image (reader);

Code Listing 4.2

Constructing an Image Object from an Image File

程式片段中的 01 行表示產生一個 MyBMPReader 的物件稱為 reader 作為影 像來源,並且指定 image.bmp 使其能夠將 image.bmp 影像檔案傳輸至指定的 ImageContent 當中。讀取的動作在 02 行完成。02 行能夠解讀成藉由來源物 件 reader 產生一個叫做 image 的影像物件。這個 reader 物件的實作將會讀取

image.bmp 檔案,並將影像內容與相關屬性轉換至 image 物件當中。

我們在此介紹影像建構時 Source 與 Content 的互動機制。

執行 Code Listing 4.2 的兩行程式時,在 01 行當中建立一個影像來源並址定 相關參數(例如檔案名稱),真正的建構程序,則是在 02 行:

02 ImageContent image (reader);

ImageContent 除了擁有影像資料的起始位址以外,亦包含影像的屬性資訊。

因此 ImageContent 在建構(執行 constructor)的程序當中,需要從傳進的 ImageSource 物件取得即將建構的影像物件之所有資訊。這些資訊即是圖 4.4 下方的資料成員,為便利起見,我們將這些資料成員封裝成 ImageCreateStruct 結構,讓 ImageSource 完成影像(從檔案或是 OS)的建立以後回傳此結構,再 指 定 到 ImageContent 的 資 料 成 員 中 。 下 圖 介 紹 影 像 建 立 的 過 程 , 從 ImageContent 的建構子(constructor)呼叫 ImageSource 的建構程序直到取得影 像資訊為止:

ImageContent

Constructor ImageSource Operating System

1. request image creation

2. begin makeImage

3. request for file I/O or MEM alloc

6. return imageCreateStruct

7. complete construction

4. allocate memory or/and file I/O 5. return resource

圖 4.5 Sequence Diagram of Image Creation

在 ImageContent 的 constructor 當中,起初對於傳入的 ImageSource 作出 建構影像的請求(呼叫 produceImage 函式),ImageSource 於是依據原先指定 的參數(例如檔案名稱)開始以本身特定的方式(例如,程式片段 4.2 當中的 MyBMPReader 將從指定的影像檔案讀取資料)產生影像資料與相關屬性,完 成後回傳 ImageCreateStruct 至 ImageContent 的建構子,最後將影像資料與 屬性指定至物件當中完成建構。

下圖為 ImageSource 的設計:

影 像 產 生 的 方 式 , 由 虛 擬 函 式 produceImage 當作取得影像資訊的介

面。因此任何繼承於 ImageSource 的物件,必須提供圖中 produceImage 函式 的實作。如此進行圖 4.5 的第 2 個 step 時,ImageSource 物件會呼叫自己的 produceImage,但實作部份卻在繼承於此之物件,例如定義在 MyBMPReader 的 produceImage 函式中。

Im ageSource virtual ~Im ageSource() Im ageSource();

virtual Im ageCreateStruct produceIm age() = 0;

另外,其中 VolumeSource 的設計與 ImageSource 相同,只是將 Image 改成 Volume 即可。

c. Image/Volume Access

: 影像資料存取,專門用來便利影像資料取得與 操作的物件。ImageContent 當中,僅提供影像資料的起始位址,這樣的設計 在於存取方式可以自行定義,較為不受限制。在 Image Library 當中,我們 則提供 Image/Volume Access 定義預設的影像資料操作方式。如 4.1.2 影像資 料表示法所述,Image 的 row pointers、Volume 的 slice 與 frame pointers 均定 義於 Image/Volume Access 當中。請見以下本物件的設計:

Volume Access<VOXEL_TYPE>

VolumeAccess(VolumeContent& volumeContent);

~VolumeAccess();

int getW idth() int getHeight() VOXEL_TYPE*** operator [] (int i) Image Access<PIXEL_TYPE>

ImageAccess(ImageContent& imageContent);

~ImageAccess();

int getW idth() int getHeight() PIXEL_TYPE* operator [] (int i)

Image/Volume Access 以 template(樣板)型式提供使用。Access 物件均包 含 Image/Volume Content 的參考(reference),除了可直接參考 Image Content 的屬性與影像資料以外,另外提供了一個 operator [],能夠以多維陣列的方 式直接對影像作操作。若要以 Access 操作一張存在的 ImageContent,則以 下的實作即可使用。

01 MyBMPReader reader(“image.bmp”);

02 ImageContent image(reader);

03 ImageAccess<unsigned char> myAccess(image);

04 myAccess[10][7] = 15;

05 unsigned char* p = myAccess[7];

Code Listing 4.3 Image/Volume Access

範例中 03 行表示產生一個 Access 物件,用來存取影像物件 image,並且指 定像素(pixel)的型態為 unsigned char(在 C 語言中,通常為佔用一個 byte 的 變數);04 行則指定影像中的 row 10 之像素 7 的數值;05 行則取得 row 7 的 起始位址至指標變數 p。下列為 Volume Data 的使用範例

01 MyLSMReader reader(“volume.lsm”);

02 VolumeContent volume(reader);

03 VolumeAccess<unsigned char> myAccess(image);

04 myAccess[0][7][1][2] = 15;

05 unsigned char* pRow = myAccess[0][2][4];

06 unsigned char** pSlice = myAccess[0][1];

07 unsigned char*** pFrame = myAccess[1];

04行指定位於 frame 0, slice 7, row 1, column 2 的像素亮度值。

05 行取得 frame 0, slice 2, 的 row 4 那列像素之起始位址(即 row pointer) 06 行取得 frame 0, slice 1 之存放 row pointers 之起始位址(即 slice pointer) 07 行取得 frame 0 之存放 slice pointers 之起始位址(即 frame pointer)

以上介紹 Image Library 當中的影像物件表示法以及影像物件之設計。在 4.2 當 中,我們將以本專案的兩個應用需求,各別設計屬於其之 Main Application 元件,

以供 User Interface 操作之用。