1. 程式人生 > >在Windows下執行Felzenszwalb的star-cascade DPM(Deformable Part Models)目標檢測Matlab原始碼

在Windows下執行Felzenszwalb的star-cascade DPM(Deformable Part Models)目標檢測Matlab原始碼

        可變形部件模型Deformable Part Models(DPM)是非常經典的目標檢測演算法,由Felzenszwalb提出,本文介紹如何在windows下執行Felzenszwalb給出的DPM演算法的star-cascade版本voc-release4.01-star-cascade,相比於基本版本voc-release4.01,star-cascade版本增加了PCA降維,檢測速度可提高十幾倍。

       有關Deformable Part Model參見論文

同樣,Felzenszwalb給出的star-cascade DPM也只能在Linux或Mac系統上執行,我們可以對其進行一些修改,使之可以在Windows的Matlab上跑起來。

 我的環境:Win7 + Matlab R2012b(其中配置VS2010中的c++編譯器)

首先,在作者的網站上下載原始碼,由於star-cascade必須在DPM的基本版本之上執行,所以還需要下載對應的DPM原始碼,我這裡用的都是第4版,即voc-release4.01 和 voc-release4.01-star-cascade。下載完成後首先要在Windows上把voc-release4.01跑起來,詳見這篇文章:

下載完voc-release4.01-star-cascade後,將star-cascade資料夾解壓到voc-release4.01資料夾中(因為要在DPM之上執行)。發現其中有很多“._”開頭的檔案,這些是Linux或Mac系統上使用的(具體我也不是很懂),全部刪掉,windows下用不到。其子資料夾data中的._開頭的檔案也可以刪掉。

然後用notepad++或其他編輯器開啟README,看看說明。README中說先進入star-cascade資料夾,編譯原始碼,然後退回到voc-release4目錄執行。在Linux下可以直接執行作者寫好的Makefile檔案進行編譯,在windows上我們需要手動編譯。開啟Makefile檔案,大體上看一看,發現需要編譯cascade.cc,model.cc和fconv_var_dim.cc三個檔案,接下來嘗試用mex進行編譯,看看會遇到什麼錯誤。

步驟1 編譯cascade.cpp和model.cpp

首先將cascade.cc,model.cc,fconv_var_dim.cc這三個檔案的副檔名都改為.cpp,否則windows中無法識別cc檔案。然後將matlab的工作目錄定位到star-cascade資料夾。cascade.cpp和model.cpp需要同時編譯,在matlab命令列中輸入:

  1. >> mex cascade.cpp model.cpp  
出現錯誤提示:
  1. \voc-release4.01-star-cascade\star-cascade\timer.h(6) : fatal error C1083: Cannot open include file: 'sys/time.h': No such file or directory   
  2.   D:\PROGRA~1\MATLAB\R2012B\BIN\MEX.PL: Error: Compile of 'cascade.cpp' failed.   
  3. Error using mex (line 206)  
  4. Unable to complete successfully.  
是說找不到timer.h中的sys/time.h標頭檔案,網上查了查,這是linux中的檔案,將“sys/”去掉,直接include time.h,因為windows中也有time.h。再次輸入上面的編譯命令,結果為:
  1. voc-release4.01-star-cascade\star-cascade\timer.h(21) : error C2079: 'tv' uses undefined struct 'timer::tic::timeval'   
  2. voc-release4.01-star-cascade\star-cascade\timer.h(22) : error C3861: 'gettimeofday': identifier not found   
  3. voc-release4.01-star-cascade\star-cascade\timer.h(23) : error C2228: left of '.tv_sec' must have class/struct/union type is 'int'   
  4. voc-release4.01-star-cascade\star-cascade\timer.h(23) : error C2228: left of '.tv_usec' must have class/struct/union type is 'int'   
  5. voc-release4.01-star-cascade\star-cascade\timer.h(28) : error C2079: 'tv' uses undefined struct 'timer::toc::timeval'   
  6. voc-release4.01-star-cascade\star-cascade\timer.h(29) : error C3861: 'gettimeofday': identifier not found   
  7. voc-release4.01-star-cascade\star-cascade\timer.h(30) : error C2228: left of '.tv_sec' must have class/struct/union type is 'int'   
  8. voc-release4.01-star-cascade\star-cascade\timer.h(30) : error C2228: left of '.tv_usec' must have class/struct/union type is 'int'   
說明windows中的time.h和linux中的不一樣,沒有結構體timeval的定義,也沒有gettimeofday函式,網上找了一段替代linux中的gettimeofday函式的程式碼,加到timer.h檔案中,同時再加兩個標頭檔案,修改完的timer.h檔案如下:
  1. #ifndef _TIMER_H_
  2. #define _TIMER_H_
  3. #include <string>
  4. #include <sstream>
  5. //#include <sys/time.h> //windows下的time.h中沒有timeval定義
  6. #include<windows.h> //windows下代替<sys/time.h>
  7. #include<time.h> //windows下代替<sys/time.h>
  8. usingnamespace std;  
  9. //windows下沒有gettimeofday函式,從網上找的一個替代函式
  10. int gettimeofday(struct timeval *tp, void *tzp)  
  11. {  
  12.     time_t clock;  
  13.     structtmtm;  
  14.     SYSTEMTIME wtm;  
  15.     GetLocalTime(&wtm);  
  16.     tm.tm_year     = wtm.wYear - 1900;  
  17.     tm.tm_mon     = wtm.wMonth - 1;  
  18.     tm.tm_mday     = wtm.wDay;  
  19.     tm.tm_hour     = wtm.wHour;  
  20.     tm.tm_min     = wtm.wMinute;  
  21.     tm.tm_sec     = wtm.wSecond;  
  22.     tm. tm_isdst    = -1;  
  23.     clock = mktime(&tm);  
  24.     tp->tv_sec = clock;  
  25.     tp->tv_usec = wtm.wMilliseconds * 1000;  
  26.     return (0);  
  27. }  
  28. class timer {  
  29. public:  
  30.   timer(string timer_name) {  
  31.     name = timer_name;  
  32.     total_time = 0;  
  33.     calls = 0;  
  34.   };  
  35.   ~timer() {};  
  36.   void tic() {  
  37.     struct timeval tv;  
  38.     gettimeofday(&tv, NULL);  
  39.     last_time = (double)tv.tv_sec + 1e-6*(double)tv.tv_usec;  
  40.     calls++;  
  41.   };  
  42.   void toc() {  
  43.     struct timeval tv;  
  44.     gettimeofday(&tv, NULL);  
  45.     double cur_time = (double)tv.tv_sec + 1e-6*(double)tv.tv_usec;  
  46.     total_time += cur_time - last_time;  
  47.   };  
  48.   constchar *msg() {  
  49.     ostringstream oss;  
  50.     oss << "timer '" << name   
  51.         << "' = " << total_time << " sec in "
  52.         << calls << " call(s)";  
  53.     return oss.str().c_str();  
  54.   };  
  55.   void mexPrintTimer() {  
  56.     mexPrintf("timer '%s' = %f sec in %d call(s)\n", name.c_str(), total_time, calls);  
  57.   };  
  58.   double getTotalTime() {  
  59.     return total_time;  
  60.   };  
  61. private:  
  62.   string name;  
  63.   int calls;  
  64.   double last_time;  
  65.   double total_time;  
  66. };  
  67. #endif
改完timer.h後,再次編譯,錯誤提示為:
  1. cascade.cpp(116) : error C2065: 'INFINITY' : undeclared identifier   
  2. cascade.cpp(130) : error C2065: 'INFINITY' : undeclared identifier   
  3. cascade.cpp(170) : error C2065: 'INFINITY' : undeclared identifier   
  4. cascade.cpp(216) : error C2065: 'INFINITY' : undeclared identifier   
  5. cascade.cpp(217) : error C2065: 'INFINITY' : undeclared identifier   
  6. cascade.cpp(218) : error C2065: 'INFINITY' : undeclared identifier   
  7. cascade.cpp(219) : error C2065: 'INFINITY' : undeclared identifier   
INFINITY是linux中的無窮大,windows下沒有,可以自己定義一個,在cascade.cpp中增加下面的語句:
  1. //INFINITY是Linux下的無窮大標誌,windows下沒有,自己定義一個
  2. #define INFINITY 0xFFFFFFFFF
然後繼續mex cascade.cpp model.cpp,沒有錯誤了。

步驟2 編譯fconv_var_dim.cpp

在matlab命令列中輸入:

  1. >> mex fconv_var_dim.cpp  
提示為:
  1. fconv_var_dim.cpp(2) : fatal error C1083: Cannot open include file: 'pthread.h': No such file or directory   
  2.   D:\PROGRA~1\MATLAB\R2012B\BIN\MEX.PL: Error: Compile of 'fconv_var_dim.cpp' failed.   
  3. Error using mex (line 206)  
  4. Unable to complete successfully.  
找不到pthread.h,可以從這個網站下載Pthreads的win32版本,下載整個pthreads-w32-2-9-1-release壓縮包,我們要用到其中兩個標頭檔案。

將pthread.h的目錄新增到環境變數,嫌麻煩的話直接把pthread.h放到VS2010安裝目錄的include資料夾中。再次編譯,提示:

  1. C:\Program Files\Microsoft Visual Studio 10.0\VC\INCLUDE\pthread.h(299) : fatal error C1083: Cannot open include file: 'sched.h': No such file or directory   
  2.   D:\PROGRA~1\MATLAB\R2012B\BIN\MEX.PL: Error: Compile of 'fconv_var_dim.cpp' failed.   
  3. Error using mex (line 206)  
  4. Unable to complete successfully.  
sched.h就是我們需要的第二個檔案,也在pthreads-w32-2-9-1-release中,把它也複製到VS2010的include資料夾中,再次編譯,提示:
  1. \voc-release4.01-star-cascade\star-cascade\fconv_var_dim.cpp(77) : error C4716: 'process' : must return a value   
  2.   D:\PROGRA~1\MATLAB\R2012B\BIN\MEX.PL: Error: Compile of 'fconv_var_dim.cpp' failed.   
  3. Error using mex (line 206)  
  4. Unable to complete successfully.  
開啟fconv_var_dim.cpp檔案,看process函式的定義,函式型別是void *,但函式體中沒有return,在windows中執行voc-release4.01的文章中說過,可以通過將process函式的型別改為void來解決沒有返回值的錯誤,但這裡如果還這樣改的話會出現下面的錯誤:
  1. fconv_var_dim.cpp(128) : error C2664: 'pthread_create' : cannot convert parameter 3 from 'void (__cdecl *)(void *)' to 'void *(__cdecl *)(void *)'   
  2.         None of the functions with this name in scope match the target type   
  3.   D:\PROGRA~1\MATLAB\R2012B\BIN\MEX.PL: Error: Compile of 'fconv_var_dim.cpp' failed.   
  4. Error using mex (line 206)  
  5. Unable to complete successfully.  
因為後面有個用process函式做引數的地方:
  1. if (pthread_create(&ts[i], NULL, process, (void *)&td[i]))  
改變process函式的型別會導致引數型別不匹配而無法傳參。

怎麼辦呢,到這裡我也沒轍了。

翻牆去google上查,偶然發現一個大牛的個人主頁,他做了star-cascade DPM演算法的改進,網頁上有原始碼下載,使用說明裡有句話:

The distribution comes with precomputed mex files for 64-bit windows, linux and mac systems.也就是說他為64位的windows、linux和mac系統編譯好了檔案,那麼他的原始碼會不會是win32上的呢,趕緊下載下來一看,果真,他提供了win32版本的和MacOSX版本的原始碼,而且也是基於voc-release4進行改進的。所以,我們可以下載 上的dtbb_1.zip原始碼,解壓後,進入star-cascade目錄,可以看到幾個cc檔案都為64位系統mex編譯好了,真是良心啊。


.mexa64是給linux-64編譯好的,.mexmaci64是給Mac-64編譯好的,.mexw64是為win-x64編譯好的。所以,如果你是64位系統,就不用編譯了,人家已經給弄好啦。我們編譯完成後,會生成一個字尾為.mexw32的檔案,這個檔案可以看做c++和matlab之間的一個介面,可以在matlab中呼叫,實現matlab和c++混合程式設計。

再仔細看看,發現他寫了個makefile_windows.m檔案,方便windows使用者快速編譯,其實裡面也就兩句話:

  1. mex cascade.cc model.cpp  
  2. mex fconv_var_dim.cpp  
不用說,幾個需要編譯的cpp檔案肯定也已經針對win32系統做了修改,我們可以挨個看看,cascade.cpp中,他直接將include timer.h註釋掉了,所以timer.h中不相容windows的都不用管了,更簡單,加入了INFINITY的定義 #define INFINITY 1e5。再看看fconv_var_dim.cpp,也就是我不會改的地方,他是在process函式的末尾加上:
  1. #ifdef MThread
  2. thread_exit(NULL);  
  3. #else
  4. eturn NULL;  
  5. #endif
然後將用到process函式的地方改為:
  1. #ifdef MThread
  2.  (pthread_create(&ts[i], NULL, process, (void *)&td[i]))  
  3.         mexErrMsgTxt("Error creating thread");    
  4. #else
  5.     process((void *)&td[i]);  
  6.     mxSetCell(plhs[0], i, td[i].mxC);  
  7. #endif
我們直接用他的fconv_var_dim.cpp檔案替換原來的,再次mex fconv_var_dim.cpp,無錯誤提示,成功了。

步驟3 執行star-cascade

將matlab的工作目錄返回到voc-release4.01,先輸入命令:

  1. addpath star-cascade  

將star-cascade加入搜尋目錄,然後呼叫cascade_demo,就可以看到檢測結果啦。