diff --git a/samples/android/face-detection/res/layout/face_detect_surface_view.xml b/samples/android/face-detection/res/layout/face_detect_surface_view.xml new file mode 100644 index 0000000000..57027643a5 --- /dev/null +++ b/samples/android/face-detection/res/layout/face_detect_surface_view.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/samples/android/face-detection/src/org/opencv/samples/fd/FdActivity.java b/samples/android/face-detection/src/org/opencv/samples/fd/FdActivity.java index e6e6d59884..1009261fad 100644 --- a/samples/android/face-detection/src/org/opencv/samples/fd/FdActivity.java +++ b/samples/android/face-detection/src/org/opencv/samples/fd/FdActivity.java @@ -1,12 +1,26 @@ package org.opencv.samples.fd; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + import org.opencv.android.BaseLoaderCallback; import org.opencv.android.LoaderCallbackInterface; import org.opencv.android.OpenCVLoader; +import org.opencv.core.Core; +import org.opencv.core.Mat; +import org.opencv.core.MatOfRect; +import org.opencv.core.Rect; +import org.opencv.core.Scalar; +import org.opencv.core.Size; +import org.opencv.framework.OpenCvJavaCameraView; +import org.opencv.framework.OpenCvCameraBridgeViewBase.CvCameraViewListener; +import org.opencv.imgproc.Imgproc; +import org.opencv.objdetect.CascadeClassifier; import android.app.Activity; -import android.app.AlertDialog; -import android.content.DialogInterface; +import android.content.Context; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -14,19 +28,34 @@ import android.view.MenuItem; import android.view.Window; import android.view.WindowManager; -public class FdActivity extends Activity { - private static final String TAG = "OCVSample::Activity"; +public class FdActivity extends Activity implements CvCameraViewListener { - private MenuItem mItemFace50; - private MenuItem mItemFace40; - private MenuItem mItemFace30; - private MenuItem mItemFace20; - private MenuItem mItemType; - private int mDetectorType = 0; - private String[] mDetectorName; - private FdView mView; + private static final String TAG = "OCVSample::Activity"; + private static final Scalar FACE_RECT_COLOR = new Scalar(0, 255, 0, 255); + public static final int JAVA_DETECTOR = 0; + public static final int NATIVE_DETECTOR = 1; - private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) { + private MenuItem mItemFace50; + private MenuItem mItemFace40; + private MenuItem mItemFace30; + private MenuItem mItemFace20; + private MenuItem mItemType; + + private Mat mRgba; + private Mat mGray; + private File mCascadeFile; + private CascadeClassifier mJavaDetector; + private DetectionBasedTracker mNativeDetector; + + private int mDetectorType = JAVA_DETECTOR; + private String[] mDetectorName; + + private float mRelativeFaceSize = 0; + private int mAbsoluteFaceSize = 0; + + private OpenCvJavaCameraView mOpenCvCameraView; + + private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { @Override public void onManagerConnected(int status) { switch (status) { @@ -34,44 +63,41 @@ public class FdActivity extends Activity { { Log.i(TAG, "OpenCV loaded successfully"); - // Load native libs after OpenCV initialization + // Load native library after(!) OpenCV initialization System.loadLibrary("detection_based_tracker"); - // Create and set View - mView = new FdView(mAppContext); - mView.setDetectorType(mDetectorType); - mView.setMinFaceSize(0.2f); - setContentView(mView); + try { + // load cascade file from application resources + InputStream is = getResources().openRawResource(R.raw.lbpcascade_frontalface); + File cascadeDir = getDir("cascade", Context.MODE_PRIVATE); + mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml"); + FileOutputStream os = new FileOutputStream(mCascadeFile); - // Check native OpenCV camera - if( !mView.openCamera() ) { - AlertDialog ad = new AlertDialog.Builder(mAppContext).create(); - ad.setCancelable(false); // This blocks the 'BACK' button - ad.setMessage("Fatal error: can't open camera!"); - ad.setButton(AlertDialog.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - finish(); - } - }); - ad.show(); - } - } break; - - /** OpenCV loader cannot start Google Play **/ - case LoaderCallbackInterface.MARKET_ERROR: - { - Log.d(TAG, "Google Play service is not accessible!"); - AlertDialog MarketErrorMessage = new AlertDialog.Builder(mAppContext).create(); - MarketErrorMessage.setTitle("OpenCV Manager"); - MarketErrorMessage.setMessage("Google Play service is not accessible!\nTry to install the 'OpenCV Manager' and the appropriate 'OpenCV binary pack' APKs from OpenCV SDK manually via 'adb install' command."); - MarketErrorMessage.setCancelable(false); // This blocks the 'BACK' button - MarketErrorMessage.setButton(AlertDialog.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - finish(); + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = is.read(buffer)) != -1) { + os.write(buffer, 0, bytesRead); } - }); - MarketErrorMessage.show(); + is.close(); + os.close(); + + mJavaDetector = new CascadeClassifier(mCascadeFile.getAbsolutePath()); + if (mJavaDetector.empty()) { + Log.e(TAG, "Failed to load cascade classifier"); + mJavaDetector = null; + } else + Log.i(TAG, "Loaded cascade classifier from " + mCascadeFile.getAbsolutePath()); + + mNativeDetector = new DetectionBasedTracker(mCascadeFile.getAbsolutePath(), 0); + + cascadeDir.delete(); + + } catch (IOException e) { + e.printStackTrace(); + Log.e(TAG, "Failed to load cascade. Exception thrown: " + e); + } + + mOpenCvCameraView.enableView(); } break; default: { @@ -83,30 +109,12 @@ public class FdActivity extends Activity { public FdActivity() { mDetectorName = new String[2]; - mDetectorName[FdView.JAVA_DETECTOR] = "Java"; - mDetectorName[FdView.NATIVE_DETECTOR] = "Native (tracking)"; + mDetectorName[JAVA_DETECTOR] = "Java"; + mDetectorName[NATIVE_DETECTOR] = "Native (tracking)"; + Log.i(TAG, "Instantiated new " + this.getClass()); } - @Override - protected void onPause() { - Log.i(TAG, "called onPause"); - if (null != mView) - mView.releaseCamera(); - super.onPause(); - } - - @Override - protected void onResume() { - Log.i(TAG, "called onResume"); - super.onResume(); - - Log.i(TAG, "Trying to load OpenCV library"); - if (!OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_2, this, mOpenCVCallBack)) { - Log.e(TAG, "Cannot connect to OpenCV Manager"); - } - } - /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { @@ -114,6 +122,75 @@ public class FdActivity extends Activity { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + setContentView(R.layout.face_detect_surface_view); + + mOpenCvCameraView = (OpenCvJavaCameraView)findViewById(R.id.fd_activity_surface_view); + mOpenCvCameraView.setCvCameraViewListener(this); + } + + @Override + public void onPause() + { + mOpenCvCameraView.disableView(); + super.onPause(); + } + + @Override + public void onResume() + { + super.onResume(); + OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_2, this, mLoaderCallback); + } + + public void onDestroy() { + super.onDestroy(); + mOpenCvCameraView.disableView(); + } + + public void onCameraViewStarted(int width, int height) { + mGray = new Mat(); + mRgba = new Mat(); + } + + public void onCameraViewStopped() { + mGray.release(); + mRgba.release(); + } + + public Mat onCameraFrame(Mat inputFrame) { + + inputFrame.copyTo(mRgba); + Imgproc.cvtColor(inputFrame, mGray, Imgproc.COLOR_RGBA2GRAY); + + if (mAbsoluteFaceSize == 0) { + int height = mGray.rows(); + if (Math.round(height * mRelativeFaceSize) > 0) { + mAbsoluteFaceSize = Math.round(height * mRelativeFaceSize); + } + mNativeDetector.setMinFaceSize(mAbsoluteFaceSize); + } + + MatOfRect faces = new MatOfRect(); + + if (mDetectorType == JAVA_DETECTOR) { + if (mJavaDetector != null) + mJavaDetector.detectMultiScale(mGray, faces, 1.1, 2, 2, // TODO: objdetect.CV_HAAR_SCALE_IMAGE + new Size(mAbsoluteFaceSize, mAbsoluteFaceSize), new Size()); + } + else if (mDetectorType == NATIVE_DETECTOR) { + if (mNativeDetector != null) + mNativeDetector.detect(mGray, faces); + } + else { + Log.e(TAG, "Detection method is not selected!"); + } + + Rect[] facesArray = faces.toArray(); + for (int i = 0; i < facesArray.length; i++) + Core.rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3); + + return mRgba; } @Override @@ -131,18 +208,37 @@ public class FdActivity extends Activity { public boolean onOptionsItemSelected(MenuItem item) { Log.i(TAG, "called onOptionsItemSelected; selected item: " + item); if (item == mItemFace50) - mView.setMinFaceSize(0.5f); + setMinFaceSize(0.5f); else if (item == mItemFace40) - mView.setMinFaceSize(0.4f); + setMinFaceSize(0.4f); else if (item == mItemFace30) - mView.setMinFaceSize(0.3f); + setMinFaceSize(0.3f); else if (item == mItemFace20) - mView.setMinFaceSize(0.2f); + setMinFaceSize(0.2f); else if (item == mItemType) { mDetectorType = (mDetectorType + 1) % mDetectorName.length; item.setTitle(mDetectorName[mDetectorType]); - mView.setDetectorType(mDetectorType); + setDetectorType(mDetectorType); } return true; } + + private void setMinFaceSize(float faceSize) { + mRelativeFaceSize = faceSize; + mAbsoluteFaceSize = 0; + } + + private void setDetectorType(int type) { + if (mDetectorType != type) { + mDetectorType = type; + + if (type == NATIVE_DETECTOR) { + Log.i(TAG, "Detection Based Tracker enabled"); + mNativeDetector.start(); + } else { + Log.i(TAG, "Cascade detector enabled"); + mNativeDetector.stop(); + } + } + } } diff --git a/samples/android/face-detection/src/org/opencv/samples/fd/FdView.java b/samples/android/face-detection/src/org/opencv/samples/fd/FdView.java deleted file mode 100644 index a9e4c2c095..0000000000 --- a/samples/android/face-detection/src/org/opencv/samples/fd/FdView.java +++ /dev/null @@ -1,175 +0,0 @@ -package org.opencv.samples.fd; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; - -import org.opencv.android.Utils; -import org.opencv.core.Core; -import org.opencv.core.Mat; -import org.opencv.core.MatOfRect; -import org.opencv.core.Rect; -import org.opencv.core.Scalar; -import org.opencv.core.Size; -import org.opencv.highgui.Highgui; -import org.opencv.highgui.VideoCapture; -import org.opencv.objdetect.CascadeClassifier; - -import android.content.Context; -import android.graphics.Bitmap; -import android.util.Log; -import android.view.SurfaceHolder; - -class FdView extends SampleCvViewBase { - private static final String TAG = "OCVSample::View"; - private Mat mRgba; - private Mat mGray; - private File mCascadeFile; - private CascadeClassifier mJavaDetector; - private DetectionBasedTracker mNativeDetector; - - private static final Scalar FACE_RECT_COLOR = new Scalar(0, 255, 0, 255); - - public static final int JAVA_DETECTOR = 0; - public static final int NATIVE_DETECTOR = 1; - - private int mDetectorType = JAVA_DETECTOR; - - private float mRelativeFaceSize = 0; - private int mAbsoluteFaceSize = 0; - - public void setMinFaceSize(float faceSize) { - mRelativeFaceSize = faceSize; - mAbsoluteFaceSize = 0; - } - - public void setDetectorType(int type) { - if (mDetectorType != type) { - mDetectorType = type; - - if (type == NATIVE_DETECTOR) { - Log.i(TAG, "Detection Based Tracker enabled"); - mNativeDetector.start(); - } else { - Log.i(TAG, "Cascade detector enabled"); - mNativeDetector.stop(); - } - } - } - - public FdView(Context context) { - super(context); - - try { - // load cascade file from application resources - InputStream is = context.getResources().openRawResource(R.raw.lbpcascade_frontalface); - File cascadeDir = context.getDir("cascade", Context.MODE_PRIVATE); - mCascadeFile = new File(cascadeDir, "lbpcascade_frontalface.xml"); - FileOutputStream os = new FileOutputStream(mCascadeFile); - - byte[] buffer = new byte[4096]; - int bytesRead; - while ((bytesRead = is.read(buffer)) != -1) { - os.write(buffer, 0, bytesRead); - } - is.close(); - os.close(); - - mJavaDetector = new CascadeClassifier(mCascadeFile.getAbsolutePath()); - if (mJavaDetector.empty()) { - Log.e(TAG, "Failed to load cascade classifier"); - mJavaDetector = null; - } else - Log.i(TAG, "Loaded cascade classifier from " + mCascadeFile.getAbsolutePath()); - - mNativeDetector = new DetectionBasedTracker(mCascadeFile.getAbsolutePath(), 0); - - cascadeDir.delete(); - - } catch (IOException e) { - e.printStackTrace(); - Log.e(TAG, "Failed to load cascade. Exception thrown: " + e); - } - - Log.i(TAG, "Instantiated new " + this.getClass()); - } - - @Override - public void surfaceCreated(SurfaceHolder holder) { - Log.i(TAG, "called surfaceCreated"); - synchronized (this) { - // initialize Mats before usage - mGray = new Mat(); - mRgba = new Mat(); - } - - super.surfaceCreated(holder); - } - - @Override - protected Bitmap processFrame(VideoCapture capture) { - capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA); - capture.retrieve(mGray, Highgui.CV_CAP_ANDROID_GREY_FRAME); - - if (mAbsoluteFaceSize == 0) { - int height = mGray.rows(); - if (Math.round(height * mRelativeFaceSize) > 0) { - mAbsoluteFaceSize = Math.round(height * mRelativeFaceSize); - } - mNativeDetector.setMinFaceSize(mAbsoluteFaceSize); - } - - MatOfRect faces = new MatOfRect(); - - if (mDetectorType == JAVA_DETECTOR) { - if (mJavaDetector != null) - mJavaDetector.detectMultiScale(mGray, faces, 1.1, 2, 2, // TODO: objdetect.CV_HAAR_SCALE_IMAGE - new Size(mAbsoluteFaceSize, mAbsoluteFaceSize), new Size()); - } - else if (mDetectorType == NATIVE_DETECTOR) { - if (mNativeDetector != null) - mNativeDetector.detect(mGray, faces); - } - else { - Log.e(TAG, "Detection method is not selected!"); - } - - Rect[] facesArray = faces.toArray(); - for (int i = 0; i < facesArray.length; i++) - Core.rectangle(mRgba, facesArray[i].tl(), facesArray[i].br(), FACE_RECT_COLOR, 3); - - Bitmap bmp = Bitmap.createBitmap(mRgba.cols(), mRgba.rows(), Bitmap.Config.ARGB_8888); - - try { - Utils.matToBitmap(mRgba, bmp); - } catch(Exception e) { - Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage()); - bmp.recycle(); - bmp = null; - } - - return bmp; - } - - @Override - public void run() { - super.run(); - - synchronized (this) { - // Explicitly deallocate Mats - if (mRgba != null) - mRgba.release(); - if (mGray != null) - mGray.release(); - if (mCascadeFile != null) - mCascadeFile.delete(); - if (mNativeDetector != null) - mNativeDetector.release(); - - mRgba = null; - mGray = null; - mCascadeFile = null; - } - } -} diff --git a/samples/android/face-detection/src/org/opencv/samples/fd/SampleCvViewBase.java b/samples/android/face-detection/src/org/opencv/samples/fd/SampleCvViewBase.java deleted file mode 100644 index 42d5b34e29..0000000000 --- a/samples/android/face-detection/src/org/opencv/samples/fd/SampleCvViewBase.java +++ /dev/null @@ -1,123 +0,0 @@ -package org.opencv.samples.fd; - -import java.util.List; - -import org.opencv.core.Size; -import org.opencv.highgui.VideoCapture; -import org.opencv.highgui.Highgui; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.util.Log; -import android.view.SurfaceHolder; -import android.view.SurfaceView; - -public abstract class SampleCvViewBase extends SurfaceView implements SurfaceHolder.Callback, Runnable { - private static final String TAG = "OCVSample::BaseView"; - - private SurfaceHolder mHolder; - private VideoCapture mCamera; - private FpsMeter mFps; - - public SampleCvViewBase(Context context) { - super(context); - mHolder = getHolder(); - mHolder.addCallback(this); - mFps = new FpsMeter(); - Log.i(TAG, "Instantiated new " + this.getClass()); - } - - public synchronized boolean openCamera() { - Log.i(TAG, "Opening Camera"); - mCamera = new VideoCapture(Highgui.CV_CAP_ANDROID); - if (!mCamera.isOpened()) { - releaseCamera(); - Log.e(TAG, "Can't open native camera"); - return false; - } - return true; - } - - public synchronized void releaseCamera() { - Log.i(TAG, "Releasing Camera"); - if (mCamera != null) { - mCamera.release(); - mCamera = null; - } - } - - public synchronized void setupCamera(int width, int height) { - if (mCamera != null && mCamera.isOpened()) { - Log.i(TAG, "Setup Camera - " + width + "x" + height); - List sizes = mCamera.getSupportedPreviewSizes(); - int mFrameWidth = width; - int mFrameHeight = height; - - // selecting optimal camera preview size - { - double minDiff = Double.MAX_VALUE; - for (Size size : sizes) { - if (Math.abs(size.height - height) < minDiff) { - mFrameWidth = (int) size.width; - mFrameHeight = (int) size.height; - minDiff = Math.abs(size.height - height); - } - } - } - - mCamera.set(Highgui.CV_CAP_PROP_FRAME_WIDTH, mFrameWidth); - mCamera.set(Highgui.CV_CAP_PROP_FRAME_HEIGHT, mFrameHeight); - } - } - - public void surfaceChanged(SurfaceHolder _holder, int format, int width, int height) { - Log.i(TAG, "called surfaceChanged"); - setupCamera(width, height); - } - - public void surfaceCreated(SurfaceHolder holder) { - Log.i(TAG, "called surfaceCreated"); - (new Thread(this)).start(); - } - - public void surfaceDestroyed(SurfaceHolder holder) { - Log.i(TAG, "called surfaceDestroyed"); - } - protected abstract Bitmap processFrame(VideoCapture capture); - - public void run() { - Log.i(TAG, "Started processing thread"); - mFps.init(); - - while (true) { - Bitmap bmp = null; - - synchronized (this) { - if (mCamera == null) - break; - - if (!mCamera.grab()) { - Log.e(TAG, "mCamera.grab() failed"); - break; - } - - bmp = processFrame(mCamera); - - mFps.measure(); - } - - if (bmp != null) { - Canvas canvas = mHolder.lockCanvas(); - if (canvas != null) { - canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR); - canvas.drawBitmap(bmp, (canvas.getWidth() - bmp.getWidth()) / 2, (canvas.getHeight() - bmp.getHeight()) / 2, null); - mFps.draw(canvas, (canvas.getWidth() - bmp.getWidth()) / 2, 0); - mHolder.unlockCanvasAndPost(canvas); - } - bmp.recycle(); - } - } - Log.i(TAG, "Finished processing thread"); - } -} \ No newline at end of file