1. 程式人生 > >使用OpenCv with Android Camera Surfaceview時OpenCVLoader失敗問題

使用OpenCv with Android Camera Surfaceview時OpenCVLoader失敗問題

今天在使用OpenCV Library 2.4.10建立了我的第一個基於opencv的Android程式,先貼出我的程式碼:

package com.example.facedetector;

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity implements CvCameraViewListener2, OnClickListener {

	private Button btnStart;
	private CameraBridgeViewBase cameraPreview = null; 
	private Mat mRgba; 
	private boolean startProcess = false;
	protected BaseLoaderCallback  mLoaderCallback = new BaseLoaderCallback(this) {
		@Override
		public void onManagerConnected(int status) {
			switch (status) {
			case LoaderCallbackInterface.SUCCESS:
			{
				cameraPreview.enableView();
			} break;
			default:
			{
				super.onManagerConnected(status);
			} break;
			}
		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		cameraPreview = (CameraBridgeViewBase) findViewById(R.id.cameraView);
		cameraPreview.setVisibility(SurfaceView.VISIBLE);
		cameraPreview.setCvCameraViewListener(this);

		btnStart = (Button) findViewById(R.id.btnStart);
		btnStart.setOnClickListener(this);

	}
	
	@Override
	protected void onResume() {
		super.onResume();
	<strong>	OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_10, getApplicationContext(), mLoaderCallback );</strong>
	}


	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		if (id == R.id.action_settings) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}

	private void startFaceDetetion(Mat image){
		String xmlfilePath=getClass().getResource("lbpcascade_frontalface.xml").getPath().substring(1);  
		CascadeClassifier faceDetector = new CascadeClassifier(xmlfilePath);  
		// Detect faces in the image.  
		// MatOfRect is a special container class for Rect.  
		MatOfRect faceDetections = new MatOfRect();  
		faceDetector.detectMultiScale(image, faceDetections);  

		System.out.println(String.format("Detected %s faces", faceDetections.toArray().length));  

		// Draw a bounding box around each face.  
		for (Rect rect : faceDetections.toArray()) {  
			Scalar color = new Scalar(0, 255, 0);
			Core.rectangle(image, new Point(rect.x, rect.y), new Point(rect.x + rect.width, rect.y + rect.height), color);
		} 

	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
        if (cameraPreview != null) {
        	cameraPreview.disableView();
        	startProcess = false;
        }
	}

	@Override
	protected void onPause() {
		super.onPause();
		if(cameraPreview != null){
			cameraPreview.disableView();
			startProcess = false;
		}
	}

	@Override
	public void onCameraViewStarted(int width, int height) {
		mRgba =  new Mat(height, width, CvType.CV_8UC4);	
	}

	@Override
	public void onCameraViewStopped() {
		mRgba.release();
	}

	@Override
	public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
	    if(startProcess == true)
		{
	    	mRgba = inputFrame.rgba();
                return mRgba
	    }
	    return null;
	}

	@Override
	public void onClick(View v) {
		startProcess = !startProcess;
	}
}
這段程式碼,按照Internet上各位前輩們的經驗總結,是沒有問題的,但是當我在真正run它的時候,還是出現了問題,logcat上出現了以下提示:
08-18 20:57:03.549: E/AndroidRuntime(3444): java.lang.RuntimeException: Unable to resume activity {com.example.facedetector/com.example.facedetector.MainActivity}: java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=org.opencv.engine.BIND }
也就是說OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_10, getApplicationContext(), mLoaderCallback );無法執行成功。

網路上也有人遇到了同樣的問題,給出的解決方案是:

Try using getApplicationContext() instead of this in OpenCVLoader.initAsync(..)

但是我在修改後,程式依然無法執行。

出現上述問題的原因主要是因為使用了Android 5.0作為targetsdk,專業的解釋 in here。這裡我就不多說,大家可以自行研究。簡單解釋是:

Android 5.0 service intents must be explicit intents and the OpenCV library uses implicit intents in the
AsyncServiceHelper.java ( new Intent("org.opencv.engine.BIND") ), so it doesn't work.

既然是因為SDK version 的問題,那麼只需要改變version就可以了,因此這裡我將我的targetsdk由API 21改為了API 19:

android:targetSdkVersion="21"  --------->  android:targetSdkVersion="19"

問題解決了,這時候就可以正常執行APP了。