第五章 實驗介紹與結果
5.1 開發 Filter 流程
5.1.2 Filter 架構實現
Filter 架構實現這部份,其所必須實現的函式會依所選擇的父類別不同而 有所不一樣,依我們 Filter 的功能所選擇的父類別是 CTransformFilter,而這 個父類別有五個必須實現的函式,如表 5-1 所示
函式名稱 敘述
CheckInputType 檢查輸入 Pin 媒體類型是否支援 CheckTransform 檢查輸出入 Pin 媒體類型是否正確 DecideBufferSize 決定輸出 Pin 要建立多大 Buffer GetMediaType 取得媒體類型
Transform 輸入資料處理函式
表 5-1 CTransformFilter 必須實現函式
57
CheckInputType
這個函式實現的目的是當上一級的輸出 Pin 要跟我們 Filter 的輸入 Pin 做 連接動作的時候,將先觸發這個函式做檢查的動作,以確定是否可以連接成功。
如果是雙方都支援的格式將返回 NOERROR 的值,反之將返回 E_FAIL 的值,而我 們實現的程式碼如圖 5-4 所示。
圖 5-4 CheckInputType 程式碼
程式碼的實現首先透過 CheckPoint 的 API 檢查 Input Pin 的媒體類型是否 正 常 可 以 工 作 , 確 定 可 以 工 作 之 後 , 先 初 步 檢 查 這 個 媒 體 結 構 中 的 成 員 FormatType 是 否 為 我 們 所 要 求 的 Format_VideoInfo 的 型 態 , 如 果 不 是 Format_VideoInfo 的型態就跳出函式返回 E_INVALIDARG,如果成功我們自己又 寫了一個 CanPerformFD 的函式來細部檢查媒體類型,CanPerformFD 的程式碼實 現如圖 5-5 所示。
圖 5-5 CanPerformFD 程式碼
58
CanPerformFD 這個函式將一層一層檢查所支援的媒體類型,因為我們這個 Filter 是設定支援 RGB24 的視訊資料,所以首先檢查 Type 是否為 Video 的類型,
再來下一層檢查輔助 Type 是否為 RGB24,如果是 RGB24 就再更近一步確認這個 結構的 Format 成員裡面的 biBitCount 成員是否為 24(biBitCount 成員代表每個 Pixel 的總 bit 數),如果都正確就返回 TRUE,反之返回 FALSE。
CheckTransform
這個函式實現的目的是在資訊處理之前,會觸發這個函式對輸出入 Pin 再作 一次檢查的動作以確保輸出入 Pin 為我們所支援的媒體格式。然而依我們 Filter 的定義輸入 Pin 和輸出 Pin 是同一類型,所以程式碼的實現只要確認輸入 Pin,
而輸入 Pin 確認後再判斷兩支 Pin 是否相等即可,程式碼如圖 5-6 所示。
圖 5-6 CheckTransform 程式碼
程式碼實現比較簡單,可以先呼叫之前已經寫好的 CanPerformFD 函式幫我 們判斷輸入 Pin 是否符合媒體類型,如果符合只要再判斷輸出入 Pin 兩個是否相 同即可達到我們的目的。
59
DecideBufferSize
當我們 Filter 的輸出 Pin 完成與下一級的輸入 Pin 連接時,必須分配一個 共同的記憶體以供傳輸使用,而實現這個函式的目的就是在做分配這個記憶體的 動作,在介紹這個函式實現之前要先介紹管理 Sample 分配器的一個結構叫做 ALLOCATOR_PROPERTIES,它包含四個成員如表 5-2 所示
成員 型態
cBuffers long cbBuffer long cbAlign long cbPrefix long
表 5-2 ALLOCATOR_PROPERTIES cBuffers : 在分配器中要建立的 Buffer 個數。
cbBuffer : 所要建立 Buffer 的大小,單位為 Byte。
cbAlign : buffer 起始位址要求多少 Byte。
cbPrefix : 每個 Buffer 前面有多少前置碼空間,這個值對於有參數要傳遞給下 一級 Filter 時可以使用,塞在 Buffer 前面一起傳下去。
了解這個結構後就可以比較清楚如何去實現 DecideBufferSize 這個函式,
實現的程式碼如圖 5-7 所示
60
圖 5-7 DecideBufferSize 程式碼
程式碼一開始透過輸入 Pin 的 IsConnect 函式去判斷輸入 Pin 是否為連接狀 態,因為 DecideBufferSize 這個函式要做的事是去決定以完成連接輸出 Pin 的 Sample 分配器大小,而要完成輸出 Pin 的連接前提是輸入 Pin 要是連接的狀態 下,因此我們才要先確認輸入 Pin 是連接的狀態,確定輸入 Pin 是連接之後就可 以透過設定 ALLOCATOR_PROPERTIES 這個結構的成員來達到建立分配器的功能,
首先我們透過設定 CBuffers=1 來決定我們 Buffer 的個數,再來是設定大小,那 因為我們 Filter 的輸入 Pin 跟輸出 Pin 是共同使用一個媒體格式,所以兩支 Pin 的 Sample 大小是一樣的,因此我們透過輸入 Pin 的 GetSampleSize 來取得 Sample 大小的值設定到 CbBuffer 這 個成員即完成分配器屬性的設定,接著透過 SetProperties 函式把我們設定好的結構告訴系統分配器,則系統就會依照我們 設定的個數大小幫我們完成配置,這個函式實現就算完成了。
61
GetMediaType
實現這個函式的目的是要讓下一級 Filter 的輸入 Pin 能夠透過這個函式獲取我 們 Filter 輸出 Pin 所支援的媒體格式方便連接時做檢查的動作,所以當我們輸 出 Pin 要跟下一級輸入 Pin 連接時就會觸發這個函式去提供下一級輸入 Pin 媒體 資訊以供比對,而這是輸出 Pin 連接時會使用到的函式,因此使用這個函式的前 提是輸入 Pin 要處於連接的狀態,程式碼如圖 5-8 所示
圖 5-8 GetMediaType 程式碼
函式一開始要先檢查輸入 Pin 是否連接,必須是連接的狀態才能繼續往下 做,再來會檢查 iPosition 這個參數是否為 0,這是一個媒體序號必須為 0 才可 以繼續工作,一切檢查工作完畢後,即可返回輸出 Pin 的媒體類型以供下一級 Filter 使用,而我們這個 Filter 輸出入 Pin 是使用同一個媒體類型,因此可以 簡單的返回輸入 Pin 的媒體類型即可。
Transform
實現這個函式對 Transform Filter 是最重要的,它的目的是對 Source Filter 傳送來的資料作處理,舉凡編解碼、影像處理、音訊處理等,都是透過 Transform Filter 的 Transform 函式所運作。而我們的 Filter 是要對影像作人 臉偵測,所以我們所實現的人臉偵測程式碼將會被放入這個函式中,其程式碼的
62
實現如圖 5-9 所示
圖 5-9 Transform 程式碼
程式碼的實現我們的作法是先把輸入 Pin 資料搬到輸出 Pin,然後直接在輸 出 Pin 上做處理的動作,當然另一種作法也可以先在輸入 Pin 上作完處理再搬到 輸出 Pin 上。因此我們延伸了兩個函式 Copy 以及 Transform 來處理搬資料以及 影像處理兩件事情,而 Copy 函式是將輸入 Pin 資料複製到輸出 Pin 上,Transform 函式是將輸出 Pin 上的資料做我們所要求的處理後再放回輸出 Pin 的 Sample 分 配器上等下一級 Filter 來拿取,如此就實現了 Transform 的功能。接下來要仔 細介紹 Copy 和 Transform 內部程式碼到底如何實現,首先是 Copy 函式的介紹,
程式碼如圖 5-10 所示
圖 5-10 Copy 程式碼
因 為 這 個 函 式 主 要 是 要 做 資 料 複 製 的 動 作 , 所 以 一 開 始 會 呼 叫 GetActualDataLength 的 API 來獲取輸入 Pin 上資料的總大小,接著在呼叫
63
GetPointer 的 API 來取得輸入 Pin 以及輸出 Pin 在記憶體中放資料的起始位址,
然後把以上得到的參數丟給 CopyMemory 函式,而它就會幫我們把輸入 Pin 的資 料複製到輸出 Pin 上去,最後再將複製的總大小設定到輸出 Pin 上,如此就完成 Copy 函式的實現了。再來就要仔細介紹最重要的 Transform 函式,其程式碼實 現如圖 5-11 所示
上下翻轉
圖 5-11 Transform 程式碼
這個函式是要對輸出 Pin 的資料作人臉偵測處理後再放回輸出 Pin,以供下 一級 Filter 來拿取處理完的資料。所以首先要取得資料才可以做處理,而取得 資料之前必須知道資料的規格才可以按照規格作存取的動作,所以我們透過 CurrentMediaType 獲得媒體格式,再從媒體格式這個結構的 Format 成員即可取 得這個 Sample 的長以及寬,且可透過 GetPoint 的 API 來幫我們取得資料存放的 起始位址,有長寬也有起始位址如此一來我們就可以取得所有資料,即是一個長 乘以寬的陣列。而我們的 Formattype 是 FORMAT_VideoInfo 的形式所以它的資料 是 BMP(BitMaP)格式,查看 BMP 規範後知道 BMP 資料是上下顛倒的形式,所以我 們先對取得的資料作一個上下翻轉回來的處理讓資料變成我們熟悉的由上往下 的形式,接著把這個資料送進我們已經開發好的人臉偵測函式(FaceDetect)中做
64
人臉偵測,FaceDetect 這個函式有六個參數必須代入分別是 z Image: 要做人臉徵測的圖像資料
z Image_width: 圖像資料的寬 z Image_Height: 圖像資料的高
z Win_width: 人臉檢測視窗寬的初始值 z Win_Height: 人臉檢測視窗高的初始值 z Scale: 每次檢測視窗加大的比率
跑完 FaceDetect 這個函式後,它會返回一個偵測完的資料陣列,最後再把這個 資料陣列複製到輸出 Pin 的 Sample 分配器上,如此即完成了整個 Transform 函 式的功能實現,而且也完成了我們人臉偵測 Transform Filter 的開發,如圖 5-12 即是我們開發出來的 Filter 圖示顯示在 GraphEdit 中的樣子
圖 5-12 人臉偵測 Filter