OpenCV中文网站

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
热搜: 安装 配置
查看: 2112|回复: 0

Opencv4Android分享:OpenCV与JNI/NDK

[复制链接]
发表于 2016-6-27 21:06:35 | 显示全部楼层 |阅读模式
本帖最后由 mumumusuc 于 2016-6-27 23:37 编辑

    上篇:Opencv4Android分享emo中调用摄像头
    本篇内容很简短,产生的原因也很简单:用C/C++解决Java中的效率问题。

一.故事背景
    楼主用的是nexus5x作为开发机,作为google亲儿子简直在合适不过了。配合上AndroidStudio,就是所见即所得。
    那么问题来了,google工程师给nexus5x提供的相机api是左右、上下颠倒的!后来工程师们更新了nexus5x的相机apk,但是治标不治本,所有调用相机api的第三方应用获取的图像依旧是倒的,包括OpenCV。

二.Java解决方案
    没办法了,只能手动颠倒一次再显示出来。操作并不难,图像矩阵2个对角线上的元素对调一下就行了。
  1. private Mat rotateMat(Mat src){
  2.     int rows = src.rows();
  3.     int cols = src.cols();
  4.     double[] tmp1,tmp2;
  5.     for(int i = 0;i < rows;i++)
  6.         for(int j = 0;j < cols/2;j++){
  7.             tmp1 = src.get(i, j);
  8.             tmp2 = src.get(rows-i-1, cols-j-1);
  9.             src.put(i, j, tmp2);
  10.             src.put(rows-i-1, cols-j-1, tmp1);
  11.         }
  12.     return src;        
  13.     }
复制代码
  
  1. public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
  2.       mCameraFrame = rotateMat(inputFrame.rgba());
  3.       return mCameraFrame;
  4.     }
复制代码

    看起来没什么问题,那就运行看看吧。打开应用瞬间相机画面卡死不动,过了十几秒可算稳定下来了,帧率稳定在5FPS。    已经预料到帧率可能会下降不少,但是从30FPS降到5FPS是根本不能接受的!

三.C++解决方案
   nexus5x原生相机分辨率在1440*1080,原生Mat图像为RGBA4通道,Java对OpenCV中Mat的处理效率实在是太低了。 果然这种大数据处理效率还是要靠C++啊,那就再Android中使用C/C++好了。        首先是配置NDK,大家都会,跳过。
    新建位图图像.bmp 新建位图图像.png


    在代码中添加native方法。
  1. public native void rotateMat(long src,long dst);
复制代码
   输入参数为2个Mat图像src和dst的指针/地址。OpenCV中提供了getNativeObjAddr()方法获取数据对象的long类型指针,你需要这样使用这个native函数:
  1. public Mat rotateMat(Mat src){   //用Java包装一下
  2.     rotateMat(src.getNativeObjAddr(),src.getNativeObjAddr());
  3.     return src;        
  4.     }
复制代码

    用C++实现这个native方法。   
    右键工程->AndroidTools->Add Native Support来添加JNI
    新建位图图像.png

   输入so文件名,这里就叫rotateMat.so好了。之后eclipse会自己在project中添加jni文件夹,里面包含一个rotateMat.cpp文件和Android.mk文件。
    新建位图图像.bmp

    其中rotateMat.cpp是个空文件,仅有一句话
  1. #include <jni.h>
复制代码
   Android.mk
  1. LOCAL_PATH := $(call my-dir)
  2. include $(CLEAR_VARS)
  3. LOCAL_MODULE    := rotateMat         //模块名
  4. LOCAL_SRC_FILES := rotateMat.cpp    //实现cpp文件
  5. include $(BUILD_SHARED_LIBRARY)
复制代码
   先不管这些,来在cpp中完成一下native方法:
  1. #include <jni.h>
  2. #include <stdio.h>
  3. #include <opencv2/core/core.hpp>
  4. #include <android/log.h>
  5. #include <vector>

  6. #define LOG_TAG "NDK/MatOperate"
  7. #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))

  8. using namespace std;
  9. using namespace cv;

  10. #ifdef __cplusplus
  11. extern "C"{
  12. #endif

  13. JNIEXPORT void JNICALL Java_com_example_opencvdemo_MainActivity_rotateMat(
  14. JNIEnv *env , jobject thiz , jlong src , jlong dst){
  15.     LOGD("Java_com_example_opencvdemo_MainActivity_rotateMat enter");
  16.     int cols = ((Mat*)src)->cols;
  17.     int rows = ((Mat*)src)->rows;
  18.     Vec4b temp ;

  19.     for( int i = 0; i < rows; i++)
  20.     for( int j = 0;j < (src == dst?cols/2:cols);j++ )
  21.       if(src != dst)
  22.          ((Mat*)dst)->at<Vec4b>(rows-1-i,cols-1-j) = ((Mat*)src)->at<Vec4b>(i,j) ;
  23.       else{
  24.          temp = ((Mat*)dst)->at<Vec4b>(rows-1-i,cols-1-j);
  25.          ((Mat*)dst)->at<Vec4b>(rows-1-i,cols-1-j) = ((Mat*)src)->at<Vec4b>(i,j) ;
  26.          ((Mat*)src)->at<Vec4b>(i,j) = temp;
  27.         }
  28.     }
  29. #ifdef __cplusplus
  30. }
  31. #endif
复制代码
    其中条件编译是为了同时照顾C和C++,rotateMat方法的实现与Java相同,依旧是调换矩阵对角线元素。此时CDT里应该有一大片错误信息,原因是我们在工程中运用的大量opencv的数据结构,你还需要把<opencv2/core/core.hpp>这些头文件考过来一起编译。   

新建位图图像.png    

    接下来是配置编译条件
回到Android.mk文件,关于详细语法请自行google,这里要强调的是如何带OpenCV编译。
  1. LOCAL_PATH := $(call my-dir)

  2. include $(CLEAR_VARS)
  3. OPENCV_INSTALL_MODULES:=on
  4. OPENCV_LIB_TYPE:=SHARED
  5. include F:/Android/OpenCV-android-sdk/sdk/native/jni/OpenCV.mk   //在这里添加OpenCV.mk的路径

  6. LOCAL_MODULE    := rotateMat         
  7. LOCAL_SRC_FILES := rotateMat.cpp   
  8. LOCAL_C_INCLUDES += $(LOCAL_PATH)
  9. LOCAL_LDLIBS     += -llog -ldl

  10. include $(BUILD_SHARED_LIBRARY)
复制代码
   还需要Application.mk文件,在jni文件夹里新建即可:
  1. APP_STL := gnustl_static
  2. APP_CPPFLAGS := -frtti -fexceptions
  3. APP_ABI := armeabi-v7a
  4. APP_PLATFORM := android-8
复制代码
   确认无误的话就可以尝试编译了!
新建位图图像.bmp
新建位图图像.bmp
    在activity中导入Library:
  1. static {
  2.              Log.i(TAG,"OpenCV library load!");
  3.              if (!OpenCVLoader.initDebug()) {
  4.     Log.i(TAG,"OpenCV load not successfully");
  5.     } else {
  6.         System.loadLibrary("rotateMat");// 与LOCAL_MODULE := rotateMat 一致就可以了
  7.    }
  8.             }
复制代码
   接下来就可以正常使用这个native函数了!
  1. public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
  2.     mCameraFrame = rotateMat(inputFrame.rgba());
  3.     return mCameraFrame;
  4.     }
复制代码
   虽然帧率也下降到了15FPS,但是还是要比动不动就卡死的Java好得多。

QQ图片20160627232830.png QQ图片20160627232838.png

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|OpenCV中文网站  

GMT+8, 2019-9-21 13:21 , Processed in 0.044001 second(s), 19 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表