前提是你已经安装了安卓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
#include
#include
#include
#include
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))