ARM開發板OK6410移植opencv-2.4.7庫qt介面顯示(附加各種問題解決方案)
轉載:http://blog.csdn.net/jiebaoabcabc/article/details/22935185
經過了昨天一天苦逼的研究opencv原始碼、arm-linux編譯器工作原理和堅持不懈的make,我終於移植成功了opencv-2.4.7for arm庫到OK6410上,遇到了各種問題,研究了很長時間,連上課時候都在想原因和解決方案,都讓我想翹課。。。
接下去我會簡單分析整個移植過程,講解一些網上常見問題,不過我建議大家還是自己先去試試移植看看,花點時間就能學到很多。
首先介紹我的開發環境:
主機OS:UBUNTU12.04
宿主機:飛凌OK6410
宿主機核心:linux3.6
opencv版本:opencv2.4.7
cmake版本:cmake2.8.12.1
交叉工具鏈:Sky arm-linux4.3.3 天嵌
接下去還是先開始利用cmake工具定製opencv,cmake的影象介面比較容易上手,所以這裡用cmake-gui講解cmake過程:
1.虛擬機器的童鞋可以先備份虛擬檔案
2.su- root切換到root使用者忽略許可權問題
3.mkdir/home/opencv/opencv-arm/ 存放makefile和一些相關cmake配置檔案
4.cmake-gui執行cmake gui
5.跟移植到linux-pc上一樣填入sourcecode和build the binaries目標路徑
6.點選Configure,保持unixmakefiles選項,選擇specify options for corss-compiling來選擇編譯器路徑
7.operating system填入os名,即編譯器名arm-linux os version這個可以不填,我不清楚這個填核心版本還是編譯器版本,compilers C填入編譯器arm-linux-gcc的elf路徑,C++填入編譯器arm-linux-g++的elf路徑,target root是尋找lib和include檔案的,這些檔案都在arm-linux編譯器檔案路徑下,比如我的編譯器路徑就是/home/arm/Sky/opt/EmbedSky/4.3.3/
8.finish後會提示:error inconfiguration procss,project files may be invalid,指的是它預設配置對你給定的os是不支援的。這是我遇到的第一個問題,究竟什麼不支援呢,為了通過配置,我去了解cmake都為opencv配置了些什麼(其實看他提示的出錯資訊就已經知道問題在哪,但是也不能稀裡糊塗的配置,我還是決定看看配置項):
opencv-arm/CMakeCache.txt是cmake記錄配置資訊的檔案,也是makefile重要資訊來源,這裡的配置選項都有註釋,可以方便大家理解配置選項的含義。
重要選項:
BUILD開頭的都是構建檔案,選上則將尋找對應原始碼然後構建在opencv庫中
WITH開頭的都是opencv對相應選項內容的支援,選上則表示opencv庫將支援所選內容,所以開發板不支援的選項不要選。
CMAKE_BUILD_TYPE構建型別,一般填入Release構建釋出版本
CMAKE_INSTALL_PREFIX安裝路徑,可以指定自己需要安裝的路徑
我的安裝路徑是usr/local/arm/opencv/-> bin/ 存放elf
-> include/ 存放opencv opencv2
-> lib/ 存放.so .a
-> share/ 存放例子中使用的xml資原始檔
CMAKE_EXE_LINKER_FLAGS連結標記,-pthread支援執行緒,-ldl避免未定義dlopen,-lrt避免未定義clock_gettime。這個要在opencv-arm/CMakeCache.txt中修改。
CMAKE_EXE_LINKER_FLAGS:STRING=''
替換成:CMAKE_EXE_LINKER_FLAGS:STRING=-pthread-ldl -lrt 全動態連結
CMAKE_EXE_LINKER_FLAGS是我遇到的第二個問題,不修改這個make編譯時會有以下錯誤:
1.執行緒和clock符號錯誤 解決方法: 新增連結器選項-pthread-lrt
../../lib/libopencv_core.so: undefinedreference to `pthread_spin_init'
../../lib/libopencv_core.so: undefinedreference to `pthread_spin_unlock'
../../lib/libopencv_core.so: undefinedreference to `pthread_spin_lock'
../../lib/libopencv_core.so: undefinedreference to `pthread_spin_destroy'
../../lib/libopencv_core.so: undefinedreference to `pthread_once'
../../lib/libopencv_core.so: undefinedreference to `clock_gettime'
../../lib/libopencv_core.so: undefinedreference to `pthread_spin_trylock'
collect2: ld returned 1 exit status
make[2]: *** [bin/opencv_perf_core] 錯誤 1
make[1]: ***[modules/core/CMakeFiles/opencv_perf_core.dir/all] 錯誤 2
make: *** [all] 錯誤 2
2.dlopen符號錯誤 解決方法: 新增連結器選項-ldl
../../lib/libopencv_ocl.so: undefinedreference to `dlopen'
../../lib/libopencv_ocl.so: undefinedreference to `dlsym'
collect2: ld returned 1 exit status
make[2]: *** [bin/opencv_perf_ocl] 錯誤 1
make[1]: ***[modules/ocl/CMakeFiles/opencv_perf_ocl.dir/all] 錯誤 2
理解的差不多了,來看看怎麼讓cmake過去,首先看錯誤提示:
CMake Error at cmake/FindCUDA.cmake:762(if):
if given arguments:
"CUDA_VERSION""VERSION_GREATER" "5.0" "AND""CMAKE_CROSSCOMPILING" "AND" "MATCHES" "arm""AND" "EXISTS""CUDA_TOOLKIT_ROOT_DIR-NOTFOUND/targets/armv7-linux-gnueabihf"
Unknown arguments specified
Call Stack (most recent call first):
cmake/OpenCVDetectCUDA.cmake:26(find_package)
cmake/OpenCVFindLibsPerf.cmake:24 (include)
CMakeLists.txt:423 (include)
這個CUDA有問題,看看WITH_CUDA預設確實是選中的,來到CMakeCache.txt檢視發現這個選項是顯示卡NVidiaCuda Runtime support的支援,我也很希望開發板能支援顯示卡- -,還是把WITH_CUDA去掉,順便去掉WITH_TIFF以免編譯時報tiff error。
總結一下修改的地方:
去掉WITH_TIFF WITH_CUDA
修改CMAKE_BUILD_TYPE為Release
修改CMAKE_INSTALL_PREFIX 路徑可以參考我的想法,而且千萬不要跟pc的庫重疊
到opencv-arm/CMakeCache.txt下找到CMAKE_EXE_LINKER_FLAGS:STRING=''
替換成:CMAKE_EXE_LINKER_FLAGS:STRING=-pthread-ldl -lrt
9.再次configurate 這次還會有許多不支援的選項,但是可以generate,我們就忽略它們,但是其中有一個我可以改 PYTHON_PACKAGES_PATH可以修改路徑,到/usr/local/lib下找到這個包,我沒試過,但是pkg-config的尋路功能對我們來說還是很有用的。
10.點選generate,如果提示generatedone說明可以拿去make了。
11.進入構建的目錄,我的是opencv-arm目錄下,執行make,各忙各的思密達。
12.終於100%了,然後還在這個目錄下執行makeinstall,如果安裝成功不會報錯,到安裝目錄下看lib下是否有.so檔案,執行file xxxxxx.so看看是不是支援arm的共享庫,是的話編譯就基本成功了。
這裡我再提個我遇到的問題,我原本以為opencv的gui可以依賴qtgui執行在arm板子上,所以我在配置時選了WITH_QT和WITH_OPENGL,把qt for arm的qmake路徑填上,結果編譯報錯,看到i386字樣我心頭一緊,趕緊跑去看CMakeCache.txt,結果一搜QT,顯示出滿螢幕的i386的qt庫路徑,看看cmake貌似沒這配置還是我沒找到怎麼的,這事情我還是以後再弄吧,opencv的gui貌似需要gtk或qt的支援才能用highgui裡的功能,這麼一來highgui庫就沒用了,不知道有沒有人可以讓highgui在arm板上用起來,我挺喜歡用這裡面的功能。我有空也會去看看WITH_QT該怎麼去支援。那顯示的工作還是讓使用者主動讓opencv移交給qt去做吧。
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
移植和測試:
移植也是比較麻煩的事,過程講解後會談談我遇到的問題
測試沒問題的移植過程:
1.將安裝目錄下/lib/*所有檔案拷到開發板/lib下,不要改路徑,必須在/lib下。容量不足的可以只拷執行需要的檔案
2.把安裝目錄下/bin/*拷到開發板/bin下,這一步可以不要
3.新建qt工程,使用dialog介面,在.pro檔案中加入
- INCLUDEPATH+= /usr/local/arm/4.4.3/opencv/include/opencv \
- /usr/local/arm/4.4.3/opencv/include/opencv2 \
- /usr/local/arm/4.4.3/opencv/include
- LIBS+= /usr/local/arm/4.4.3/opencv/lib/libopencv*
4.新建switch.cpp原始檔,複製上IplImage與QImage間轉換用程式碼:
- #include "switch.h"
- ImageCVtoQT::ImageCVtoQT(IplImage *_srcImage)
- :srcImage(_srcImage)
- {
- assert(srcImage != NULL);
- width = srcImage -> width;
- height = srcImage -> height;
- channel = srcImage -> nChannels;
- }
- ImageCVtoQT::~ImageCVtoQT()
- {
- cvReleaseImage(&srcImage);
- }
- const QImage ImageCVtoQT::getQtImage()
- {
- QImage desImage = QImage(width, height, QImage::Format_RGB32);
- for(int i=0; i<height; i++)
- {
- for(int j=0;j<width; j++)
- {
- int r,g,b;
- if(RGB_TYPE == channel)
- {
- b = (int)CV_IMAGE_ELEM(srcImage, uchar , i, j*3+0);
- g = (int)CV_IMAGE_ELEM(srcImage, uchar , i, j*3+1);
- r = (int)CV_IMAGE_ELEM(srcImage, uchar , i, j*3+2);
- }
- elseif(GRAY_TYPE == channel)
- {
- b = (int)CV_IMAGE_ELEM(srcImage, uchar, i, j);
- g = b;
- r = b;
- }
- desImage.setPixel(j, i, qRgb(r, g, b));
- }
- }
- return desImage;
- }
- /////////////////////////////////////////////////////////////////////
- ImageQTtoCV::ImageQTtoCV(QImage _srcImage)
- :srcImage(_srcImage)
- {
- assert(!srcImage.isNull());
- width=srcImage.width();
- height=srcImage.height();
- }
- ImageQTtoCV::~ImageQTtoCV()
- {
- }
- IplImage *ImageQTtoCV::getCvImage()
- {
- IplImage *desImage=cvCreateImage(cvSize(width,height),8,3);
- for(int i=0;i<height;i++)
- {
- for(int j=0;j<width;j++)
- {
- QRgb rgb=srcImage.pixel(j,i);
- CV_IMAGE_ELEM(desImage,uchar,i,j*3+0)=qBlue(rgb);
- CV_IMAGE_ELEM(desImage,uchar,i,j*3+1)=qGreen(rgb);
- CV_IMAGE_ELEM(desImage,uchar,i,j*3+2)=qRed(rgb);
- }
- }
- return desImage;
- }
5.新建switch.h標頭檔案供呼叫:
- #ifndef SWITCH_H
- #define SWITCH_H
- //#include "highgui.h"
- #include "cv.h"
- #include "cxcore.h"
- #include <QImage>
- #define RGB_TYPE 3
- #define GRAY_TYPE 1
- class ImageCVtoQT
- {
- public:
- ImageCVtoQT(IplImage *_srcImage);
- ~ImageCVtoQT();
- const QImage getQtImage(void);
- private:
- IplImage *srcImage;
- //QImage desImage;
- int width;
- int height;
- int channel;
- };
- class ImageQTtoCV
- {
- public:
- ImageQTtoCV(QImage _srcImage);
- ~ImageQTtoCV();
- IplImage *getCvImage(void);
- private:
- QImage srcImage;
- int width;
- int height;
- //int channel;
- };
- #endif // SWITCH_H
上述兩個類非常棒,感謝網上牛人,我也為了解這兩個類的實現而看了半天的原始碼。QT和OPENCV原始碼間看的我比較暈。
6.在介面檔案中拉入一個lable,將其展開與窗體同大小,我沒有太多qt編寫經驗,顯示圖只會lable顯示,而且畫圖的效果- -,就不多說了,等這個移植好後看看qt的程式設計吧。。。
7.在dialog的建構函式中進行影象的匯入和轉換,過程是QImage匯入圖片-> 原始IplImage->opencv對原始IplImage進行處理->處理後IplImage->QImage->使用QImage藉助qt窗體上顯示圖片。
dialog.cpp要貼的內容:- #include "dialog.h"
- #include "ui_dialog.h"
- #include "switch.h"
- #include "QtGui"
- Dialog::Dialog(QWidget *parent) :
- QDialog(parent),
- ui(new Ui::Dialog)
- {
- ui->setupUi(this);
- //宣告IplImage指標
- IplImage *pImg = NULL;
- QImage *qImg = new QImage;
- //載入圖片
- if(!(qImg->load("/home/project/sao22.bmp"))) // 我的開發板支援bmp格式,小心路徑
- {
- return;
- }
- //switch
- ImageQTtoCV qtc(*qImg);
- pImg = qtc.getCvImage();
- if(!pImg)
- return;
- IplImage *pGrayImg = NULL;
- pGrayImg = cvCreateImage(cvGetSize(pImg), IPL_DEPTH_8U, 1);
- cvCvtColor(pImg, pGrayImg, CV_BGR2GRAY);
- ImageCVtoQT ctq(pGrayImg);
- *qImg = ctq.getQtImage();
- if(!qImg)
- return;
- ui->label->setPixmap(QPixmap::fromImage(*qImg));
- }
- Dialog::~Dialog()
- {
- delete ui;
- }
8.qt用qmake for arm構建工程,完成後將elf檔案從debug資料夾中拷貝到開發板中(千萬不要在第三級目錄以上,否則會報錯),將圖片也拷到對應路徑上
9.寫好啟動指令碼後執行,就可以看到螢幕上顯示處理後的gray圖片(愛圖)了:
這說明opencv庫可以正常工作,其他功能我還沒試,但基本的建立圖片和顏色處理是沒問題的,至此opencv2.4.7庫移植到arm開發板ok6410成功。
接下來我再來說說我遇到的問題:
1. :-1: 警告:../../lib/libopencv_core.so,needed by /usr/local/arm/4.4.3/opencv/lib/libopencv_calib3d.so,not found (try using -rpath or -rpath-link)
:-1:警告:../../lib/libopencv_flann.so,needed by /usr/local/arm/4.4.3/opencv/lib/libopencv_calib3d.so, not found (tryusing -rpath or -rpath-link)
先是在qt上警告,後在開發板上執行時也報錯:
error while loading shared libraries:../../lib/libcxcore.so: cannot open shared object file: No such file ordirectory
還好我那時還冷靜,看到了../../lib/這個提示,感覺這個庫是不是隻能在特定的路徑下呢?假設當前執行路徑是/home/qt/sd11/,../../所指的資料夾是qt/,但qt/下沒有lib資料夾,這時我把執行目錄換成/home/,../../所指的資料夾是根目錄,根目錄下有lib資料夾,而且裡面放著libcxcore.so,這樣elf就能找到共享檔案了。所以這問題連帶著qt編譯時的報錯一起解決。
第一種方案,編譯qt的工程也要在二級目錄下,不過這樣是很難做到的,應為這樣會使第二級目錄變得很混亂,但是這些警告對產生elf是沒影響的,忽略了吧。elf的執行目錄在第二級目錄下倒是還算沒問題。
第二種方案,編譯qt的目錄可以隨意,在../../下建立一個lib資料夾,裡面放入對應.so庫,執行時也這麼做。這麼做貌似更好。
2. [[email protected]/home]#./test11
OpenCV Error: Unspecified error (The functionis not implemented. Rebuild the library with Windows, GTK+ 2.x or Carbonsupport. If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config,then re-run cmake or configure script) in cvNamedWindow, file/home/opencv/opencv-2.4.7/modules/highgui/src/window.cpp, line 483
terminate called after throwing an instanceof 'cv::Exception'
what(): /home/opencv/opencv-2.4.7/modules/highgui/src/window.cpp:483: error:(-2) The function is not implemented. Rebuild the library with Windows, GTK+2.x or Carbon support. If you are on Ubuntu or Debian, install libgtk2.0-devand pkg-config, then re-run cmake or configure script in function cvNamedWindow
出現這個的原因是因為使用者使用了highgui中的功能,比如cvloadiamge之類的。GTK是開源gui,但是開發板應該是很難搭建起這個gui的,arm下qt的gui貌似也很難讓opencv依賴,所以還是不要使用highgui的功能,就乖乖進行opencv和qt的轉換吧。。。
這個轉換的問題我很感激學長的一席話,我本來為了解決這個問題還想移植GTK上去呢- -,當他一說不要移植GTK的時候我就突然想到以前做過qt和cv的轉換,竟然qt能顯示那就讓qt去顯示吧,哎,茅塞頓開。。。
突然想想寫博文好累啊,自己理解了不簡單,讓別人也理解那就更難了,但是寫博文的時候我也學到了很多,我也會繼續把我的學習所得分享出來,大家也要多多和別人交流哦,否則就是閉門造車。
今天就到這裡,米娜桑,我要回去過清明小長假咯,下次見。