好戏上演:为Android项目编译OpenCV 4.1.0,可按需编译模块,大大减少空间。

3,050 阅读4分钟

“编译库就像烹饪美食一样。有时候我们只想要一个简单的番茄酱,而不是五花八门的酱料。” —— 某位聪明的程序员

在这篇博客中,我们将一起探讨如何为您的Android项目编译OpenCV 4.1.0版本。毕竟,谁不喜欢一碗美味的意大利面(代码)配上简单的番茄酱(库)呢?

在开始之前,我们需要确保已经安装了以下工具和库:

  1. Android NDK(我们将使用20.0.5594570版本)
  2. OpenCV 4.1.0源代码
  3. CMake(我们将使用3.26.3版本)
  4. Ninja构建系统
  5. 硬件环境Macbook Pro m1 Pro

准备好了吗?那么让我们开始吧!

第一步:下载OpenCV 4.1.0源代码

获取源代码: 从OpenCV的GitHub仓库克隆4.1.0版本的代码:

git clone https://github.com/opencv/opencv.git
cd opencv
git checkout 4.1.0

第二步:创建构建目录

为了保持源代码目录的整洁,我们将在opencv-4.1.0目录旁边创建一个名为build_android的构建目录。这个目录将存放我们编译生成的文件。

第三步:编译OpenCV库

现在,我们需要编译OpenCV库。首先,我们将为armeabi-v7aarm64-v8aABI创建单独的构建目录。为了使这个过程更简单,我们将使用以下命令:

mkdir build_android_armv7
mkdir build_android_arm64

接下来,我们将使用CMake和Ninja工具为不同的ABI构建OpenCV库。让我们首先为armeabi-v7a构建库:

# 切换到创建的armeabi-v7a构建目录
cd build_android_armv7

# 运行CMake命令以生成构建文件,指定以下选项:
# -DCMAKE_TOOLCHAIN_FILE: 指定Android NDK的toolchain文件路径
# -DANDROID_ABI: 设置目标Android ABI(本例中为armeabi-v7a)
# -DANDROID_PLATFORM: 设置目标Android平台版本(本例中为android-21)
# -DANDROID_STL: 设置C++标准库类型(本例中为c++_static)
# -DANDROID_NATIVE_API_LEVEL: 设置Android Native API级别(本例中为21)
# -DBUILD_SHARED_LIBS: 是否构建共享库(本例中为ON)
# -DBUILD_EXAMPLES: 是否构建示例(本例中为OFF)
# -DBUILD_DOCS: 是否构建文档(本例中为OFF)
# -DBUILD_PERF_TESTS: 是否构建性能测试(本例中为OFF)
# -DBUILD_TESTS: 是否构建测试(本例中为OFF)
# -DBUILD_ANDROID_EXAMPLES: 是否构建Android示例(本例中为OFF)
# -DBUILD_LIST: 设置要构建的模块列表
# -GNinja: 使用Ninja作为构建系统
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
      -DANDROID_ABI=armeabi-v7a \
      -DANDROID_PLATFORM=android-21 \
      -DANDROID_STL=c++_static \
      -DANDROID_NATIVE_API_LEVEL=21 \
      -DBUILD_SHARED_LIBS=ON \
      -DBUILD_EXAMPLES=OFF \
      -DBUILD_DOCS=OFF \
      -DBUILD_PERF_TESTS=OFF \
      -DBUILD_TESTS=OFF \
      -DBUILD_ANDROID_EXAMPLES=OFF \
      -DBUILD_LIST=core,imgproc,imgcodecs,videoio,highgui,objdetect \
      -GNinja \
      ../

假设需要构建所有模块,删掉-DBUILD_LIST这一行即可

现在我们已经生成了构建文件,接下来我们将使用Ninja来实际构建库:

# 使用Ninja进行构建
ninja

在构建完成后,我们会得到针对armeabi-v7a ABI的OpenCV库。同样的步骤也可以应用于arm64-v8a ABI,只需将-DANDROID_ABI选项更改为arm64-v8a并进入build_android_arm64目录即可:

# 切换到创建的arm64-v8a构建目录
cd ../build_android_arm64

# 运行CMake命令以生成构建文件,指定以下选项(与前面的命令类似,但将ANDROID_ABI更改为arm64-v8a)
cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
      -DANDROID_ABI=arm64-v8a \
      -DANDROID_PLATFORM=android-21 \
      -DANDROID_STL=c++_static \
      -DANDROID_NATIVE_API_LEVEL=21 \
      -DBUILD_SHARED_LIBS=ON \
      -DBUILD_EXAMPLES=OFF \
      -DBUILD_DOCS=OFF \
      -DBUILD_PERF_TESTS=OFF \
      -DBUILD_TESTS=OFF \
      -DBUILD_ANDROID_EXAMPLES=OFF \
      -DBUILD_LIST=core,imgproc,imgcodecs,videoio,highgui,objdetect \
      -GNinja \
      ../

# 使用Ninja进行构建
ninja

经过这些步骤,您已经成功编译了针对armeabi-v7aarm64-v8a ABI的OpenCV库。接下来,我们将继续将这些库整合到您的Android项目中。

下图为我编译好的so文件。

image.png

看到这里可能有人要问,我制定了六个模块,为什么生成了九个so文件? 对于OpenCV,某些模块会依赖于其他模块。因此,在构建特定模块时,其依赖项也会被构建。在指定的六个模块中,有些模块依赖于其他模块,这就是为什么看到了多于六个的.so文件的原因。

生成的.so文件列表及其可能的依赖关系:

  • libopencv_core.so(核心模块,几乎所有其他模块都依赖于此模块)
  • libopencv_objdetect.so(依赖于coreimgproccalib3dfeatures2dhighguiimgcodecsflann等)
  • libopencv_calib3d.so(依赖于coreimgprocfeatures2d等)
  • libopencv_features2d.so(依赖于coreimgprocflann等)
  • libopencv_highgui.so(依赖于coreimgcodecsvideoio等)
  • libopencv_videoio.so(依赖于coreimgcodecs等)
  • libopencv_imgcodecs.so(依赖于coreimgproc等)
  • libopencv_imgproc.so(依赖于core
  • libopencv_flann.so(依赖于core

即使你仅指定了六个模块,但由于它们的依赖关系,还是生成了其他模块的.so文件。在实际应用中,这些依赖关系可能是必要的。

第五步:将编译好的库整合到Android项目中

现在我们已经为两种不同的ABI编译了OpenCV库,接下来我们需要将这些库整合到您的Android项目中。为了简化这个过程,请遵循以下步骤:

  1. build_android_armv7build_android_arm64目录中的lib文件夹复制到您的Android项目的jniLibs目录中。如果jniLibs目录尚不存在,请创建它。您的项目结构应该如下所示:
your-android-project
│
└───app
    └───src
        └───main
            └───jniLibs
                ├───armeabi-v7a
                │   └───libopencv_*.so
                └───arm64-v8a
                    └───libopencv_*.so
  1. 将OpenCV库的头文件也复制到项目中。您可以将opencv-4.1.0/include目录复制到项目的cpp目录下(如果尚不存在,请创建它)。您的项目结构应该如下所示:
your-android-project
│
└───app
    └───src
        └───main
            ├───cpp
            │   └───include
            │       └───opencv2
            └───jniLibs
                ├───armeabi-v7a
                │   └───libopencv_*.so
                └───arm64-v8a
                    └───libopencv_*.so
  1. 在您的项目中使用OpenCV库。首先,在您的项目的CMakeLists.txt文件中,添加以下内容:
# 添加OpenCV头文件路径
include_directories(${CMAKE_SOURCE_DIR}/cpp/include)

# 链接OpenCV库
function(add_opencv_lib LIB_NAME)
    add_library(${LIB_NAME} SHARED IMPORTED)
    set_target_properties(${LIB_NAME} PROPERTIES
                          IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/jniLibs/${ANDROID_ABI}/${LIB_NAME}.so)
    target_link_libraries(your_target_name ${LIB_NAME})
endfunction()

add_opencv_lib(libopencv_core)
add_opencv_lib(libopencv_imgproc)
add_opencv_lib(libopencv_imgcodecs)
add_opencv_lib(libopencv_videoio)
add_opencv_lib(libopencv_highgui)
add_opencv_lib(libopencv_objdetect)

记得将your_target_name替换为您项目中CMakeLists.txt中的目标名称。

现在,您已成功将编译好的OpenCV库整合到了您的Android项目中。您可以在项目中使用OpenCV提供的各种功能了。

结束语

在这篇博客文章中,我们详细介绍了如何为Android项目编译OpenCV 4.1.0库的整个过程。我们了解了如何使用CMake和Ninja构建工具为不同的ABI生成库文件,以及如何将这些库整合到您的Android项目中。

希望这篇文章能对您有所帮助。现在,您可以好好享受那碗美味的意大利面(代码)和番茄酱(库)了!如果您在编译过程中遇到任何问题,请在评论区留言,我们将尽力提供帮助。祝您编程愉快