请先观看《linuxubuntu12.04上opencv2.4.7 cmake2.8.12.1环境搭建》,对linux上编译opencv有基本了解再来看这篇文章效果最佳
经过了昨天一天苦逼的研究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); } else if(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. [root@lin/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去显示吧,哎,茅塞顿开。。。
突然想想写博文好累啊,自己理解了不简单,让别人也理解那就更难了,但是写博文的时候我也学到了很多,我也会继续把我的学习所得分享出来,大家也要多多和别人交流哦,否则就是闭门造车。
今天就到这里,米娜桑,我要回去过清明小长假咯,下次见。