对于熟悉c++的人来说,如果能在android中写c++代码会是比较方便的,这时得用Cmake,配置并不复杂,甚至比ndk-build要简单,所以这一篇,就写用cmake导入opencv并在里面写一个例子的过程。我也是一边学一边摸索,一边踩坑一边记录经验,同时和大家分享交流一下。
我的环境工具版本:
Android 3.0.1
OpenCV 3.4.1
创建工程
首先,创建项目工程的时候,勾选include c++ support
一路next,到最后一步,如下:
如果没有装CMake,建好以后会报错,那么安装Cmake
安装CMake
点击File—Settings—Appearance & Behavior – System Settings – Android SDK – SDK Tools,会看到Cmake选项,后面是Not Installed,说明没有安装CMake。勾选CMake,点击OK,联网情况下将会自动完成安装。
将OpenCV下面的java文件夹导入项目中,File – New – Import Module 选择OpenCV-android-sdk\sdk\java导入,然后分别修改app和opencv的gradle中的版本号(详见前一篇)例如我的手机是android7.1.2的,所以SDK版本号全部设为了25。
在主模块中加入对OpenCV Library的依赖,File – Project structure –app –点右边“+”号,choose Module,选择opencvLibary341导入。
获得NDK/native的OpenCV支持。
具体来说:
把路径OpenCV-android-sdk\sdk\native\jni\include这个include文件夹整个拷到路径\app\src\main\cpp目录下
把路径为OpenCV-android-sdk\sdk\native\libs里面的几个文件夹,拷贝到\src\main\jniLibs下面。
检查App的gradle文件
android {
compileSdkVersion 25 defaultConfig { applicationId "scr.face_detection4" minSdkVersion 25 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" externalNativeBuild {
cmake {
cppFlags "-frtti -fexceptions" }
}
ndk{
abiFilters 'armeabi-v7a' } sourceSets.main{ jniLibs.srcDir 'src/main/jniLibs' // set .so files directory to libs jni.srcDirs = [] //disable automatic ndk-build call }
}
buildTypes {
release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' }
}
externalNativeBuild {
cmake { path "CMakeLists.txt" }
}
}
- 修改CmakeLists.txt
我的代码如下:
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
set(OpenCV_DIR D:/AndroidSDK/OpenCV-android-sdk/sdk/native/jni)
find_package(OpenCV REQUIRED)
if(OpenCV_FOUND)
include_directories(${OpenCV_INCLUDE_DIRS})
message(STATUS "OpenCV library status:")
message(STATUS " version: ${OpenCV_VERSION}")
message(STATUS " libraries: ${OpenCV_LIBS}")
message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")
else(OpenCV_FOUND)
message(FATAL_ERROR "OpenCV library not found")
endif(OpenCV_FOUND)
set(CMAKE_VERBOSE_MAKEFILE on)
set(libs "${CMAKE_SOURCE_DIR}/src/main/jniLibs")
include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)
add_library(libopencv_java3 SHARED IMPORTED )
set_target_properties(libopencv_java3 PROPERTIES
IMPORTED_LOCATION "${libs}/${ANDROID_ABI}/libopencv_java3.so")
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
${log-lib}
${OpenCV_LIBS}
-ljnigraphics
libopencv_java3
)
去除OpenCV Manager 依赖
检查\src\main\jniLibs下面对应的发布平台的文件夹里是否有libopencv_java3.so文件,没有的话去OpenCV-android-sdk\sdk\native\libs里面拷一个
到MainActivity.java里面onCreate()函数前面加一句话
static {
System.loadLibrary("opencv_java3");
System.loadLibrary("native-lib");
}
写个例子测试一下
调用相机,要修改几个文件
- AndroidManifest.xml
在最外层里面加入:
- 编写native-lib.cpp
写了个简单测试,就做边缘Canny检测吧
jstring Java_scr_face_1detection4_MainActivity_stringFromJNI( JNIEnv *env, jobject /* this */)
{
std::string hello = "Hello from C++ Again";
return env->NewStringUTF(hello.c_str());
}
void Java_scr_face_1detection4_MainActivity_nativeProcessFrame(JNIEnv *, jobject, jlong addrGray, jlong addrRgba)
{
Mat &mGr = *(Mat *) addrGray;
Mat &mRgb = *(Mat *) addrRgba;
Canny(mGr,mRgb,50,200);
}
- 回到MainActivity.java,调用native-lib
public class MainActivity extends AppCompatActivity implements CvCameraViewListener2{
private static final String TAG = "MainActivity";
private Mat mRgba;
private Mat mIntermediateMat;
private Mat mGray;
private CameraBridgeViewBase mOpenCvCameraView;
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("opencv_java3");
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// Example of a call to a native method
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
//add
mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.tutorial2_activity_surface_view);
mOpenCvCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
mOpenCvCameraView.setCvCameraViewListener(this);
mOpenCvCameraView.setClickable(true);
}
@Override
public void onPause()
{
super.onPause();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}
@Override
public void onResume()
{
super.onResume();
if (!OpenCVLoader.initDebug()) {
Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
} else {
Log.d(TAG, "OpenCV library found inside package. Using it!");
mOpenCvCameraView.enableView();
}
}
public void onDestroy() {
super.onDestroy();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
}
public void onCameraViewStarted(int width, int height) {
mRgba = new Mat(height, width, CvType.CV_8UC4);
mIntermediateMat = new Mat(height, width, CvType.CV_8UC4);
mGray = new Mat(height, width, CvType.CV_8UC1);
}
public void onCameraViewStopped() {
mRgba.release();
mGray.release();
mIntermediateMat.release();
}
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
mRgba = inputFrame.rgba();
mGray = inputFrame.gray();
nativeProcessFrame(mGray.getNativeObjAddr(), mRgba.getNativeObjAddr());
return mRgba;
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
public native void nativeProcessFrame(long matAddrGr, long matAddrRgba);
}
运行就可以了。这样就是通过CMake调用cpp里面的OpenCV代码了。
调试中出现过的问题
- Execution failed for task ':app:externalNativeBuildDebug'.
这个问题是上文中CmakeLists.txt中的set后的路径没写对造成的,仔细检查修改后就过了。
- This adb server's $ADB_VENDOR_KEYS is not set
真机调试的时候遇到过这个问题,因为自己对AS调试还不太了解,连接好了就解决了。连接分两部分,一方面是AS里面的设置,按下图调好
另一部分是手机的设置。
根据自己的手机设置,打开“开发者选项”设置“USB调试”,允许“USB”安装。就可以调试了。
- Unknown failure (at android.os.Binder.execTransact(Binder.java:565))Error while Installing APKs
这问题开始也是不明白,上网查了一些方案以后,发现一种有效的方法,即——关闭MIUI优化
- no implementation found for nativeProcessFrame:
这个是cpp文件中的写法有问题,后来我去掉了JNICALL JNIEXPORT就好了,出现了红绿小箭头,看起来建立了调用关联。
参考:
https://blog.csdn.net/sw5131899/article/details/77197133
https://blog.csdn.net/lplj717/article/details/77991365