OpenCV4Android 2,脱离JNI的C++接口

来源:互联网 发布:数据库 删除表drop 编辑:程序博客网 时间:2024/06/06 07:24

OpenCV4Android 2,脱离JNIC++接口

——让你的OpenCV程序远离Android

关于环境的搭建,参考我前一篇博客:

第一个OpenCV4Android

程序目标:实现相机对周围环境的实时检测,要求OpenCV处理代码脱离Android代码已经JNI代码


1Camera线程

在写C++代码时,我们会开启一个新的线程,在线程中cvqueryframe实时的得到每一帧的数据再进行处理,那么按照这个思路我们在Android使用相机也要开启一个新的线程。

思考了很久怎样在Android新线程中(非UI线程)中嵌入OpenCV打开相机的例子,其实,这一点org.opencv.android.CameraBridgeViewBase类中已经帮我们(通过继承)解决了。我们可以键入如下测试代码:


 

protected void onCreate(Bundle savedInstanceState) {    Log.i(TAG,"called onCreate");        super.onCreate(savedInstanceState);                setContentView(R.layout.activity_main);        mOpenCvCameraView=(CameraBridgeViewBase)findViewById(R.id.HelloOpenCvView);        mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);        mOpenCvCameraView.setCvCameraViewListener(this);        btn=(Button)findViewById(R.id.button1);        btn.setOnClickListener(new OnClickListener(){        public void onClick(View view){        doCany=!doCany;        System.out.println("OnClickListener   -----"+Thread.currentThread().getId());        }        });      }

public Mat onCameraFrame(CvCameraViewFrame inputFrame){         System.out.println("onCameraFrame   -----"+Thread.currentThread().getId());         return inputFrame.rgba();    }

第一个println位于我们响应UI界面的OnCreatea函数,二个println位于我们响应CameraViewListener的事件响应函数中。

在LogCat中我们新建 Tag=System.out的过滤器,我们会得到下列输出:


可以看到输出的线程ID并不同,那么显然CameraBridgeViewBase中已经帮我们解决了多线程的问题,我们没有必要再开一个新的线程去处理相机的每一帧数据。直接在onCameraFrame(CvCameraViewFrameinputFrame)中加入处理每一帧图像的相关代码即可。


2、程序框架





3、程序源码

 

Activity_main.xml

注意android:layout_widthandroid:layout_height不能为任意值


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:opencv="http://schemas.android.com/apk/res-auto"    android:id="@+id/container"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical" >    <org.opencv.android.JavaCameraView                android:layout_width="320px"        android:layout_height="280px"android:visibility="gone"android:id="@+id/HelloOpenCvView"opencv:show_fps="true"opencv:camera_id="any"/>    <Button        android:id="@+id/button1"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_below="@+id/HelloOpenCvView"        android:text="Button" /></LinearLayout>

MainActivity.java


public class MainActivity extends Activity implements CvCameraViewListener2 {private CameraBridgeViewBase mOpenCvCameraView;private boolean              mIsJavaCamera = true;private boolean doCany=false;private Button btn;private static final String TAG = "OCVSample::Activity";private ProcessImg nativeProcess;String xmlFile="heh";//当加载OpenCV库成功后的回调函数 private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {        @Override        public void onManagerConnected(int status) {            switch (status) {                case LoaderCallbackInterface.SUCCESS:                {                    Log.i(TAG, "OpenCV loaded successfully");        //加载本地库                    System.loadLibrary("ProcessImg");        //                    mOpenCvCameraView.enableView();                    nativeProcess=new ProcessImg(xmlFile);                } break;                default:                {                    super.onManagerConnected(status);                } break;            }        }    };    @SuppressWarnings("deprecation")@Override    protected void onCreate(Bundle savedInstanceState) {    Log.i(TAG,"called onCreate");        super.onCreate(savedInstanceState);                                              setContentView(R.layout.activity_main);               if (mIsJavaCamera)            mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.HelloOpenCvView);        else            mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.HelloOpenCvView);                mOpenCvCameraView=(CameraBridgeViewBase)findViewById(R.id.HelloOpenCvView);                mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);        //设置CameraView的事件监听        mOpenCvCameraView.setCvCameraViewListener(this);                      btn=(Button)findViewById(R.id.button1);                btn.setOnClickListener(new OnClickListener(){        public void onClick(View view){        doCany=!doCany;        System.out.println("OnClickListener   -----"+Thread.currentThread().getId());        }                });            }    public void onPause()    {    super.onPause();    if(mOpenCvCameraView!=null){    mOpenCvCameraView.disableView();    }    }    public void onResume()    {    super.onResume();    System.out.println("onResume   -----"+Thread.currentThread().getId());    OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);    }            public void onDestroy(){    super.onDestroy();    if(mOpenCvCameraView!=null){    mOpenCvCameraView.disableView();    }    }       @Override//当相机开始工作后触发该函数,之后会调用onCameraFrame()函数public void onCameraViewStarted(int width, int height) {// TODO Auto-generated method stub}    public void onCameraViewStopped(){        }    //接收每一帧图像,并进行需要的处理    public Mat onCameraFrame(CvCameraViewFrame inputFrame){    System.out.println("onCameraFrame   -----"+Thread.currentThread().getId());    if(doCany==true){    Mat CannyMat=inputFrame.gray();        inputFrame.gray().copyTo(CannyMat);    nativeProcess.SignDetect(CannyMat);    return CannyMat;    }    else {    return inputFrame.rgba();        }    }  }


Process.java


public class ProcessImg {public ProcessImg(String xmlFile){mNativeObj = nativeCreateObject(xmlFile);}public void SignDetect(Mat input){nativeSignDetect(mNativeObj,input.getNativeObjAddr());}private long mNativeObj = 0;private static native long nativeCreateObject(String cascadeName);private static native void nativeSignDetect(long thiz,long inputImage);}


ProcessImg.h自动生成,不在论述。

ProcessImg.cpp


#include "ProcessImg.h"#include <opencv2/core/core.hpp>#include <opencv/cv.hpp>#include <opencv2/highgui/highgui.hpp>#include <string>#include <vector>#include <android/log.h>#include "TrafficSign.h"using namespace std;using namespace cv;extern "C" {JNIEXPORT jlong JNICALL Java_com_pan_helloopencv_ProcessImg_nativeCreateObject  (JNIEnv *jenv, jclass, jstring jFileName){jlong result = 0;//将jstring 转换为 char*const char* xmlFileName = jenv->GetStringUTFChars(jFileName,NULL);//string xmlFile(xmlFileName); try    {//创建图像处理对象(由TrafficSign.h定义)result = (jlong)new TrafficSignDetection(xmlFileName);return result;    } catch(cv::Exception& e)     {        // LOGD("nativeCreateObject caught cv::Exception: %s", e.what());         jclass je = jenv->FindClass("org/opencv/core/CvException");         if(!je)             je = jenv->FindClass("java/lang/Exception");         jenv->ThrowNew(je, e.what());     }     catch (...)     {        // LOGD("nativeCreateObject caught unknown exception");         jclass je = jenv->FindClass("java/lang/Exception");         jenv->ThrowNew(je, "Unknown exception in JNI code {highgui::VideoCapture_n_1VideoCapture__()}");         return 0;     }}/* * Class:     com_pan_helloopencv_ProcessImg * Method:    nativeSignDetect * Signature: (JJ)V */JNIEXPORT void JNICALL Java_com_pan_helloopencv_ProcessImg_nativeSignDetect  (JNIEnv *jenv, jclass, jlong thiz, jlong inputImage){  try    {//处理每一帧图像  ((TrafficSignDetection*)thiz)->process(*((Mat*)inputImage));    }  catch(cv::Exception& e)     {         //LOGD("nativeCreateObject caught cv::Exception: %s", e.what());         jclass je = jenv->FindClass("org/opencv/core/CvException");         if(!je)             je = jenv->FindClass("java/lang/Exception");         jenv->ThrowNew(je, e.what());     }     catch (...)     {        // LOGD("nativeDetect caught unknown exception");         jclass je = jenv->FindClass("java/lang/Exception");         jenv->ThrowNew(je, "Unknown exception in JNI code {highgui::VideoCapture_n_1VideoCapture__()}");     }}}

TrafficSign.h

C++代码实现图像处理的主逻辑


#pragma once#if defined(__linux__) || defined(LINUX) || defined(__APPLE__) || defined(ANDROID)#include <opencv2/core/core.hpp>#include <vector>#include <string>class TrafficSignDetection{public://需要的参数struct Parameters{int minObjectSize;int maxObjectSize;double scaleFactor;int maxTrackLifetime;int minNeighbors;int minDetectionPeriod; //the minimal time between run of the big object detector (on the whole frame) in ms (1000 mean 1 sec), default=0Parameters();};//含参构造函数//TrafficSignDetection(const char* xmlFile,const Parameters& params);TrafficSignDetection(const char* xmlFile);virtual ~TrafficSignDetection();//进行交通标志检测virtual void process(const cv::Mat& imageGray);//停止检测virtual void stop();//virtual void setXmlFile(const char *fileName);private:char * FileName;};namespace cv{::TrafficSignDetection;}#endif


Android.mk


LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)include E:/OpenCV-2.4.4-android-sdk/sdk/native/jni/OpenCV.mkLOCAL_SRC_FILES  := ProcessImg.cpp TrafficSign.cppLOCAL_LDLIBS     += -llog -ldlLOCAL_CFLAGS += -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) -fpermissiveLOCAL_MODULE     := ProcessImginclude $(BUILD_SHARED_LIBRARY)


4、运行结果




点击按钮楷书处理每一帧图像:



至此,我们就实现了脱离一切JNI函数的接口文件TrafficSign.h,之后我们就可以定义TrafficSign.cpp文件,用纯C++的代码实现我们的图像算法了!


再使用Cygwin编译的时候遇到很多问题,会在后面的博文中进行讨论,因此未完待续……


技术交流,欢迎联系 pan_an@foxmail.com

0 0