javandk编译opencv、opencv_contrib编译和使⽤相机Aruco姿态估计
OpenCV3.1时代开始,Android平台就已经有官⽅提供的OpenCV库了,理论上我们是不需要再⾃⾏编译的。⽽且OpenCV的官⽅建议也是直接使⽤OpenCV4Android库(也就是预编译的libopencv_java3.so),并提供了两套使⽤⽅法:
利⽤OpenCV提供的全套Java接⼝, 在Android Java层调⽤。
利⽤OpenCV提供的C/C++ 接⼝, 在JNI层使⽤(就跟在PC端VC++下使⽤OpenCV⼀样⼀样的)。
是由于在实际的应⽤中难免会遇到⼀些问题,⽐如在Android⼯程中如果要同时使⽤SNPE(⼀个⾼性能神经⽹络加速库)和OpenCV时,由于SNPE使⽤的STL链接的是libc++,⽽OpenCV默认使⽤的是gnu_stl,所以会导致gradle不管怎么配置都⽆法正常编译过的情况。
这种情况下如果gradle中选择arguments '-DANDROID_STL=c++_shared’的话SNPE可以正常编译,但是在使⽤像imwrite这样的OpenCV函数时就会报链接错误。相反如果gradle中选择arguments '-ANDROID_STL=gnu_stl’则SNPE⽆法编译通过。
另外⼀⽅⾯,官⽅预编译好的OpenCV4Android库是不带contrib模块的,所以⽆法使⽤像是xfeatures2d、ArUco这样的库。
这⾥在windows下使⽤Andorid NDK编译,后期可以直接在Android 中使⽤或者部署。
⽂章⽬录
1、源码编译
(1)windows开发环境
win 10操作系统,CMake 3.21,Android Studio 2020.3的有关sdk信息如下截图
(2)下载源码
两个包解压到如D:\opencv\opencv4.5.4下
(3)编译
可以在任意位置执⾏编译脚本,这⾥选择D:\opencv\opencv4.5.4⽬录,执⾏脚本为
python ./sources/platforms/android/build_sdk.py \
--extra_modules_path=D:/opencv/opencv4.5.4/opencv_contrib-4.5.4/modules/ \
--config ./sources/platforms/fig.py
当提⽰ work_dir、opencv_dir或者ndk_path、sdk_path错误,需要再添加对应⽬录,可以直接查看 build_sdk.py的解释,例如添加ndk 或者sdk⽬录 --sdk_path=E:/AndroidProjects/SDK。
默认编译 arm64-v8a、 armeabi-v7a、x86、x86_64四个版本。根据需要修改fig.py脚本即可,不做说明。
编译成功将在指定⽬录下⽣成OpenCV-android-sdk⽂件夹,内部⽂件夹为
2、Aruco的jni代码项⽬
参考前⾯的博⽂,仅使⽤opencv4java module,或者仅使⽤opencv native jni,或者两者都使⽤。这⾥演⽰Aruco姿态估计使⽤。
导⼊opencv module有多种⽅式,可以参考OpenCV-android-sdk下adle的注释说明部分。
2.1、使⽤opencv的CameraActivity获取相机画⾯
为使⽤⽅便,直观看到检测效果,在项⽬中导⼊opencv module,使⽤CameraActivity实现相机画⾯的
预览、能够实时通过回调获取相机画⾯的rgba格式的Mat对象。
注意给予app相机权限,这⾥的代码可以参考opencv sdk下的sample项⽬。
public class MainActivity extends CameraActivity implements CvCameraViewListener2 {
private static final String TAG ="cvCameraJni";
private CameraBridgeViewBase mOpenCvCameraView;
private BaseLoaderCallback mLoaderCallback =new BaseLoaderCallback(this){
@Override
public void onManagerConnected(int status){
switch(status){
case LoaderCallbackInterface.SUCCESS:{
Log.i(TAG,"OpenCV loaded successfully");
}break;
default:{
}break;
}
}
};
public MainActivity(){
public MainActivity(){
Log.i(TAG,"Instantiated new "+Class());
}
@Override
protected void onCreate(Bundle savedInstanceState){
setContentView(R.layout.activity_main);
mOpenCvCameraView =(CameraBridgeViewBase)findViewById(R.id.javaCameraView);
mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
mOpenCvCameraView.setCvCameraViewListener(this);
}
@Override
public void onPause()
{
if(mOpenCvCameraView !=null)
mOpenCvCameraView.disableView();
}
@Override
public void onResume()
{
if(!OpenCVLoader.initDebug()){
Log.d(TAG,"Internal OpenCV library not found. Using OpenCV Manager for initialization");
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0,this, mLoaderCallback);
}else{
Log.d(TAG,"OpenCV library found inside package. Using it!");
}
}
@Override
protected List<?extends CameraBridgeViewBase>getCameraViewList(){
return Collections.singletonList(mOpenCvCameraView);
}
public void onDestroy(){
if(mOpenCvCameraView !=null)
mOpenCvCameraView.disableView();
}
public void onCameraViewStarted(int width,int height){
Log.d(TAG,"start size "+ width +"x"+ height);
}
public void onCameraViewStopped(){
}
public Mat onCameraFrame(CvCameraViewFrame inputFrame){
ba();// 必须返回rgba格式, CvCameraViewFrame还封装了对应的gray格式
}
}
后⾯是应⽤,我们可以在 onCameraFrame() 回调函数中对实时画⾯进⾏操作、并将处理后的画⾯返回预览显⽰的app中。
2.2、实现jni部分代码
2.2.1、Java中封装类CvAurcoWrapper
为了在Java中简单使⽤,封装⼀个CvAurcoWrapper.java类,并定义需要的native函数,如下public class CvAurcoWrapper {
static{
System.loadLibrary("cvcamerajni");
}
public CvAurcoWrapper(){}
public boolean init(String detectorParamsFile, String cameraParamsFile){
mNativeObj =nativeCreateAurco(detectorParamsFile, cameraParamsFile);
return mNativeObj !=0;
}
public boolean detect(Mat imageGray, Mat pos){
return nativeDetect(mNativeObj, NativeObjAddr(), NativeObjAddr());
}
public void release(){
nativeDestroyAurco(mNativeObj);
mNativeObj =0;
}
private long mNativeObj =0;// ⽤来保存c++下对象的指针
// 初始化、返回c++对象指针
private static native long nativeCreateAurco(String detectorParamsFile, String cameraParamsFile);
// 通过传递c++对象指针进⾏资源释放
private static native void nativeDestroyAurco(long thiz);
python转java代码// 通过传递c++对象指针,以及图像和接收结果的指针,进⾏Aruco姿态估计
private static native boolean nativeDetect(long thiz,long inputImage,long pos);
}
2.2.1、jni代码native函数的实现
在实现上⾯三个native函数前,先说明c++下的Aruco模块使⽤类封装,给出公有函数申明如下
/// #include "DetectMarkersApi.h"
#pragma once
#include"opencv2\aruco.hpp"
class DetectMarkersApi
{
public:
DetectMarkersApi();
~DetectMarkersApi();
/**
* 初始化检测参数、相机参数
* @param detectorParamsFile l⽂件⽬录
* @param cameraParamsFile l⽂件⽬录
*/
bool init(const std::string detectorParamsFile,const std::string cameraParamsFile);
/**
* 检测、姿态估计
* @param src 待处理图像
* @param tvec 姿态计算结果,单位⽶
* @param rvec 旋转向量
* @return 是否检测到有效 Marker
*/
bool getPosition(const cv::Mat &src, cv::Vec3d &tvec, cv::Vec3d &rvec);
// draw results
void draw(cv::Mat &src, cv::Vec3d tvec, cv::Vec3d rvec);
}
native的jni部分的函数名必须为 “包名_函数名”,参数为对应jni类型;由于native使⽤c语⾔分离实现的⽅式,将⼀个类的成员函数功能调⽤全部通过对象指针⽅式实现。
注意以下代码使⽤了异常,需要在app的adle的android.defaultConfig下添加⽀持
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -frtti -fexceptions"
}
}
jni代码如下所⽰:
#include"CvAurcoWrapper_jni.h"
#include"DetectMarkersApi.h"
#include<string>
#include<vector>
#include<android/log.h>
#include<opencv2/imgproc.hpp>
#define LOG_TAG "CvAurcoWrapper_nativeCreateAurco"
#define LOGD(...)((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#define LOGE(...)((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
JNIEXPORT jlong JNICALL Java_com_example_cvcamerajni_CvAurcoWrapper_nativeCreateAurco
(JNIEnv *env, jclass clazz, jstring detector_params_file, jstring camera_params_file)
{
LOGD("CvAurcoWrapper_nativeCreateAurco enter");
const char*jdetctcorstr = env->GetStringUTFChars(detector_params_file,NULL);
const char*jcamerastr = env->GetStringUTFChars(camera_params_file,NULL);
std::string detectorParamsFile(jdetctcorstr);
std::string cameraParamsFile(jcamerastr);
jlong result =0;
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论