第四章 系統設計
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 操作之用。