Android_高级进阶

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

2019-10-25  本文已影响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))
上一篇下一篇

猜你喜欢

热点阅读