美文网首页Android_高级进阶
1.通过opencv源码编译Android上可以使用的库

1.通过opencv源码编译Android上可以使用的库

作者: 写代码的向日葵 | 来源:发表于2019-10-25 12:59 被阅读0次

    前提是你已经安装了安卓ndk和cmake

    1. 下载opencv源码

    git clone https://github.com/opencv/opencv.git opencv

    2. 您应该创建一个输出目录(在上面opencv的源码目录),因为OpenCV不允许进行源内构建

    mkdir android_build
    cd android_build
    

    3.要使用cmake创建必要的构建结构,您需要在ANDROID_NDK变量中提供NDK的路径。也许这个问题已经得到解决,但是OpenCV使用的.cmake文件在Android上无法正常工作。幸运的是,NDK提供了位于build / cmake中某个位置的android.toolchain.cmake文件。我通过定义-DCMAKE_TOOLCHAIN_FILE变量来强制cmake使用此文件。

    其他一些调整是我禁用了Java东西的构建,并且还指定了我要使用的STL运行时(尽管可能默认情况下已经设置了相同的运行时)。

    我还指定了安装前缀。这是所有输出文件(包括.so库和头文件)将被复制到的目录。

    您应该指定的另一个变量是ANDROID_ABI,否则默认情况下会选择不推荐使用的armeabi。对于其他项目,通常将使用CMAKE_ANDROID_ARCH_ABI,但是在使用NDK提供的.cmake文件时,似乎会忽略此标志。

    您的cmake shell命令应如下所示

    cmake .. -DCMAKE_TOOLCHAIN_FILE=/path/to/ndk/build/cmake/android.toolchain.cmake -DANDROID_NDK=/path/to/ndk -DANDROID_NATIVE_API_LEVEL=android-21 -DBUILD_JAVA=OFF -DBUILD_ANDROID_EXAMPLES=OFF -DBUILD_ANDROID_PROJECTS=OFF -DANDROID_STL=c++_shared -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX:PATH=/absolute/path/to/opencv/android_build/out -DANDROID_ABI=arm64-v8a
    

    请注意,我首先将当前工作目录更改为在上一步中创建的目录(android_build /),然后执行了shell命令。

    在某些情况下,您只需要某些模块特定功能的一小部分。为了减少构建时间和最终大小,OpenCV的构建系统允许您在构建过程中禁用某些模块和功能。

    cmake .. \
    -DBUILD_opencv_ittnotify=OFF -DBUILD_ITT=OFF -DCV_DISABLE_OPTIMIZATION=ON -DWITH_CUDA=OFF -DWITH_OPENCL=OFF -DWITH_OPENCLAMDFFT=OFF -DWITH_OPENCLAMDBLAS=OFF -DWITH_VA_INTEL=OFF -DCPU_BASELINE_DISABLE=ON -DENABLE_SSE=OFF -DENABLE_SSE2=OFF -DBUILD_TESTING=OFF -DBUILD_PERF_TESTS=OFF -DBUILD_TESTS=OFF -DCMAKE_BUILD_TYPE=RELEASE -DBUILD_EXAMPLES=OFF -DBUILD_DOCS=OFF -DBUILD_opencv_apps=OFF -DBUILD_SHARED_LIBS=OFF -DOpenCV_STATIC=ON -DWITH_1394=OFF -DWITH_ARITH_DEC=OFF -DWITH_ARITH_ENC=OFF -DWITH_CUBLAS=OFF -DWITH_CUFFT=OFF -DWITH_FFMPEG=OFF -DWITH_GDAL=OFF -DWITH_GSTREAMER=OFF -DWITH_GTK=OFF -DWITH_HALIDE=OFF -DWITH_JASPER=OFF -DWITH_NVCUVID=OFF -DWITH_OPENEXR=OFF -DWITH_PROTOBUF=OFF -DWITH_PTHREADS_PF=OFF -DWITH_QUIRC=OFF -DWITH_V4L=OFF -DWITH_WEBP=OFF \
    -DBUILD_LIST=core,features2d,flann,imgcodecs,imgproc,stitching \
    -DANDROID_NDK=/path/to/ndk -DCMAKE_TOOLCHAIN_FILE=/path/to/ndk/build/cmake/android.toolchain.cmake -DANDROID_NATIVE_API_LEVEL=android-21 -DBUILD_JAVA=OFF -DBUILD_ANDROID_EXAMPLES=OFF -DBUILD_ANDROID_PROJECTS=OFF -DANDROID_STL=c++_shared -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX:PATH=/absolute/path/to/opencv/android_build/out -DANDROID_ABI=arm64-v8a
    

    这是在为Pano Stitch&Crop App编译OpenCV时禁用某些模块和功能的方式。请注意我如何禁用某些功能,然后使用它来指定所需的模块。OFF``-DBUILD_LIST

    您可能需要针对特定​​情况更改一些其他选项。您可以使用以下命令来检查哪些变量可用

    cmake -LA
    

    如果出现一些错误,并且您更改了一些选项来修复这些错误,则可能还需要先删除CMakeCache.txt文件,然后才能成功运行cmake。

    最后建立项目执行

    make
    make install
    

    我们将在Android应用程序中使用的.so库应位于out / sdk / native / libs /目录内。
    CMake使得交叉编译Android项目变得非常容易。您可以使用上面显示的相同方法来交叉编译另一个相关的库dlib
    4.准备要构建的Android Studio项目

    在这里,我假设您已经创建了一个新的启用了C ++支持的Android Studio项目。
    为了正确地将我们的本机代码与OpenCV库链接,您应该在应用程序模块文件夹中更改CMakeLists.txt文件

    # Configure path to include directories
    include_directories(SYSTEM $ENV{VENDOR}/opencv/include )
    
    # Set up OpenCV shared .so library so that it can
    # be linked to your app
    add_library( cv_core-lib SHARED IMPORTED)
    set_target_properties( cv_core-lib
                          PROPERTIES IMPORTED_LOCATION
                          $ENV{VENDOR}/opencv/lib/${ANDROID_ABI}/libopencv_core.so )
    
    add_library( cv_imgproc-lib SHARED IMPORTED)
    set_target_properties( cv_imgproc-lib
            PROPERTIES IMPORTED_LOCATION
            $ENV{VENDOR}/opencv/lib/${ANDROID_ABI}/libopencv_imgproc.so )
    
    add_library( cv_imgcodecs-lib SHARED IMPORTED)
    set_target_properties( cv_imgcodecs-lib
            PROPERTIES IMPORTED_LOCATION
            $ENV{VENDOR}/opencv/lib/${ANDROID_ABI}/libopencv_imgcodecs.so )
    
    ...
    
    # jnigraphics lib from NDK is used for Bitmap manipulation in native code
    find_library( jnigraphics-lib jnigraphics )
    
    # Link to your native app library
    target_link_libraries( my_native-lib ${jnigraphics-lib} cv_core-lib cv_imgproc-lib cv_imgcodecs-lib other-libs...)
    

    在这里,我链接了core,imgproc和imgcodecs模块。您可能需要根据使用的功能添加其他OpenCV库。

    我决定在这种情况下使用共享库,但是静态链接也应该起作用。

    我还链接了NDK的jnigraphics库。在我的示例代码中,我使用一些位图操作方法,因此这是必需的。

    接下来,您应该更改模块级别的build.gradle文件

    android {
        ...
        defaultConfig {
            ...
            sourceSets {
                main {
                    jniLibs.srcDirs = [
                            System.getenv('VENDOR') + '/opencv/lib'
                    ]
                }
            }
    

    这将确保gradle将lib文件夹中的OpenCV .so库打包到最终的APK中。该LIB /文件夹应包含每个支持的体系结构,例如,armeabi,armeabi-V7A,...我通常至少包括子文件夹armeabi-V7A和86的最终APK进行发布,但测试你可以只包括ABI支持由您测试设备

    此外,在模块级别的build.gradle文件中,我通常还会指定要为其构建的ABI,要使用的C ++运行时,C ++异常支持以及其他一些东西
    在以下代码中,我将对来自克尔克岛的一张度假照片使用Canny边缘检测。首先,我将JPEG图像加载到Kotlin的位图中,然后使用本机代码中的位图像素来执行Canny边缘检测。

    考虑到您可以使用OpenCV进行的所有操作,此示例似乎有些la脚,但是我试图将其简化。如果您对Android上的某些低延迟/实时Camera图像处理感兴趣。

    始终可以直接使用本机C ++代码打开图像文件。但是,Android的Java API 在许多其他图像处理和图形相关功能中使用Bitmap类。因此,我认为在本地代码中显示如何访问Bitmap的像素缓冲区而不进行不必要的复制会很有用。

    始终可以直接使用本机C ++代码打开图像文件。但是,Android的Java API 在许多其他图像处理和图形相关功能中使用Bitmap类。因此,我认为在本地代码中显示如何访问Bitmap的像素缓冲区而不进行不必要的复制会很有用。

    首先,我们创建一个本机C ++函数,该函数将通过JNI从我们的Kotlin类中调用。该函数接受一个输入bitmap,该输入将用于检测精明边缘。该功能会将检测到的边缘写入给定destination文件。

    #include <jni.h>
    #include <string>
    
    #include <opencv2/opencv.hpp>
    #include <opencv2/imgproc/imgproc.hpp>
    #include <android/bitmap.h>
    
    using namespace cv;
    
    extern "C" {
    JNIEXPORT void JNICALL
    Java_eu_sisik_opencvsample_MainActivity_canny(
            JNIEnv *env,
            jobject /* this */,
            jobject bitmap,
            jstring destination) {
    
        // Get information about format and size
        AndroidBitmapInfo info;
        AndroidBitmap_getInfo(env, bitmap, &info);
    
        // Get pointer to pixel buffer
        void *pixels = 0;
        AndroidBitmap_lockPixels(env, bitmap, &pixels);
    
        // I create separate scope for input Mat here
        // to make sure it is destroyed before unlocking
        // pixels
        {
            // Check the format info before you pick the CV_ type
            // for OpenCV Mat
            // info.format == ANDROID_BITMAP_FORMAT_RGBA_8888 -> CV_8UC4
    
            // Now create the mat
            Mat input(info.height, info.width, CV_8UC4, pixels);
    
            // Perform canny edge detection
            Mat edges;
            Canny(input, edges, 200.0, 600.0, 600.0);
    
            // Save to destination
            const char *dest = env->GetStringUTFChars(destination, 0);
            imwrite(dest, edges);
            env->ReleaseStringUTFChars(destination, dest);
        }
    
        // Release the Bitmap buffer once we have it inside our Mat
        AndroidBitmap_unlockPixels(env, bitmap);
    }
    
    }
    

    在上面的代码中,我从位图像素缓冲区构造了一个OpenCV Mat。我为inputMat对象创建了一个单独的作用域,以确保它在调用之前被破坏AndroidBitmap_unlockPixels()。

    在Kotlin代码中,您应确保加载了本地.so库System.loadLibrary()。本机方法用external关键字声明

    class MainActivity : AppCompatActivity() {
    ...
        external fun canny(src: Bitmap?, destinationPath: String): Void
    
        companion object {
    
            init {
                System.loadLibrary("native-lib")
            }
        }
    ...
    

    要使用Kotlin的本机C ++函数

    var bitmap: Bitmap? = null
    assets.open("pinezici_krk_island.JPG").use {
        bitmap = BitmapFactory.decodeStream(it)
    }
    
    // Store result inside of app's cache folder
    var dest = cacheDir.absolutePath + "/canny.JPG"
    
    // Pass the bitmap to native C++ code and perform canny edge detection
    canny(bitmap, dest)
    
    // Show the processed image
    ivPhoto.setImageBitmap(BitmapFactory.decodeFile(dest))
    

    相关文章

      网友评论

        本文标题:1.通过opencv源码编译Android上可以使用的库

        本文链接:https://www.haomeiwen.com/subject/csjsvctx.html