本帖最后由 mumumusuc 于 2016-6-27 23:37 编辑
上篇:Opencv4Android分享emo中调用摄像头
本篇内容很简短,产生的原因也很简单:用C/C++解决Java中的效率问题。
一.故事背景
楼主用的是nexus5x作为开发机,作为google亲儿子简直在合适不过了。配合上AndroidStudio,就是所见即所得。
那么问题来了,google工程师给nexus5x提供的相机api是左右、上下颠倒的!后来工程师们更新了nexus5x的相机apk,但是治标不治本,所有调用相机api的第三方应用获取的图像依旧是倒的,包括OpenCV。
二.Java解决方案
没办法了,只能手动颠倒一次再显示出来。操作并不难,图像矩阵2个对角线上的元素对调一下就行了。
- private Mat rotateMat(Mat src){
- int rows = src.rows();
- int cols = src.cols();
- double[] tmp1,tmp2;
- for(int i = 0;i < rows;i++)
- for(int j = 0;j < cols/2;j++){
- tmp1 = src.get(i, j);
- tmp2 = src.get(rows-i-1, cols-j-1);
- src.put(i, j, tmp2);
- src.put(rows-i-1, cols-j-1, tmp1);
- }
- return src;
- }
复制代码 - public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
- mCameraFrame = rotateMat(inputFrame.rgba());
- return mCameraFrame;
- }
复制代码
看起来没什么问题,那就运行看看吧。打开应用瞬间相机画面卡死不动,过了十几秒可算稳定下来了,帧率稳定在5FPS。 已经预料到帧率可能会下降不少,但是从30FPS降到5FPS是根本不能接受的!
三.C++解决方案
nexus5x原生相机分辨率在1440*1080,原生Mat图像为RGBA4通道,Java对OpenCV中Mat的处理效率实在是太低了。 果然这种大数据处理效率还是要靠C++啊,那就再Android中使用C/C++好了。 首先是配置NDK,大家都会,跳过。
在代码中添加native方法。
- public native void rotateMat(long src,long dst);
复制代码 输入参数为2个Mat图像src和dst的指针/地址。OpenCV中提供了getNativeObjAddr()方法获取数据对象的long类型指针,你需要这样使用这个native函数:- public Mat rotateMat(Mat src){ //用Java包装一下
- rotateMat(src.getNativeObjAddr(),src.getNativeObjAddr());
- return src;
- }
复制代码
用C++实现这个native方法。
右键工程->AndroidTools->Add Native Support来添加JNI
输入so文件名,这里就叫rotateMat.so好了。之后eclipse会自己在project中添加jni文件夹,里面包含一个rotateMat.cpp文件和Android.mk文件。
其中rotateMat.cpp是个空文件,仅有一句话 Android.mk- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE := rotateMat //模块名
- LOCAL_SRC_FILES := rotateMat.cpp //实现cpp文件
- include $(BUILD_SHARED_LIBRARY)
复制代码 先不管这些,来在cpp中完成一下native方法:
- #include <jni.h>
- #include <stdio.h>
- #include <opencv2/core/core.hpp>
- #include <android/log.h>
- #include <vector>
- #define LOG_TAG "NDK/MatOperate"
- #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
- using namespace std;
- using namespace cv;
- #ifdef __cplusplus
- extern "C"{
- #endif
- JNIEXPORT void JNICALL Java_com_example_opencvdemo_MainActivity_rotateMat(
- JNIEnv *env , jobject thiz , jlong src , jlong dst){
- LOGD("Java_com_example_opencvdemo_MainActivity_rotateMat enter");
- int cols = ((Mat*)src)->cols;
- int rows = ((Mat*)src)->rows;
- Vec4b temp ;
- for( int i = 0; i < rows; i++)
- for( int j = 0;j < (src == dst?cols/2:cols);j++ )
- if(src != dst)
- ((Mat*)dst)->at<Vec4b>(rows-1-i,cols-1-j) = ((Mat*)src)->at<Vec4b>(i,j) ;
- else{
- temp = ((Mat*)dst)->at<Vec4b>(rows-1-i,cols-1-j);
- ((Mat*)dst)->at<Vec4b>(rows-1-i,cols-1-j) = ((Mat*)src)->at<Vec4b>(i,j) ;
- ((Mat*)src)->at<Vec4b>(i,j) = temp;
- }
- }
- #ifdef __cplusplus
- }
- #endif
复制代码 其中条件编译是为了同时照顾C和C++,rotateMat方法的实现与Java相同,依旧是调换矩阵对角线元素。此时CDT里应该有一大片错误信息,原因是我们在工程中运用的大量opencv的数据结构,你还需要把<opencv2/core/core.hpp>这些头文件考过来一起编译。
接下来是配置编译条件
回到Android.mk文件,关于详细语法请自行google,这里要强调的是如何带OpenCV编译。
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- OPENCV_INSTALL_MODULES:=on
- OPENCV_LIB_TYPE:=SHARED
- include F:/Android/OpenCV-android-sdk/sdk/native/jni/OpenCV.mk //在这里添加OpenCV.mk的路径
- LOCAL_MODULE := rotateMat
- LOCAL_SRC_FILES := rotateMat.cpp
- LOCAL_C_INCLUDES += $(LOCAL_PATH)
- LOCAL_LDLIBS += -llog -ldl
- include $(BUILD_SHARED_LIBRARY)
复制代码 还需要Application.mk文件,在jni文件夹里新建即可:- APP_STL := gnustl_static
- APP_CPPFLAGS := -frtti -fexceptions
- APP_ABI := armeabi-v7a
- APP_PLATFORM := android-8
复制代码 确认无误的话就可以尝试编译了!
在activity中导入Library:
- static {
- Log.i(TAG,"OpenCV library load!");
- if (!OpenCVLoader.initDebug()) {
- Log.i(TAG,"OpenCV load not successfully");
- } else {
- System.loadLibrary("rotateMat");// 与LOCAL_MODULE := rotateMat 一致就可以了
- }
- }
复制代码 接下来就可以正常使用这个native函数了!
- public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
- mCameraFrame = rotateMat(inputFrame.rgba());
- return mCameraFrame;
- }
复制代码 虽然帧率也下降到了15FPS,但是还是要比动不动就卡死的Java好得多。
|