编译

为方便断点调试、可视化管理,等等,編译OpenCV要通过編写该平台IDE需要的工程文件。Windows是*.sln(Visual Studio),iOS是project.pbxproj(XCode),Android则是*.mk(Android Studio+NDK)。但OpenCV官方写的是CMake,为此編译要分两个步骤。第一步是使用opencv官方介绍的方法,用CMake编译出结果。第二步是复制第一步生成的哪些文件,结合须要编译的cpp,基于各平台的IDE(Visual Studo/Xcode/NDK)新建opencv工程。

因为已有替代模块,Rose用到的opencv不编译dnn、highgui、imgcodec、videoio。如何不让CMake编译它们,以不编译highgui为例,让CMake找不到<opencv>/modules/highgui/CMakeLists.txt就行了,像把文件名改为CMakeLists.txt_。注:一旦少了模块,CMake将不会生成官方自带的app。

在Windows,考虑到opencv作为app一部分时,会极大变长app链接时间,opencv还是作为dll。但在Android和iOS,Rose已把opencv汇入app源码。为减少include数目,把具体模块下的include/opencv2下内容移动(不是复制)到<opencv>/opencv2。以imgproc为例,把<opencv>/modules/imgproc/include/opencv2下内容移动到<opencv>/opencv2。

一、CMake生成的文件

CMake生成的既有头文件(hpp),也有源文件(cpp),按影响范围分为两类,一类是全局文件,一类是模块私有文件。

全局文件都是头文件,包括opencv_modules.hpp、custom_hal.hpp、cv_cpu_config.h、cvconfig.h。另外有个version_string.inc,它的内容就是一串字符,描述了編译、链接该版本opencv时概述,像包括哪些模块,涉及到哪些开源库,虽然生成时是放在core模块,但把它归到全局文件。在不同平台,全局文件会有不一样内容。除了version_string.inc外的四个文件,Rose给出了能用于Windows、iOS、Android的统一版本。Rose给的version_string.inc是Windows版本,不过把当中换行符由“\r\n”改到了“\n”。

模块私有文件分为两类。第一类是opencl脚本,像opencl_kernels_core.cpp、opencl_kernels_core.hpp。第二类和simd优化相关,像accum.avx.cpp,stat.sse4_2.cpp。对不同平台,这些文件内容是一样的(iOS不支持superres,因而没有opencl_kernels_superres.cpp/hpp)。Rose把它们放在了<opencv>/autogen。

Windows

预定义宏

  • __OPENCV_BUILD=1。iOS、Android都需要
  • CVAPI_EXPORTS。要把相关函数、类放入opencv.dll
  • _VARIADIC_MAX=10。还不清楚为什么要定义这宏,只是CMake生成的sln定义了

换到Visual Studio自建工程时,由于CMake生成的sln用的字符集是Multi-Byte Character Set,为换到Unicode Character Set,须修改OpenCV源码。

<opencv>/modules/core/src/glob.cpp 
WIN32_FIND_DATA data---->WIN32_FIND_DATAA data;

编译Windows要做到让app和opencv使用同样运行库!

#include <opencv2/core/utility.hpp>
int main( int argc, const char** argv )
{	
	cv::Point2f point = cv::Point2f((float)500, (float)500);

	std::vector<cv::Point2f> tmp;
	tmp.push_back(point);
	std::vector<cv::Point2f>* tmp2 = &tmp;
	{
		cv::InputOutputArray _corners(tmp);
		cv::Mat cornersmat = _corners.getMat();
		// if error, cornersmat is empty!
		int count = cornersmat.checkVector(2, CV_32F);
		CV_Assert( count >= 0 );
	}
	return 0;
}

以上代码如果正确执行,cornersmat的rows、cols都是1,后面count也是1。但会出错,出错时cornersmat是个空Mat,后面的count值是-1!查这问题原因,要深入到getMat内部,它会执行下面语句。

const std::vector<uchar>& v = *(const std::vector<uchar>*)obj;

同样是这条语句,app中得到tmp是转换后的正确值(见代码中tmp2),但库中(opencv)代码转换后却是nil!而且可以确定两处的obj值是同一个。要解决这问题,找到办法是让app和opencv使用同一个运行库。

编译基于Rose的app时,使用的是Release,运行库Multi-threaded(/MT)。可使用下来,如果opencv是debug,运行库用(/MT)会使编译失败。为让调试app时能进入opencv,只好让opencv也是release,而为支持断点,须要修改opencv的几处设置。

  • C/C++——General。Debug Information Format改为“Program Database(/Zi)”。
  • C/C++——Optimization。Optimization改为“Disabled(/Od)”)。

iOS

ios不支持superres,要跨平台的app不要用当中的api。

不管是debug还是release,xcode默认都会让ipa带调试符号文件,这会使得opencv达到上百M。为减少尺寸,Release时手动把Generate Debug Symbols设为No。

Android

要配合CMake,须使用NDK-r14b。若使用新版,像NDK-r22,会报错。此处编译只是为得到android下的特定文件,像cvconfig.h。最后生成的opencv使用*.mk,不受NDK版本限制。

在CMake編译时,有些库可能会给編译造成小麻烦,而你已确认不须要它,像libtiff。为强制不編译,可以这么做。1)用文本編辑器打开<opencv>/CMakeLists.txt。2)找到“OCV_OPTION(WITH_TIFF...ON...“,把ON改为OFF。以下是在Windows用CMake編译。

$ cd opencv-4.5.1
$ mkdir android
$ cd android
$ cmake.exe -G"MinGW Makefiles" -DCMAKE_TOOLCHAIN_FILE=../platforms/android/android.toolchain.cmake -DANDROID_NDK="c:\Users\ancientcc\AppData\Local\Android\sdk\ndk-bundle" -DCMAKE_MAKE_PROGRAM="c:\Users\ancientcc\AppData\Local\Android\sdk\ndk-bundle\prebuilt\windows-x86_64\bin\make.exe" -DANDROID_NATIVE_API_LEVEL=21 -DCMAKE_BUILD_TYPE=Release -DANDROID_ABI="armeabi-v7a with NEON" -DANDROID_STL=c++_shared -DBUILD_SHARED_LIBS=ON ..
$ cmake --build .
  • ANDROID_NDK:NDK路径,这个路径建议用绝对路径,相对路径时可能会找不到NDK错误。
  • ANDROID_NATIVE_API_LEVEL:要使用的API Level。为较少改动,开始使用21。使用18会出更多错误。
  • ANDROID_STL:默认使用的是gnustl,但app要求用c++_shared。
  • BUILD_SHARED_LIBS:默认编译成静态库(*.a),但现在编译成*.a时在链接出错,先强制为*.so。

opencv支持在Mac OS X编译,编译方法类似Windows,当然啦,不再须要-G"MinGW Makefiles"、-DCMAKE_MAKE_PROGRAM。编译方法类似编译boringssl中介绍的Android部分。以下是当中的cmake命令。

cmake -DCMAKE_TOOLCHAIN_FILE=../platforms/android/android.toolchain.cmake -DANDROID_NDK=/Users/ancientcc/android-ndk-r14b -DANDROID_NATIVE_API_LEVEL=21 -DCMAKE_BUILD_TYPE=Release -DANDROID_ABI="armeabi-v7a with NEON" -DANDROID_STL=c++_shared -DBUILD_SHARED_LIBS=ON ..

为成功编译,须要修改两个ndk中的.h,修改原因:“<ndk>/platforms/android-21/arch-arm/usr/include下有同名文件,因为是“<>”,底下的cmath会优先读这目录下文件,导致编译时出错。”

<ndk>/sources/cxx-stl/llvm-libc++/include/cmath
line 305-->#include <math.h>--->#include "math.h"
<ndk>\sources\cxx-stl\llvm-libc++\include\cstdlib
line 86-->#include <stdlib.h>--->#include "stdlib.h"

 

二、升级

一旦有了OpenCV新版本,可按下以下步骤让尽快保持同步。

  • 把dnn、highgui、imgcodec、videoio下的CMakeLists.txt改名为CMakeLists.txt_。    
  • 按官方写的,在Windows平台用CMake編译出OpenCV。
  • 查看opencv_modules.hpp、custom_hal.hpp、cv_cpu_config.h、cvconfig.h有没有出现变化,有的话修改。
  • 复制模块私有文件到<opencv>/autogen,version_string.inc到<opencv>/。各文件中的换行符由“\r\n”改到“\n”。
  • 通过CMake編译出的结果,修改工程要編译哪些源文件。

全部评论: 0

    写评论: