基于OpenHarmony下的OpenCV库的交叉编译与实施

项目概要

开发环境
  • 平台:Windows 11、WSL Ubuntu 22.04

  • IDE :Deveco Studio 5.0.3.910/6.0 (6.0下没有API12的模拟器)

  • SDK:OpenHarmony 5.0.0 API12 FULL-SDK

  • 开发模板:Native C++

描述

​ 将OpenCV集成到开发环境,并使用C++实现调用摄像头并进行人脸检测(仅检测人脸位置)
​ 需支持路径加载、Base64格式加载等

交叉编译OpenCV

环境准备
  • 使用Ubuntu 22.04环境(或使用Windows下的WSL Ubuntu工具)

  •     # 更新并获取编译依赖
        sudo apt update
        sudo apt install build-essential cmake git pkg-config
        sudo apt install libgtk-3-dev libavcodec-dev libavformat-dev libswscale-dev
        sudo apt install python3-dev python3-numpy
    
  • # 获取 opencv源码 版本控制为 4.8.0
    git clone https://github.com/opencv/opencv.git
    cd opencv
    git checkout 4.8.0
    
  • # 获取 含有Linux 可用的native 工具的SDK 并解压
    wget https://repo.huaweicloud.com/openharmony/os/5.0.0-Release/ohos-sdk-windows_linux-public.tar.gz
    mkdir -p OpenHarmony5.0-Linux-SDK
    tar -xzf ohos-sdk-windows_linux-public.tar.gz -C OpenHarmony5.0-Linux-SDK --strip-components=1
    # 此压缩包为功能拆分压缩的分模块集合 需要二次解压 native
    cd ~/OpenHarmony5.0-Linux-SDK
    unzip native-linux-x64-5.0.0.71-Release.zip -d native-sdk
    
  • 现在的文件目录如下

    ~/
    ├── opencv/
    ├── OpenHarmony5.0-Linux-SDK
    │   └── native-sdk
    |        └──native
    |            ├── llvm/          ← Clang 工具链(Linux ELF)
    |           ├── sysroot/       ← 目标系统头文件和库
    |           └── build/         ← CMake 工具链文件所在(注意:在 native/ 内!)
    |            └──…………
    └── ...
    
  • # 1. 检查 clang 是否为 Linux ELF(非 .exe)
    file ~/OpenHarmony5.0-Linux-SDK/native-sdk/native/llvm/bin/clang
    
    # 应输出类似:
    # ...: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), ...
    
    # 2. 检查 sysroot
    ls ~/OpenHarmony5.0-Linux-SDK/native-sdk/native/sysroot/usr/include/stdio.h
    
    # 3. 检查 CMake 工具链文件
    ls ~/OpenHarmony5.0-Linux-SDK/native-sdk/native/build/cmake/ohos.toolchain.cmake
    
    # 如果都存在且 clang 是 ELF 文件,说明 交叉编译环境没有问题
    
  • 此时,所有的交叉编译工具已经准备完成。


开始编译
  • 因为默认编译会编译arm64 所以需要建立x86_64的Cmake配置

    nano ~/opencv/ohos-x86_64.toolchain.cmake
    
    # ohos-x86_64.toolchain.cmake
    set(CMAKE_SYSTEM_NAME OHOS)
    set(CMAKE_SYSTEM_PROCESSOR x86_64)
    
    # 指定 OpenHarmony SDK 路径
    set(OHOS_SDK_ROOT "$ENV{HOME}/OpenHarmony5.0-Linux-SDK/native-sdk/native")
    
    # 指定编译器
    set(CMAKE_C_COMPILER   ${OHOS_SDK_ROOT}/llvm/bin/x86_64-unknown-linux-ohos-clang)
    set(CMAKE_CXX_COMPILER ${OHOS_SDK_ROOT}/llvm/bin/x86_64-unknown-linux-ohos-clang++)
    
    # 指定 sysroot
    set(CMAKE_SYSROOT ${OHOS_SDK_ROOT}/sysroot)
    
    # 必要的编译标志
    set(CMAKE_C_FLAGS "--target=x86_64-unknown-linux-ohos --sysroot=${CMAKE_SYSROOT} -D__MUSL__" CACHE STRING "")
    set(CMAKE_CXX_FLAGS "--target=x86_64-unknown-linux-ohos --sysroot=${CMAKE_SYSROOT} -D__MUSL__" CACHE STRING "")
    
    # 链接器标志
    set(CMAKE_SHARED_LINKER_FLAGS "--rtlib=compiler-rt -fuse-ld=lld -Wl,--no-undefined" CACHE STRING "")
    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}" CACHE STRING "")
    
    # 禁用 rpath
    set(CMAKE_SKIP_RPATH TRUE)
    
  • 然后执行交叉编译 (x86_64)

    cd ~/opencv/build-x86
    rm -rf *
    # 功能根据需要开启 此处禁用了图像处理功能!!
    cmake \
      -DCMAKE_TOOLCHAIN_FILE=~/opencv/ohos-x86_64.toolchain.cmake \
      -DCMAKE_INSTALL_PREFIX=~/opencv-ohos-x86_64 \
      -DBUILD_SHARED_LIBS=ON \
      -DBUILD_opencv_apps=OFF \
      -DBUILD_TESTS=OFF \
      -DBUILD_PERF_TESTS=OFF \
      -DBUILD_EXAMPLES=OFF \
      -DWITH_OPENMP=OFF \
      -DWITH_IPP=OFF \
      -DWITH_TBB=OFF \
      -DWITH_EIGEN=OFF \
      -DWITH_V4L=OFF \
      -DWITH_GSTREAMER=OFF \
      -DWITH_FFMPEG=OFF \
      -DWITH_GTK=OFF \
      -DOPENCV_GENERATE_PKGCONFIG=OFF \
      -DWITH_JPEG=OFF \
      -DWITH_PNG=OFF \
      -DWITH_TIFF=OFF \
      -DWITH_WEBP=OFF \
      ..
    
    make -j$(nproc)
    make install
    
  • 编译完成后,需要查看生成的动态链接库依赖是否正确(否则会导致仅core导入时正常,导入其他时Napi构建失败)

    cd ~/opencv-ohos-x86_64/lib
    
    readelf -d libopencv_core.so | grep NEEDED # 依次查看所有so
    
    # 如果出现[../../lib/libopencv_*.so] 则说明依赖异常
    
  • 依赖异常时需要进行修复 使用以下代码

    # 列出所有 .so 文件
    for so in *.so; do
      echo "Fixing $so..."
      # 将 ../../lib/libxxx.so 替换为 libxxx.so
      patchelf --replace-needed ../../lib/libopencv_*.so libopencv_core.so "$so" 2>/dev/null || true
      # 可继续添加其他模块...
    done
    # 修复后重新验证 直到不再出现[../../lib/libopencv_*.so]类似情况
    
  • 此时,交叉编译已完成,结果保存至

    cd opencv-ohos-x86_64
    

补充

arm32 交叉编译说明

mkdir -p ~/opencv/build-arm32
cd ~/opencv/build-arm32
rm -rf *

# 创建arm32工具链文件
nano ~/opencv/ohos-arm32.toolchain.cmake

# ohos-arm32.toolchain.cmake
set(CMAKE_SYSTEM_NAME OHOS)
set(CMAKE_SYSTEM_PROCESSOR arm)

set(OHOS_SDK_ROOT "$ENV{HOME}/OpenHarmony5.0-Linux-SDK/native-sdk/native")

set(CMAKE_C_COMPILER   ${OHOS_SDK_ROOT}/llvm/bin/armv7-unknown-linux-ohos-clang)
set(CMAKE_CXX_COMPILER ${OHOS_SDK_ROOT}/llvm/bin/armv7-unknown-linux-ohos-clang++)

set(CMAKE_SYSROOT ${OHOS_SDK_ROOT}/sysroot)

set(CMAKE_C_FLAGS "--target=armv7-unknown-linux-ohos --sysroot=${CMAKE_SYSROOT} -D__MUSL__ -march=armv7-a -mfloat-abi=softfp -mfpu=neon" CACHE STRING "")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "")

set(CMAKE_SHARED_LINKER_FLAGS "--rtlib=compiler-rt -fuse-ld=lld -Wl,--no-undefined" CACHE STRING "")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}" CACHE STRING "")

set(CMAKE_SKIP_RPATH TRUE)

然后执行编译

# 功能根据需要开启 此处禁用了图像处理功能!!

cmake \
  -DCMAKE_TOOLCHAIN_FILE=~/opencv/ohos-arm32.toolchain.cmake \
  -DCMAKE_INSTALL_PREFIX=~/opencv-ohos-arm32 \
  -DBUILD_SHARED_LIBS=ON \
  -DBUILD_opencv_apps=OFF \
  -DBUILD_TESTS=OFF \
  -DBUILD_PERF_TESTS=OFF \
  -DBUILD_EXAMPLES=OFF \
  -DWITH_OPENMP=OFF \
  -DWITH_IPP=OFF \
  -DWITH_TBB=OFF \
  -DWITH_EIGEN=OFF \
  -DWITH_V4L=OFF \
  -DWITH_GSTREAMER=OFF \
  -DWITH_FFMPEG=OFF \
  -DWITH_GTK=OFF \
  -DOPENCV_GENERATE_PKGCONFIG=OFF \
  -DWITH_JPEG=OFF \
  -DWITH_PNG=OFF \
  -DWITH_TIFF=OFF \
  -DWITH_WEBP=OFF \
  ..

make -j$(nproc)
make install

arm64 交叉编译说明

cd ~/opencv/build
rm -rf *  # 清理中断产生的残余文件
# 功能根据需要开启 此处禁用了图像处理功能!!
cmake \
  -DCMAKE_TOOLCHAIN_FILE=~/OpenHarmony5.0-Linux-SDK/native-sdk/native/build/cmake/ohos.toolchain.cmake \
  -DPLATFORM=aarch64-linux-ohos \
  -DCMAKE_INSTALL_PREFIX=~/opencv-ohos-arm64 \
  -DBUILD_SHARED_LIBS=ON \
  -DBUILD_opencv_apps=OFF \
  -DBUILD_TESTS=OFF \
  -DBUILD_PERF_TESTS=OFF \
  -DBUILD_EXAMPLES=OFF \
  -DWITH_OPENMP=OFF \
  -DWITH_IPP=OFF \
  -DWITH_TBB=OFF \
  -DWITH_EIGEN=OFF \
  -DWITH_V4L=OFF \
  -DWITH_GSTREAMER=OFF \
  -DWITH_FFMPEG=OFF \
  -DWITH_GTK=OFF \
  -DOPENCV_GENERATE_PKGCONFIG=OFF \
  -DWITH_JPEG=OFF \
  -DWITH_PNG=OFF \
  -DWITH_TIFF=OFF \
  -DWITH_WEBP=OFF \
  ..

集成到开发环境

前提

目前已有文件夹 opencv-ohos-x86_64 结构如下

.
├── LICENSE
├── bin
│   └── setup_vars_opencv4.sh
├── include
│   └── opencv4
├── lib
│   ├── cmake
│   ├── libopencv_calib3d.so
│   ├── libopencv_core.so
│   ├── libopencv_dnn.so
│   ├── libopencv_features2d.so
│   ├── libopencv_flann.so
│   ├── libopencv_gapi.so
│   ├── libopencv_highgui.so
│   ├── libopencv_imgcodecs.so
│   ├── libopencv_imgproc.so
│   ├── libopencv_ml.so
│   ├── libopencv_objdetect.so
│   ├── libopencv_photo.so
│   ├── libopencv_stitching.so
│   ├── libopencv_video.so
│   └── libopencv_videoio.so
├── out.txt
└── share
    ├── licenses
    └── opencv4
开始集成
  • 在 windows 下 打开 Deveco Studio,新建Native C++ 项目 选择API 为12 在设置将 OpenHarmony SDK 设置为 OpenHarmony 5.0.0 API12 FULL-SDK (假设叫Demo)

  • 在 Demo/entry/src/main/cpp 下 新建 opencvLib/x86_64 文件夹 将 opencv-ohos-x86_64 中的lib和include文件夹复制进去 (其他架构同理)

  • 打开 Demo/entry/build-profile.json5 修改下面的片段

    {
      "apiType": "stageMode",
      "buildOption": {
        "externalNativeOptions": {
          "abiFilters": ["x86_64","arm64-v8a"], //添加这一行
          "path": "./src/main/cpp/CMakeLists.txt",
          "arguments": "",
          "cppFlags": "",
        }
      },
        ……
    }
    
  • 打开 Demo/entry/src/main/cpp/napi_init.cpp 上方引入

    #include <opencv2/opencv.hpp>
    
  • 打开 Demo/entry/src/main/cpp/CMakeLists.txt 将里面的内容替换为以下内容

    cmake_minimum_required(VERSION 3.5.0)
    project(ceui)
    
    if(DEFINED PACKAGE_FIND_FILE)
        include(${PACKAGE_FIND_FILE})
    endif()
    
    # ==============================
    # Step 1: Check OHOS_ARCH
    # ==============================
    if(NOT DEFINED OHOS_ARCH OR "${OHOS_ARCH}" STREQUAL "")
        message("OHOS_ARCH is not defined! Please specify with -DOHOS_ARCH=<arch> (e.g., x86_64, arm64-v8a)")
    else()
        message("Using architecture: ${OHOS_ARCH}")
    endif()
    
    # ==============================
    # Step 2: Set OpenCV paths (keep your structure)
    # ==============================
    set(OPENCV_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/libs/opencvLib/${OHOS_ARCH})
    message("Checking OpenCV root: ${OPENCV_ROOT}")
    
    if(NOT EXISTS ${OPENCV_ROOT})
        message("ERROR: OpenCV root directory does not exist: ${OPENCV_ROOT}")
    else()
        message("OpenCV root found: ${OPENCV_ROOT}")
    endif()
    
    set(OPENCV_INCLUDE_DIR ${OPENCV_ROOT}/include/opencv4)
    message("Checking OpenCV headers: ${OPENCV_INCLUDE_DIR}")
    
    if(NOT EXISTS ${OPENCV_INCLUDE_DIR})
        message("ERROR: OpenCV include directory missing: ${OPENCV_INCLUDE_DIR}")
    else()
        message("OpenCV headers found: ${OPENCV_INCLUDE_DIR}")
    endif()
    
    set(OPENCV_LIB_DIR ${OPENCV_ROOT}/lib)
    message("Checking OpenCV lib dir: ${OPENCV_LIB_DIR}")
    
    if(NOT EXISTS ${OPENCV_LIB_DIR})
        message("ERROR: OpenCV lib directory missing: ${OPENCV_LIB_DIR}")
    else()
        message("OpenCV lib dir found: ${OPENCV_LIB_DIR}")
    endif()
    
    # ==============================
    # Step 3: Base64 headers (optional)
    # ==============================
    set(Base64_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include/tool)
    if(EXISTS ${Base64_INCLUDE_DIR})
        message("Base64 headers: ${Base64_INCLUDE_DIR}")
    else()
        message("⚠ Base64 include dir not found (optional): ${Base64_INCLUDE_DIR}")
    endif()
    
    # ==============================
    # Step 4: Include directories
    # ==============================
    include_directories(${OPENCV_INCLUDE_DIR})
    include_directories(${Base64_INCLUDE_DIR})
    message("Header search paths configured")
    
    # ==============================
    # Step 5: Define OpenCV modules
    # ==============================
    set(OPENCV_MODULES
        opencv_core
        opencv_imgproc
        opencv_calib3d
        opencv_dnn
        opencv_features2d
        opencv_flann
        opencv_gapi
        opencv_highgui
        opencv_imgcodecs
        opencv_ml
        opencv_objdetect
        opencv_photo
        opencv_stitching
        opencv_video
        opencv_videoio
    )
    message("OpenCV modules to link: ${OPENCV_MODULES}")
    
    # ==============================
    # Step 6: Create main library
    # ==============================
    add_library(entry SHARED napi_init.cpp)
    message("Main library 'entry' created")
    
    # ==============================
    # Step 7: Prepare build output directory
    # ==============================
    message("CMAKE_LIBRARY_OUTPUT_DIRECTORY = ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
    set(BUILD_LIB_DIR ${CMAKE_LIBRARY_OUTPUT_DIRECTORY})
    file(MAKE_DIRECTORY ${BUILD_LIB_DIR})
    message("Build lib directory ensured: ${BUILD_LIB_DIR}")
    
    # ==============================
    # Step 8: Copy .so files with proper build rules
    # ==============================
    foreach(module IN LISTS OPENCV_MODULES)
        set(SRC_SO "${OPENCV_LIB_DIR}/lib${module}.so")
        set(DST_SO "${BUILD_LIB_DIR}/lib${module}.so")
    
        message("Processing module: ${module}")
        message("  Source: ${SRC_SO}")
        message("  Destination: ${DST_SO}")
    
        if(NOT EXISTS ${SRC_SO})
            message("  ERROR: Missing source .so file!")
        else()
            message("  Source .so exists")
        endif()
    
        # 创建复制规则(Ninja 可以调度)
        add_custom_command(
            OUTPUT ${DST_SO}
            COMMAND ${CMAKE_COMMAND} -E copy_if_different "${SRC_SO}" "${DST_SO}"
            DEPENDS "${SRC_SO}"
            COMMENT "Copying ${module} to build directory"
            VERBATIM
        )
    
        # 创建一个自定义目标来触发复制
        add_custom_target(copy_${module}_so ALL DEPENDS ${DST_SO})
    
        # 创建 IMPORTED 库(用于链接)
        add_library(${module} SHARED IMPORTED GLOBAL)
        set_target_properties(${module} PROPERTIES
            IMPORTED_LOCATION "${DST_SO}"
            IMPORTED_NO_SONAME TRUE
        )
    
        # 确保在构建 entry 前完成复制
        add_dependencies(entry copy_${module}_so)
    endforeach()
    
    # ==============================
    # Step 9: Link dependencies
    # ==============================
    target_link_libraries(entry
        PUBLIC
            ace_napi.z
            uv
            pthread
            z
            m
            ${OPENCV_MODULES}
    )
    message("Linking completed for 'entry' with all OpenCV modules")
    
    # ==============================
    # Final message
    # ==============================
    message("Successfully configured OpenCV integration for entry library")
    
  • 然后清理项目 注意删除.cxx文件夹 重新构建即可启动 此时,已可以在napi_init.cpp使用OpenCV库进行代码编辑。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐