Android OpenCV SDK 编译教程(WSL2 Ubuntu 22.04 环境)

在 Android 项目中集成 OpenCV 时,若官方预编译库无法满足需求------例如你需要启用opencv_contrib扩展模块,或者希望精细化控制所包含的库模块以优化 APK 体积------那么从源码开始自行编译 OpenCV Android SDK,无疑是最灵活、最可控的方案。反之,如果你的项目仅需 OpenCV 最核心的基础功能,直接通过 Maven 依赖(如 org.opencv:opencv)无疑是更快捷的选择。

本文将详细分享从环境准备、源码编译,到最终生成定制化 AAR 包的完整操作流程,希望能帮助你在项目中更高效地集成 OpenCV。

一、前提条件

在开始之前,请确保系统已经配置好以下环境(版本仅供参考):

  1. Python 3:3.10.12
  2. Android NDK:r28c
  3. Android SDK:API 36
  4. Java JDK:17

备注:NDK/SDK/JDK 版本并非固定,但需与项目和编译脚本保持兼容。

二、获取 OpenCV 源码

你可以直接从 GitHub Releases 下载指定版本(推荐稳定性更佳),或使用 git checkout 切换到对应版本。

复制代码
https://github.com/opencv/opencv/releases/tag/4.12.0
https://github.com/opencv/opencv_contrib/releases/tag/4.12.0

选择下载 zip / tar.gz后解压 或以 git 克隆都完全可以,但请确保 opencvopencv_contrib 的版本号一致,否则会导致模块匹配错误。

三、自定义 NDK 配置文件

OpenCV 提供的 build_sdk.py 能通过配置文件控制编译过程;但官方默认只给到 ndk-25.config.py,这个配置未必能满足你的需求(如支持 16 KB 页面大小),因此通常 建议自定义一个配置文件

以下为一个满足 Android SDK 36 / NDK r28c 的示例配置:

复制代码
import os
from build_sdk_helper import ABI

ANDROID_NATIVE_API_LEVEL = int(os.environ.get('ANDROID_NATIVE_API_LEVEL', 24))

cmake_common_vars = {
    'ANDROID_COMPILE_SDK_VERSION': os.environ.get('ANDROID_COMPILE_SDK_VERSION', 36),
    'ANDROID_TARGET_SDK_VERSION': os.environ.get('ANDROID_TARGET_SDK_VERSION', 36),
    'ANDROID_MIN_SDK_VERSION': os.environ.get('ANDROID_MIN_SDK_VERSION', ANDROID_NATIVE_API_LEVEL),
    'ANDROID_GRADLE_PLUGIN_VERSION': '8.12.0',
    'GRADLE_VERSION': '8.13',
    'KOTLIN_PLUGIN_VERSION': '2.0.21',
}

ABIs = [
    #ABI("2", "armeabi-v7a", None, ndk_api_level=ANDROID_NATIVE_API_LEVEL, cmake_vars=cmake_common_vars),
    ABI("3", "arm64-v8a", None, ndk_api_level=ANDROID_NATIVE_API_LEVEL, cmake_vars=cmake_common_vars),
    #ABI("5", "x86_64", None, ndk_api_level=ANDROID_NATIVE_API_LEVEL, cmake_vars=cmake_common_vars),
    #ABI("4", "x86", None, ndk_api_level=ANDROID_NATIVE_API_LEVEL, cmake_vars=cmake_common_vars),
]

配置项说明

1. ANDROID_NATIVE_API_LEVEL

指定 NDK 最小 API Level,可通过外部环境变量覆盖。

2. cmake_common_vars

用于控制 CMake 编译环境、Gradle 插件版本、Kotlin 插件版本等,与 Android Studio/AGP 的兼容性强相关。

3. ABIs

用于指定要编译的 CPU 架构。

你当前只启用了:

  • arm64-v8a(最推荐、也是多数设备默认支持的 ABI)

如有需要,也可开启 armeabi-v7ax86x86_64 等架构。


四、编译命令示例

假设你的目录结构如下:

复制代码
opencv/
 ├── opencv-4.12.0
 └── opencv_contrib-4.12.0

在该目录下执行:

复制代码
python3 ./opencv-4.12.0/platforms/android/build_sdk.py \
    --ndk_path /你的NDK目录/android-ndk-r28c \
    --sdk_path /你的SDK目录/sdk \
    --config ./opencv-4.12.0/platforms/android/ndk-28.config.py \
    --extra_modules_path ./opencv_contrib-4.12.0/modules \
    --no_samples_build \
    --opencl ./opencv

编译完成后,会在当前目录生成一个 opencv/OpenCV-android-sdk/ 文件夹,其中的 sdk/ 即为你要集成到项目中的 OpenCV 库。

参数说明:

  • --no_samples_build
    不编译示例工程,节省时间。
  • --opencl
    启用 OpenCL 支持(如果设备不使用,可关闭)。
  • 建议只编译所需 ABI ⇒ 提高速度 + 减小 SDK 体积
  • 路径必须为 Linux 风格 ⇒ 不要混用 Windows 路径
  • 缺依赖不必慌 ⇒ 根据报错按需安装即可
  • 编译可能需要 20--60 分钟 ⇒ 视 CPU 性能及是否启用 contrib 而定

五、AAR打包

本人仍未找到如何打包OpenCV其他静态库到prefab中的办法,如果你在找这个,可以跳过。如果你知道,欢迎评论区分享。

许多开发者遇到的问题往往不在编译,而在 如何将自编译的 OpenCV SDK 打包为可统一维护的 Maven 依赖

1.官方发布的 aar 与 SDK 中的 prefab 结构不一致

官方 SDK 中 prefab 包含:

  • opencv_java4
  • 支持 Prefab OpenCV C++ 头文件自动注入

但你自己用 SDK 工具打包出来的默认 aar 却长这样:

  • opencv_jni_shared
  • 也支持 Prefab OpenCV C++ 头文件,但不带so,头文件没用。

官方自己"左右脑互搏",结果就是很多开发者不知道怎样生成支持原生导入的 OpenCV AAR

2. 可行方案①:发布前手动修改 AAR prefab 内容

通过 Gradle 插件将 opencv_java4.so 写入 prefab 结构,再替换原始 aar,参考代码如下。

kotlin 复制代码
android{
    defaultConfig {
        minSdk = 23

        externalNativeBuild {
            cmake {
                abiFilters += listOf("armeabi-v7a", "arm64-v8a")
                arguments += listOf("-DANDROID_STL=c++_shared")
                targets += listOf("opencv_java4")//原本这里是opencv_jni_shared,改一下
            }
        }
    }
    prefab {
        create("opencv_java4") {//原本这里是opencv_jni_shared,改一下
            headers = "native/jni/include"
        }
    }
}
val originalAarFile = file("$buildDir/outputs/aar/${project.name}-release.aar")
val tempAarDir = file("$buildDir/outputs/aar/tmp")

// 任务 1:重新打包 AAR,向 prefab 添加 opencv_java4.so
val buildFinalAar by tasks.registering(Zip::class) {
    dependsOn("bundleReleaseAar")
    archiveFileName.set(originalAarFile.name)
    // 输出到临时目录
    destinationDirectory.set(tempAarDir)

    // 先复制原始 AAR 内容
    from(zipTree(originalAarFile)){
        // 添加 opencv_java4.so 到 prefab/opencv_java4/lib/<abi>/
        val abiList = listOf("armeabi-v7a", "arm64-v8a")

        abiList.forEach { abi ->
            val soFile = file( "${project.projectDir}/native/libs/$abi/libopencv_java4.so")
            if (soFile.exists()) {
                from(soFile) {
                    into("prefab/modules/opencv_java4/libs/android.$abi")
                    duplicatesStrategy = DuplicatesStrategy.INCLUDE // 这里保证覆盖旧的
                }
            } else {
                println("× 未找到: ${soFile.path}")
            }
        }
    }

    doLast {
        println("Final AAR created at: ${archiveFile.get().asFile.absolutePath}")
    }
}

// 任务 2:替换 AAR
val replaceAar by tasks.registering(Copy::class) {
    dependsOn(buildFinalAar)
    from(buildFinalAar.flatMap { it.archiveFile })
    into(originalAarFile.parentFile)

    doLast {
        println("AAR replaced → ${originalAarFile.absolutePath}")
        tempAarDir.deleteRecursively()
    }
}

// 发布配置
publishing {
    publications {
        register("opencv-release", MavenPublication::class.java) {
            afterEvaluate {
                tasks.named("generateMetadataFileForOpencv-releasePublication") {
                    dependsOn(replaceAar)
                }
                from(components["release"])
            }
            groupId = "org.opencv"
            artifactId = "opencv"
            version = "4.12.0"
        }
    }

    repositories {
        maven {
            name = "my-repo"
            url = "${layout.buildDirectory.get().asFile}/repo"
        }
    }
}

举一反三,这种写法其实能解决很多问题,就是手段比较粗暴,所以特地记录了下来。

CMakeLists.txt代码如下。

cmake 复制代码
cmake_minimum_required(VERSION 3.18.1)

project(opencv_java4)#原本这里是opencv_jni_shared,都改成opencv_java4

# dummy target to bring libc++_shared.so into packages
add_library(opencv_java4 SHARED dummy.cpp)
target_link_options(opencv_java4  PRIVATE "-Wl,--as-needed"  "-Wl,-z,max-page-size=16384"  "-Wl,--allow-shlib-undefined")

3. 可行方案②:直接链接opencv_java4.so

由于opencv_java4.so文件的特殊性,有一种简化操作,将opencv_java这个共享库链接到你新建的opencv_java4库上即可,参考代码如下。

kotlin 复制代码
android{
    defaultConfig {
        minSdk = 23

        externalNativeBuild {
            cmake {
                abiFilters += listOf("armeabi-v7a", "arm64-v8a")
                arguments += listOf(
                    "-DOpenCV_DIR=" + project(":lib_opencv").projectDir + "/native/jni",//替换为你的opencv路径
                    "-DANDROID_TOOLCHAIN=clang",
                    "-DANDROID_STL=c++_shared",
                    "-DANDROID_ARM_NEON=TRUE"
                )
                //删除targets
            }
        }
    }
    prefab {
        create("opencv_java4") {//原本这里是opencv_jni_shared,改一下
            headers = "native/jni/include"
        }
    }
}

// 发布配置
publishing {
    publications {
        register("opencv-release", MavenPublication::class.java) {
            afterEvaluate {
                tasks.named("generateMetadataFileForOpencv-releasePublication") {
                    dependsOn(replaceAar)
                }
                from(components["release"])
            }
            groupId = "org.opencv"
            artifactId = "opencv"
            version = "4.12.0"
        }
    }

    repositories {
        maven {
            name = "my-repo"
            url = "${layout.buildDirectory.get().asFile}/repo"
        }
    }
}

CMakeLists.txt代码如下。

cmake 复制代码
cmake_minimum_required(VERSION 3.18.1)

project(opencv_java4)#原本这里是opencv_jni_shared,都改成opencv_java4
find_package(OpenCV REQUIRED CONFIG) #如果不生效就是OpenCV_DIR配置错误

add_library(opencv_java4 SHARED dummy.cpp)
target_link_libraries(opencv_java4 PRIVATE opencv_java)#这行是关键
target_link_options(opencv_java4  PRIVATE "-Wl,--as-needed"  "-Wl,-z,max-page-size=16384"  "-Wl,--allow-shlib-undefined")

六、最后

本文介绍了在 WSL2 中编译 OpenCV SDK 的完整流程、配置文件技巧,以及最关键的------如何构建支持原生导入的 OpenCV AAR。如果有错误,欢迎指正。此外,如果本文对你有帮助,欢迎点赞、收藏、转发,或者请作者喝杯咖啡,嘿嘿 ☕️😊。

相关推荐
python百炼成钢1 小时前
49.Linux音频驱动
android·linux·音视频
向上的车轮1 小时前
图像处理OpenCV与深度学习框架YOLOv8的主要区别是什么?
图像处理·深度学习·opencv·yolov8
棒棒的皮皮1 小时前
【OpenCV】Python图像处理之像素操作
图像处理·python·opencv
四代水门1 小时前
简易视频预览器
android·android-studio
曼巴UE51 小时前
UE5 C++ TSet 创建初始和迭代
java·c++·ue5
向上的车轮1 小时前
基于深度学习与OpenCV的物体计数技术全解析
人工智能·深度学习·opencv
AA陈超1 小时前
Lyra学习5:GameFeatureAction分析
c++·笔记·学习·ue5·lyra
curry____3031 小时前
study in Dev-c++(string insert基本用法)(2025.12.2)
c++·string·insert
nono牛1 小时前
C++ 语言全面教程 (基础入门)
java·jvm·c++