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

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、程序框架


OpenCV4Android 2,脱离JNI的C++接口_第1张图片



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=0

		Parameters();
	};
	//含参构造函数
	//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.mk
LOCAL_SRC_FILES  := ProcessImg.cpp TrafficSign.cpp

LOCAL_LDLIBS     += -llog -ldl

LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) -fpermissive
LOCAL_MODULE     := ProcessImg

include $(BUILD_SHARED_LIBRARY)


4、运行结果


OpenCV4Android 2,脱离JNI的C++接口_第2张图片


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

OpenCV4Android 2,脱离JNI的C++接口_第3张图片


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


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


技术交流,欢迎联系 [email protected]

你可能感兴趣的:(android,jni,opencv)