使用 Android NDK 构建本机 OpenCV 会给出“对 'cv::String::deallocate()' 的未定义引用”

Posted

技术标签:

【中文标题】使用 Android NDK 构建本机 OpenCV 会给出“对 \'cv::String::deallocate()\' 的未定义引用”【英文标题】:Building native OpenCV using Android NDK gives "undefined reference to 'cv::String::deallocate()'"使用 Android NDK 构建本机 OpenCV 会给出“对 'cv::String::deallocate()' 的未定义引用” 【发布时间】:2016-07-21 16:06:50 【问题描述】:

我正在尝试在 android Studio 中将 OpenCV 与 NDK 一起使用。您可能会注意到我正在使用另一个名为 GStreamer 的本地库。

我的 build.gradle:

apply plugin: 'com.android.application'

def getNdkCommandLine(ndkRoot, target) 
    def gstRoot
    def opencvRoot
    gstRoot = 'C:/local/gstreamer-1.0-android-arm-1.8.0'
    opencvRoot = 'C:/local/OpenCV-3.1.0-android-sdk/OpenCV-android-sdk'

    if (ndkRoot == null)
        throw new GradleException('NDK not configured')

    return ["$ndkRoot/ndk-build.cmd",
            'NDK_PROJECT_PATH=build',
            'APP_BUILD_SCRIPT=src/main/jni/Android.mk',
            'NDK_APPLICATION_MK=src/main/jni/Application.mk',
            'GSTREAMER_JAVA_SRC_DIR=src/main/java',
            "GSTREAMER_ROOT_ANDROID=$gstRoot",
            "OPENCV_ROOT_ANDROID=$opencvRoot",
            "$target"]


android 
    compileSdkVersion 19
    buildToolsVersion "23.0.2"

    sourceSets 
        main 
            // Avoid using the built in JNI generation plugin
            jni.srcDirs = []
            jniLibs.srcDirs = ['build/libs']
        
    

    defaultConfig 
        applicationId "com.mytestcom.mytestapp"
        minSdkVersion 19
        targetSdkVersion 19
        compileOptions 
            sourceCompatibility JavaVersion.VERSION_1_7
            targetCompatibility JavaVersion.VERSION_1_7
        
    

    buildTypes 
        release 
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
            signingConfig signingConfigs.release
        
    

    // Before compiling our app, prepare NDK code
    tasks.withType(JavaCompile) 
        compileTask -> compileTask.dependsOn ndkBuild
    

    // Need to call clean on NDK ourselves too
    clean.dependsOn 'ndkClean'

    // Build native code using mk files like on Eclipse
    task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') 
        commandLine getNdkCommandLine(android.ndkDirectory, 'TARGET_ARCH_ABI=armeabi-v7a')
    

    task ndkClean(type: Exec, description: 'Clean JNI code built via NDK') 
        commandLine getNdkCommandLine(android.ndkDirectory, 'clean')
    

    //Renames APK to current versionName found in Android Manifest
    applicationVariants.all  variant ->
        variant.outputs.each  output ->
            def outputFile = output.outputFile
            if (outputFile != null && outputFile.name.endsWith('.apk')) 
                def fileName = "My_Test_App-$versionName.apk"
                output.outputFile = new File(outputFile.parent, fileName)
            
        
    


dependencies 
    compile 'com.android.support:support-v4:19.1.0'
    compile project(':openCVLibrary310')

Android.mk:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := gstplayer
LOCAL_SRC_FILES := player.cpp
LOCAL_C_INCLUDES := $(OPENCV_ROOT_ANDROID)/sdk/native/jni/include

LOCAL_SHARED_LIBRARIES := gstreamer_android
LOCAL_LDLIBS := -llog -landroid
include $(BUILD_SHARED_LIBRARY)

ifeq ($(TARGET_ARCH_ABI),armeabi)
GSTREAMER_ROOT        := $(GSTREAMER_ROOT_ARM)
else ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
GSTREAMER_ROOT        := $(GSTREAMER_ROOT_ARMV7)
else ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
GSTREAMER_ROOT        := $(GSTREAMER_ROOT_ARM64)
else ifeq ($(TARGET_ARCH_ABI),x86)
GSTREAMER_ROOT        := $(GSTREAMER_ROOT_X86)
else ifeq ($(TARGET_ARCH_ABI),x86_64)
GSTREAMER_ROOT        := $(GSTREAMER_ROOT_X86_64)
else
$(error Target arch ABI not supported)
endif

ifndef GSTREAMER_ROOT
ifndef GSTREAMER_ROOT_ANDROID
$(error GSTREAMER_ROOT_ANDROID is not defined!)
endif
GSTREAMER_ROOT        := $(GSTREAMER_ROOT_ANDROID)
endif

GSTREAMER_NDK_BUILD_PATH  := $(GSTREAMER_ROOT)/share/gst-android/ndk-build/

include $(GSTREAMER_NDK_BUILD_PATH)/plugins.mk
GSTREAMER_PLUGINS         := $(GSTREAMER_PLUGINS_CORE) $(GSTREAMER_PLUGINS_PLAYBACK) $(GSTREAMER_PLUGINS_CODECS) $(GSTREAMER_PLUGINS_NET) $(GSTREAMER_PLUGINS_SYS) $(GSTREAMER_PLUGINS_CODECS_RESTRICTED) $(GSTREAMER_CODECS_GPL) $(GSTREAMER_PLUGINS_ENCODING) $(GSTREAMER_PLUGINS_VIS) $(GSTREAMER_PLUGINS_EFFECTS) $(GSTREAMER_PLUGINS_NET_RESTRICTED)
GSTREAMER_EXTRA_DEPS      := gstreamer-player-1.0 gstreamer-video-1.0 glib-2.0

OPENCV_INSTALL_MODULES:=on
OPENCV_CAMERAMODULES:=off
OPENCV_LIB_TYPE:=STATIC

include $(GSTREAMER_NDK_BUILD_PATH)/gstreamer-1.0.mk
include $(OPENCV_ROOT_ANDROID)/sdk/native/jni/OpenCV.mk

Application.mk:

APP_PLATFORM=android-19
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_ABI := armeabi-v7a

我正在使用 OpenCV-3.1.0-android-sdk、NDK r10e、Android Studio 2.1、Windows。我尝试使用不同版本的 OpenCV (2.4.11),但没有帮助。

C:\local\OpenCV-3.1.0-android-sdk\OpenCV-android-sdk\sdk\native\jni\include\opencv2\core\cvstd.hpp
Error:(625) undefined reference to 'cv::String::allocate(unsigned int)'
Error:(667) undefined reference to 'cv::String::deallocate()'
Error:(667) undefined reference to 'cv::String::deallocate()'
Error:(667) undefined reference to 'cv::String::deallocate()'
Error:(667) undefined reference to 'cv::String::deallocate()'

C:\local\OpenCV-3.1.0-android-sdk\OpenCV-android-sdk\sdk\native\jni\include\opencv2\core\mat.inl.hpp
Error:(443) undefined reference to 'cv::error(int, cv::String const&, char const*, char const*, int)'
Error:(459) undefined reference to 'cv::error(int, cv::String const&, char const*, char const*, int)'
Error:(682) undefined reference to 'cv::Mat::deallocate()'
Error:(571) undefined reference to 'cv::fastFree(void*)'
Error:(592) undefined reference to 'cv::Mat::copySize(cv::Mat const&)'

【问题讨论】:

我认为问题在于正在使用的 .hpp 文件需要构建和链接到外部库。例如,libopencv_core.a。因此,在this 链接之后,我在我的Android.mk 文件中添加了一个部分,该部分应将模块添加为PREBUILT_STATIC_LIBRARY。我已经修复了此添加导致的所有错误,但我又回到了与以前相同的错误(未定义对...的引用) 这是肯定的。我没有看到您的应用程序链接的库的任何设置。 【参考方案1】:

几天前我遇到了类似的问题。 Imo,您应该尝试将LOCAL_LDLIBS := -llog -landroid 替换为LOCAL_LDLIBS += -llog -landroid

【讨论】:

【参考方案2】:

对这些错误的解释是 GStreamer 中使用的 OpenCV 代码无法访问库的其余部分,因此无法找到 allocate()deallocate() 等基本方法。

解决方案是在 Android.mk 中包含一些预构建的静态库,以便定义这些引用。所以在include $(OPENCV_ROOT_ANDROID)/sdk/native/jni/OpenCV.mk之后我添加了这些行来添加相关的库:

include $(CLEAR_VARS)
LOCAL_MODULE := opencv-tbb
LOCAL_SRC_FILES := opencvLib/3rdparty/libs/$(TARGET_ARCH_ABI)/libtbb.a
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := opencv-core
LOCAL_SRC_FILES := opencvLib/$(TARGET_ARCH_ABI)/libopencv_core.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/opencvLib/include/
LOCAL_STATIC_LIBRARIES := opencv-tbb
include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := opencv-imgproc
LOCAL_SRC_FILES := opencvLib/$(TARGET_ARCH_ABI)/libopencv_imgproc.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/opencvLib/include/
LOCAL_STATIC_LIBRARIES := opencv-core
include $(PREBUILT_STATIC_LIBRARY)

LOCAL_EXPORT_C_INCLUDESvariable 设置对应于该库的包含文件。在这种情况下,include 文件夹适用于两个库。

现在,为了让 GStreamer 代码能够使用这些库,我们必须将 LOCAL_STATIC_LIBRARIES := opencv-core opencv-imgproc 添加到引用两个添加的 OpenCV 模块的 gstplayer 模块中。

编辑:

另外我忘了提到我在 Application.mk 中将 APP_STL := gnustl_static 更改为 APP_STL := gnustl_shared

【讨论】:

OpenCV.mk中有两个函数:add_opencv_moduleadd_opencv_3rdparty_component。它们以时尚 opencv_* 生成LOCAL_MODULEs。例如 opencv_core、opencv_imgproce ...我很好奇为什么您没有收到模块名称冲突错误,然后我意识到您使用了“-”而不是“_”。因此,如果您尝试使用opencv_*,您最终可能会删除您定义的所有 3 个模块:D。

以上是关于使用 Android NDK 构建本机 OpenCV 会给出“对 'cv::String::deallocate()' 的未定义引用”的主要内容,如果未能解决你的问题,请参考以下文章

Android Studio外部本机构建预编译头

用于本机可执行文件的 Android NDK 分析器不生成数据

使用独立工具链 android arm 构建本机库

在本机代码中使用 opencv 进行 Android 应用程序开发

使用 NDK 构建音频处理 Little Endian SDK

Android NDK将参数传递给本机方法