Android Hal层接入Opencv(踩坑篇)

文章目录[x]
  1. 1:尝试接入Opencv Android SDK
  2. 2:寻找问题
  3. 3:解决方法

为了在AOSPHal层相机添加图片处理的一些功能。我尝试接入OpenCV 4.5.1来实现该功能,本文将介绍接入过程中所遇到的坑和对应的解决方法。

尝试接入Opencv Android SDK

首先,我尝试接入4.5.1版本的Android SDK版本,需要将SDK包下的arm32位动态库和arm64位动态库添加到AOSP的模块中。首先在vendor目录下对应厂商的目录下创建libs目录,并在libs目录下定义libopencv_java4模块,具体目录结构如下:

- vendor
   - mediatek (联发科)
     - libs
       - libopencv_java4
         - Android.mk
         - arm32
           - libopencv_java4.so
         - arm64
           - libopencv_java4.so

Android.mk的定义如下:

LOCAL_PATH := $(my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE = libopencv_java4.so
LOCAL_MODULE_CLASS = SHARED_LIBRARIES
LOCAL_MODULE_OWNER = mtk
LOCAL_MODULE_SUFFIX = .so
LOCAL_MODULE_TAGS = optional
LOCAL_MULTILIB = 64
LOCAL_SRC_FILES_64 = arm64/$(LOCAL_MODULE).so
LOCAL_MODULE_PATH_64 = $(TARGET_OUT_VENDOR)/lib64
include $(BUILD_PREBUILT)

include $(CLEAR_VARS)
LOCAL_MODULE = libopencv_java4.so
LOCAL_MODULE_CLASS = SHARED_LIBRARIES
LOCAL_MODULE_OWNER = mtk
LOCAL_MODULE_SUFFIX = .so
LOCAL_MODULE_TAGS = optional
LOCAL_MULTILIB = 32
LOCAL_SRC_FILES_32 = arm32/$(LOCAL_MODULE).so
LOCAL_MODULE_PATH_32 = $(TARGET_OUT_VENDOR)/lib
include $(BUILD_PREBUILT)

然后在对应厂商的device.mk下添加如下代码来将该模块包含到产品里:

#在打包vendor.img前将32位动态库和64位动态库拷贝到 vendor/lib目录和vendor/lib64目录下
PRODUCT_COPY_FILES += vendor/mediatek/libs/libopencv_java4/arm32/libopencv_java4.so:$(TARGET_COPY_OUT_VENDOR)/lib/libopencv_java4.so:mtk
PRODUCT_COPY_FILES += vendor/mediatek/libs/libopencv_java4/arm64/libopencv_java4.so:$(TARGET_COPY_OUT_VENDOR)/lib64/libopencv_java4.so:mtk
#把该模块添加到产品中
PRODUCT_PACKAGES += libopencv_java4

在需要使用的模块下,导入opencv的头文件和libopencv_java4动态库模块,然后在cpp文件中使用opencv的库函数:

#将头文件放入include文件夹下

LOCAL_C_INCLUDES += include

#添加动态库依赖

LOCAL_SHARED_LIBRARIES += libopencv_java4

当以为一切完美无缺并在mm编译该模块时,出现了如下错误信息:

undefined reference to 'cv::imwrite(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, cv::_InputArray const&, std::__1::vector<int, std::__1::allocator<int> > const&)'

寻找问题

在上面提到的编译问题中,可以看到在链接opencv的动态库时不能在动态库中寻找到指定的函数。可以知道,AOSP在编译时是用的是libc++的标准库,而libc++的标志库字符串的命名空间是std::__1::basic_string开头的,当用nm命令查看opencv库中记录的符号名称却如下:

nm -D libopencv_java4.so | c++filt | grep imwrite

00000000001f108c T Java_org_opencv_imgcodecs_Imgcodecs_imwrite_10
00000000001f12c0 T Java_org_opencv_imgcodecs_Imgcodecs_imwrite_11
00000000001f14ec T Java_org_opencv_imgcodecs_Imgcodecs_imwritemulti_10
00000000001f17bc T Java_org_opencv_imgcodecs_Imgcodecs_imwritemulti_11
000000000080429c T cv::imwrite(std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> > const&, cv::_InputArray const&, std::__ndk1::vector<int, std::__ndk1::allocator<int> > const&)

可以看到,opencv的动态库的符号却是std::__ndk1::basic_string开头,因为符号不一致,这肯定链接不上。既然opencvAOSP模块都是用的libc++去编译的,为什么两者都不一致呢。

google官网下载的ndk解压后(在AOSPprebuilts/ndk中也可以找到),在r13/sources/cxx-stl/llvm-libc++/include/__config下可以找到如下代码:

#define _LIBCPP_ABI_VERSION 1

...

#define _LIBCPP_NAMESPACE _LIBCPP_CONCAT(__ndk,_LIBCPP_ABI_VERSION)

标准的llvm-libc++ __config定义如下:

#define _LIBCPP_ABI_VERSION 1

...

#define _LIBCPP_NAMESPACE _LIBCPP_CONCAT(__,_LIBCPP_ABI_VERSION)

从上面的对比,可以看出,谷歌在ndk中集成libc++时对其部分代码进行了修改。而opencv交叉编译出的so库是用的ndk中的libc++,所以其命名空间是std::__ndk1::basic_string。

解决方法

既然找出了问题,就需要找出解决办法,我们可以在opencvrelease页面下下载对应版本的源码,然后编写Android.mk放到AOSP中和用到的模块一起进行编译。不了解opencv的源码?谷歌官方已经集成了opencv 3.00的版本,你只需要将其目录下的Android.mk中每个模块的LOCAL_NDK_STL_VARIANTLOCAL_SDK_VERSION注释掉(因为是c++编写,所以也需要把libopencv_java模块去掉),然后mm就可以用标准的llvm-libc++编译出动态库。然后将各个模块添加到device.mk中,然后在使用的模块中引入该模块就行了。

点赞
  1. andy说道:

    Hi 你编译有遇到这样的问题吗?
    error: vendor/nxp-opensource/imx/camera/Android.bp:107:1: dependency "libopencv_core" of "libimxcamerahwl_impl" missing variant:
    os:android, image:vendor.30, arch:arm64_armv8-a_cortex-a53, sdk:, link:shared, version:
    available variants:

    1. melrose说道:

      你需要添加opencv的动态库到你用到的模块中

      1. andy说道:

        嗯嗯 这个我加上了。
        但是还有另一个问题哎, 我的头文件应该怎么include呀?

      2. andy说道:

        应该说是 我的头文件应该指向什么位置的文件呢? 是inlcude opencv 的source code里面的文件路径吗

        1. melrose说道:

          你可以下载对应版本的opencv的android sdk,里面有所有的头文件。https://github.com/opencv/opencv/releases,sdk/native/jni/include下面的所有的头文件拷贝包含到你的模块中去就行了

        2. melrose说道:

          当然你也可以从他源码里面去一个一个找,比较麻烦

          1. andy说道:

            嗯嗯,我是这么做的 直接拷贝的prebuilt sdk的include

            不过我编译的android.bp对每个lib都加了
            cppflags: ["-fexceptions"],
            不知道为啥在编译调用opencv lib的地方编译的时候还是有下面的报错哎

            opencv/include/opencv2/flann/hierarchical_clustering_index.h:385:13: error: cannot use 'throw' with exceptions disabled
            FLANN_THROW(cv::Error::StsError, "Unknown algorithm for choosing initial centers.");
            ^
            opencv/include/opencv2/flann/general.h:53:32: note: expanded from macro 'FLANN_THROW'
            #define FLANN_THROW(TYPE, STR) throw FLANNException(STR)

          2. melrose说道:

            我是直接用的google aosp里面的3.0.0,把它克隆到external里面的,然后如果想用ndk里面的libc++编译可以不做任何修改,如果想用默认的libc++去编译需要将它目录下的Android.mk中每个模块的LOCAL_NDK_STL_VARIANT和LOCAL_SDK_VERSION注释掉,然后你可以进它的目录mm,可以在out目录下面的产品的system目录下面的lib和lib64目录找到生成的18个动态库,你把这些定义成预编译库(如果不是给java用就不要libopencv_java),在你用到的地方导入这些动态库和头文件,你用到的地方可能要加一些cppflag

    2. melrose说道:

      https://developer.android.com/ndk/guides/cpp-support Android.mk下添加c++的异常处理支持:
      LOCAL_CPPFLAGS += -fexceptions -frtti

    3. melrose说道:

      你用到opencv的模块中也要加

      1. andy说道:

        嗯, 谢谢! 加上之后我编译能过了。
        不过跑不起来。。(sad)
        我感觉还是因为rtti 的关系, 导致找不到vtab

        不知道老哥你那边都没这样的问题的吗?
        而且我试了下aosp opencv3 编不出来的额, 我的android版本是android11

        1. melrose说道:

          我编译出来的arm32和64位的动态库,你试试,里面的Android.mk可能要改改。链接: https://pan.baidu.com/s/11ecgINPIPDDgMmPKluJPlQ 密码: 0roh
          --来自百度网盘超级会员V2的分享

          1. max说道:

            大佬,opencv编写好的Android.mk能不能共享下,谢谢

  2. 027liuweiming说道:

    按照你的这个提示操作,方便贴一下的 android.mk 文件吗?

    我是直接用的google aosp里面的3.0.0,把它克隆到external里面的,然后如果想用ndk里面的libc++编译可以不做任何修改,如果想用默认的libc++去编译需要将它目录下的Android.mk中每个模块的LOCAL_NDK_STL_VARIANT和LOCAL_SDK_VERSION注释掉,然后你可以进它的目录mm,可以在out目录下面的产品的system目录下面的lib和lib64目录找到生成的18个动态库,你把这些定义成预编译库(如果不是给java用就不要libopencv_java),在你用到的地方导入这些动态库和头文件,你用到的地方可能要加一些cppflag

  3. 027liuweiming说道:

    已经搞定了,多谢大神

    LOCAL_PATH := $(call my-dir)
    FLOW := $(LOCAL_PATH)

    ############# compile opencv3.0.0 libs/*so ############
    include $(LOCAL_PATH)/libs/Android.mk

    ############# compile opticalflow.so ############
    LOCAL_PATH :=$(FLOW)
    include $(CLEAR_VARS)
    LOCAL_MODULE := libopencv_optical_flow
    LOCAL_MODULE_TAGS := optional
    LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/bin

    LOCAL_SRC_FILES := \
    optical_flow.cpp

    LOCAL_C_INCLUDES := \
    $(LOCAL_PATH)/include/

    LOCAL_SHARED_LIBRARIES := \
    libopencv_calib3d \
    libopencv_core \
    libopencv_features2d \
    libopencv_flann \
    libopencv_highgui \
    libopencv_imgcodecs \
    libopencv_imgproc \
    libopencv_ml \
    libopencv_objdetect \
    libopencv_photo \
    libopencv_rsobjdetect \
    libopencv_shape \
    libopencv_stitching \
    libopencv_superres \
    libopencv_video \
    libopencv_videoio \
    libopencv_videostab

    include $(BUILD_SHARED_LIBRARY)

发表评论

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像

Title - Artist
0:00