天天看點

移植OpenCV到ARM時的注意事項

在嵌入式平台中,一般來說是使用C語言進行程式設計,嵌入式系統所使用的編譯器也通常對C語言有更好的優化能力,而對C++語言的支援力度不是很大。OpenCV源碼采用C、C++混合程式設計,所有API既可以用C也可以用C++調用。早期的OpenCV大多使用C語言編寫,而OpenCV 1.0之後的版本則主要使用C++了。為了适應嵌入式平台,我們在移植時一般選用早期的OpenCV版本,這裡選用的即是OpenCV 1.0。

在移植OpenCV到嵌入式系統中時,有許多需要注意的地方,主要包括兩部分:一是記憶體管理、位元組對齊;二是文法問題,主要是指如何修改C++文法和C99版本的C語言文法。具體來說,有以下一些地方需要注意:

(1)記憶體問題。

我們使用的DM6467是雙核架構,ARM+DSP,ARM端運作Linux作業系統,DSP運作DSP/BIOS系統。Linux中對于記憶體位址使用的是虛拟位址,而DSP沒有存儲管理器,隻能使用實體位址,是以在Linux中配置設定的連續記憶體在DSP看來其實是實體上不連續的。因而我們要使用某種方法使得Linux端配置設定的記憶體是在實體上連續的,這裡使用了CMEM子產品來配置設定連續記憶體。

要使OpenCV使用CMEM配置設定連續記憶體,需要修改OpenCV的記憶體管理函數cvAlloc()和cvFree_(),這兩個函數在cxalloc.cpp檔案中,具體的修改辦法參見上一周的文檔。

(2)C99版C語言文法問題。

C99标準擴充了标準的C語言文法,而OpenCV中部分源碼使用了該标準,而我們使用的編譯器不支援該标準,是以需要修改響應源碼。

在目前的OpenCV中,主要是以下類型代碼使用了C99标準:

for ( int i = 0; i < 100; i ++ )

{

……

}

這裡的整形變量i并沒有在循環體前面進行聲明,這在gnueabi-gcc中無法編譯,需要修改為以下代碼;

int i;

for ( i = 0; i < 100; i ++ )

{

……

}

(3)函數形參表。

OpenCV中很多函數在函數聲明時設定了預設值,在調用該函數時如果某個參數未設定則使用預設值,如下所示。

函數定義:

CVAPI(void) cvIntegral( const CvArr* image, CvArr* sum,

                       CvArr* sqsum CV_DEFAULT(NULL),

                       CvArr* tilted_sum CV_DEFAULT(NULL));

函數調用:

cvIntegral( img, sum );

我們使用的編譯器不支援這種函數原型聲明,也不支援這種參數不夠的函數調用,對于以上部分代碼需要修改為:

函數聲明改為:

CVAPI(void) cvIntegral( const CvArr* image, CvArr* sum, CvArr* sqsum, CvArr* tilted_sum);

或者在頭檔案中添加以下宏定義:

#define CV_DEFAULT(x)

函數調用:

cvIntegral( img, sum, NULL, NULL );

(4)C++文法問題。

在OpenCV中有很多C++文法的源碼,對于這部分代碼的修改比較麻煩,必須在了解了代碼的基礎上才能進行進行修改,否則非常可能改錯。在修改時候有兩種思路:一是想方設法讓代碼分支到不包含C++文法的代碼段去運作;二是将C++格式的成員變量、方法用C來實作,不過這比較複雜,但有的時候必須這麼做。

(5)IPP庫和OPENMP。

OpenCV是由Intel發起的,OpenCV中的很多源碼都是可以使用Intel處理上優化過的IPP庫(以及OPENMP)進行算法加速。OpenCV的代碼使得使用者在沒有安裝IPP庫時可以使用OpenCV自帶的算法來完成運算,在安裝了IPP庫後可以在編譯時自動內建IPP庫函數以提高算法運作速度。為了實作使用者層的透明,OpenCV中引入了IPPI_CALL()函數,如下所示:

        IPPI_CALL( func( src->data.ptr, src->step, ssize, dst->data.ptr,

                         dst->step, dsize, cn, xmin, xmax, xofs, buf ));

對于這部分代碼的修改比較簡單,隻需要跟蹤源碼找到在沒有使用IPP庫時OpenCV調用的函數是什麼,然後替換掉函數名即可,例如上面的函數調用修改之後為:

icvResize_Bilinear( src->data.ptr, src->step, ssize, dst->data.ptr, dst->step, dsize, cn, xmax, xofs, yofs, (int *)buf0, (int *)buf1 );

(6)宏定義的問題。

在OpenCV中大量使用宏定義,并且很多宏定義代碼非常長并且有些文法比較不常見,需要修改,例如以下這個例子:

#define  ICV_DEF_RESIZE_BILINEAR_FUNC( flavor, arrtype, worktype, alpha_field,  \

                                       mul_one_macro, descale_macro )           \

static CvStatus CV_STDCALL                                                      \

icvResize_Bilinear_##flavor##_CnR( const arrtype* src, int srcstep, CvSize ssize,\

                                   arrtype* dst, int dststep, CvSize dsize,     \

                                   int cn, int xmax,                            \

                                   const CvResizeAlpha* xofs,                   \

                                   const CvResizeAlpha* yofs,                   \

                                   worktype* buf0, worktype* buf1 )             \

{                                                                               \

    int prev_sy0 = -1, prev_sy1 = -1;                                           \

    int k, dx, dy;                                                              \

修改辦法是将其改為函數,如下所示:

static void icvResize_Bilinear( const uchar* src, int srcstep, CvSize ssize,

                                   uchar* dst, int dststep, CvSize dsize,    

                                   int cn, int xmax,                            

                                   const CvResizeAlpha* xofs,                  

                                   const CvResizeAlpha* yofs,                  

                                   int* buf0, int* buf1 )