diff --git a/modules/java/CMakeLists.txt b/modules/java/CMakeLists.txt index eb40260366..ee1ad30813 100644 --- a/modules/java/CMakeLists.txt +++ b/modules/java/CMakeLists.txt @@ -237,7 +237,7 @@ if(ANDROID) set(lib_target_files ${ANDROID_LIB_PROJECT_FILES}) ocv_list_add_prefix(lib_target_files "${OpenCV_BINARY_DIR}/") - android_get_compatible_target(lib_target_sdk_target ${ANDROID_NATIVE_API_LEVEL} ${ANDROID_SDK_TARGET}) + android_get_compatible_target(lib_target_sdk_target ${ANDROID_NATIVE_API_LEVEL} ${ANDROID_SDK_TARGET} 11) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/android_lib/${ANDROID_MANIFEST_FILE}" "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_MANIFEST_FILE}") diff --git a/modules/java/generator/src/java/android+CameraBridgeViewBase.java b/modules/java/generator/src/java/android+CameraBridgeViewBase.java new file mode 100644 index 0000000000..444e4fa7af --- /dev/null +++ b/modules/java/generator/src/java/android+CameraBridgeViewBase.java @@ -0,0 +1,335 @@ +package org.opencv.android; + +import java.util.List; + +import org.opencv.android.Utils; +import org.opencv.core.Mat; +import org.opencv.core.Size; +import org.opencv.highgui.Highgui; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.util.AttributeSet; +import android.util.Log; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +/** + * This is a basic class, implementing the interaction with Camera and OpenCV library. + * The main responsibility of it - is to control when camera can be enabled, process the frame, + * call external listener to make any adjustments to the frame and then draw the resulting + * frame to the screen. + * The clients shall implement CvCameraViewListener + * TODO: add method to control the format in which the frames will be delivered to CvCameraViewListener + */ +public abstract class CameraBridgeViewBase extends SurfaceView implements SurfaceHolder.Callback { + + private static final int MAX_UNSPECIFIED = -1; + + protected int mFrameWidth; + protected int mFrameHeight; + + protected int mMaxHeight; + protected int mMaxWidth; + + protected int mPreviewFormat = Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA; + + private Bitmap mCacheBitmap; + + public CameraBridgeViewBase(Context context, AttributeSet attrs) { + super(context, attrs); + getHolder().addCallback(this); + mMaxWidth = MAX_UNSPECIFIED; + mMaxHeight = MAX_UNSPECIFIED; + } + + public interface CvCameraViewListener { + /** + * This method is invoked when camera preview has started. After this method is invoked + * the frames will start to be delivered to client via the onCameraFrame() callback. + * @param width - the width of the frames that will be delivered + * @param height - the height of the frames that will be delivered + */ + public void onCameraViewStarted(int width, int height); + + /** + * This method is invoked when camera preview has been stopped for some reason. + * No frames will be delivered via onCameraFrame() callback after this method is called. + */ + public void onCameraViewStopped(); + + /** + * This method is invoked when delivery of the frame needs to be done. + * The returned values - is a modified frame which needs to be displayed on the screen. + * TODO: pass the parameters specifying the format of the frame (BPP, YUV or RGB and etc) + */ + public Mat onCameraFrame(Mat inputFrame); + + } + + private static final int STOPPED = 0; + private static final int STARTED = 1; + + private static final String TAG = "CameraBridge"; + + private CvCameraViewListener mListener; + private int mState = STOPPED; + + private boolean mEnabled; + private boolean mSurfaceExist; + + private Object mSyncObject = new Object(); + + public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { + synchronized(mSyncObject) { + if (!mSurfaceExist) { + mSurfaceExist = true; + checkCurrentState(); + } else { + /** Surface changed. We need to stop camera and restart with new parameters */ + /* Pretend that old surface has been destroyed */ + mSurfaceExist = false; + checkCurrentState(); + /* Now use new surface. Say we have it now */ + mSurfaceExist = true; + checkCurrentState(); + } + } + } + + public void surfaceCreated(SurfaceHolder holder) { + /* Do nothing. Wait until surfaceChanged delivered */ + } + + public void surfaceDestroyed(SurfaceHolder holder) { + synchronized(mSyncObject) { + mSurfaceExist = false; + checkCurrentState(); + } + } + + /** + * This method is provided for clients, so they can enable the camera connection. + * The actual onCameraViewStarted callback will be delivered only after both this method is called and surface is available + */ + public void enableView() { + synchronized(mSyncObject) { + mEnabled = true; + checkCurrentState(); + } + } + + /** + * This method is provided for clients, so they can disable camera connection and stop + * the delivery of frames even though the surface view itself is not destroyed and still stays on the scren + */ + public void disableView() { + synchronized(mSyncObject) { + mEnabled = false; + checkCurrentState(); + } + } + + public void setCvCameraViewListener(CvCameraViewListener listener) { + mListener = listener; + } + + /** + * This method sets the maximum size that camera frame is allowed to be. When selecting + * size - the biggest size which less or equal the size set will be selected. + * As an example - we set setMaxFrameSize(200,200) and we have 176x152 and 320x240 sizes. The + * preview frame will be selected with 176x152 size. + * This method is useful when need to restrict the size of preview frame for some reason (for example for video recording) + * @param maxWidth - the maximum width allowed for camera frame. + * @param maxHeight - the maximum height allowed for camera frame + */ + public void setMaxFrameSize(int maxWidth, int maxHeight) { + mMaxWidth = maxWidth; + mMaxHeight = maxHeight; + } + + public void SetCaptureFormat(int format) + { + mPreviewFormat = format; + } + + /** + * Called when mSyncObject lock is held + */ + private void checkCurrentState() { + int targetState; + + if (mEnabled && mSurfaceExist) { + targetState = STARTED; + } else { + targetState = STOPPED; + } + + if (targetState != mState) { + /* The state change detected. Need to exit the current state and enter target state */ + processExitState(mState); + mState = targetState; + processEnterState(mState); + } + } + + private void processEnterState(int state) { + switch(state) { + case STARTED: + onEnterStartedState(); + if (mListener != null) { + mListener.onCameraViewStarted(mFrameWidth, mFrameHeight); + } + break; + case STOPPED: + onEnterStoppedState(); + if (mListener != null) { + mListener.onCameraViewStopped(); + } + break; + }; + } + + private void processExitState(int state) { + switch(state) { + case STARTED: + onExitStartedState(); + break; + case STOPPED: + onExitStoppedState(); + break; + }; + } + + private void onEnterStoppedState() { + /* nothing to do */ + } + + private void onExitStoppedState() { + /* nothing to do */ + } + + // NOTE: The order of bitmap constructor and camera connection is important for android 4.1.x + // Bitmap must be constructed before surface + private void onEnterStartedState() { + /* Connect camera */ + if (!connectCamera(getWidth(), getHeight())) { + AlertDialog ad = new AlertDialog.Builder(getContext()).create(); + ad.setCancelable(false); // This blocks the 'BACK' button + ad.setMessage("It seems that you device does not support camera (or it is locked). Application will be closed."); + ad.setButton(DialogInterface.BUTTON_NEUTRAL, "OK", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + ((Activity) getContext()).finish(); + } + }); + ad.show(); + + } + } + + private void onExitStartedState() { + disconnectCamera(); + if (mCacheBitmap != null) { + mCacheBitmap.recycle(); + } + } + + /** + * This method shall be called by the subclasses when they have valid + * object and want it to be delivered to external client (via callback) and + * then displayed on the screen. + * @param frame - the current frame to be delivered + */ + protected void deliverAndDrawFrame(Mat frame) { + Mat modified; + + if (mListener != null) { + modified = mListener.onCameraFrame(frame); + } else { + modified = frame; + } + + boolean bmpValid = true; + if (modified != null) { + try { + Utils.matToBitmap(modified, mCacheBitmap); + } catch(Exception e) { + Log.e(TAG, "Mat type: " + modified); + Log.e(TAG, "Bitmap type: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight()); + Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage()); + bmpValid = false; + } + } + + if (bmpValid && mCacheBitmap != null) { + Canvas canvas = getHolder().lockCanvas(); + if (canvas != null) { + canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR); + canvas.drawBitmap(mCacheBitmap, (canvas.getWidth() - mCacheBitmap.getWidth()) / 2, (canvas.getHeight() - mCacheBitmap.getHeight()) / 2, null); + getHolder().unlockCanvasAndPost(canvas); + } + } + } + + /** + * This method is invoked shall perform concrete operation to initialize the camera. + * CONTRACT: as a result of this method variables mFrameWidth and mFrameHeight MUST be + * initialized with the size of the Camera frames that will be delivered to external processor. + * @param width - the width of this SurfaceView + * @param height - the height of this SurfaceView + */ + protected abstract boolean connectCamera(int width, int height); + + /** + * Disconnects and release the particular camera object being connected to this surface view. + * Called when syncObject lock is held + */ + protected abstract void disconnectCamera(); + + // NOTE: On Android 4.1.x the function must be called before SurfaceTextre constructor! + protected void AllocateCache() + { + mCacheBitmap = Bitmap.createBitmap(mFrameWidth, mFrameHeight, Bitmap.Config.ARGB_8888); + } + + public interface ListItemAccessor { + public int getWidth(Object obj); + public int getHeight(Object obj); + }; + + /** + * This helper method can be called by subclasses to select camera preview size. + * It goes over the list of the supported preview sizes and selects the maximum one which + * fits both values set via setMaxFrameSize() and surface frame allocated for this view + * @param supportedSizes + * @param surfaceWidth + * @param surfaceHeight + * @return + */ + protected Size calculateCameraFrameSize(List supportedSizes, ListItemAccessor accessor, int surfaceWidth, int surfaceHeight) { + int calcWidth = 0; + int calcHeight = 0; + + int maxAllowedWidth = (mMaxWidth != MAX_UNSPECIFIED && mMaxWidth < surfaceWidth)? mMaxWidth : surfaceWidth; + int maxAllowedHeight = (mMaxHeight != MAX_UNSPECIFIED && mMaxHeight < surfaceHeight)? mMaxHeight : surfaceHeight; + + for (Object size : supportedSizes) { + int width = accessor.getWidth(size); + int height = accessor.getHeight(size); + + if (width <= maxAllowedWidth && height <= maxAllowedHeight) { + if (width >= calcWidth && height >= calcHeight) { + calcWidth = (int) width; + calcHeight = (int) height; + } + } + } + + return new Size(calcWidth, calcHeight); + } +} diff --git a/modules/java/generator/src/java/android+JavaCameraView.java b/modules/java/generator/src/java/android+JavaCameraView.java new file mode 100644 index 0000000000..e3e80c61ca --- /dev/null +++ b/modules/java/generator/src/java/android+JavaCameraView.java @@ -0,0 +1,242 @@ +package org.opencv.android; + +import java.io.IOException; +import java.util.List; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.ImageFormat; +import android.graphics.SurfaceTexture; +import android.hardware.Camera; +import android.hardware.Camera.PreviewCallback; +import android.os.Build; +import android.util.AttributeSet; +import android.util.Log; +import android.view.SurfaceHolder; + +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.Size; +import org.opencv.highgui.Highgui; +import org.opencv.imgproc.Imgproc; + +/** + * This class is an implementation of the Bridge View between OpenCv and JAVA Camera. + * This class relays on the functionality available in base class and only implements + * required functions: + * connectCamera - opens Java camera and sets the PreviewCallback to be delivered. + * disconnectCamera - closes the camera and stops preview. + * When frame is delivered via callback from Camera - it processed via OpenCV to be + * converted to RGBA32 and then passed to the external callback for modifications if required. + */ +public class JavaCameraView extends CameraBridgeViewBase implements PreviewCallback { + + private static final int MAGIC_TEXTURE_ID = 10; + private static final String TAG = "JavaCameraView"; + + private Mat mBaseMat; + private byte mBuffer[]; + + private Thread mThread; + private boolean mStopThread; + + public static class JavaCameraSizeAccessor implements ListItemAccessor { + + public int getWidth(Object obj) { + Camera.Size size = (Camera.Size) obj; + return size.width; + } + + public int getHeight(Object obj) { + Camera.Size size = (Camera.Size) obj; + return size.height; + } + } + + private Camera mCamera; + + public JavaCameraView(Context context, AttributeSet attrs) { + super(context, attrs); + Log.d(TAG, "Java camera view ctor"); + } + + @TargetApi(11) + protected boolean initializeCamera(int width, int height) { + Log.d(TAG, "Initialize java camera"); + synchronized (this) { + mCamera = null; + + Log.d(TAG, "Trying to open camera with old open()"); + try { + mCamera = Camera.open(); + } + catch (Exception e){ + Log.e(TAG, "Camera is not available (in use or does not exist): " + e.getLocalizedMessage()); + } + + if(mCamera == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { + boolean connected = false; + for (int camIdx = 0; camIdx < Camera.getNumberOfCameras(); ++camIdx) { + Log.d(TAG, "Trying to open camera with new open(" + Integer.valueOf(camIdx) + ")"); + try { + mCamera = Camera.open(camIdx); + connected = true; + } catch (RuntimeException e) { + Log.e(TAG, "Camera #" + camIdx + "failed to open: " + e.getLocalizedMessage()); + } + if (connected) break; + } + } + + if (mCamera == null) + return false; + + /* Now set camera parameters */ + try { + Camera.Parameters params = mCamera.getParameters(); + Log.d(TAG, "getSupportedPreviewSizes()"); + List sizes = params.getSupportedPreviewSizes(); + + /* Select the size that fits surface considering maximum size allowed */ + Size frameSize = calculateCameraFrameSize(sizes, new JavaCameraSizeAccessor(), width, height); + + params.setPreviewFormat(ImageFormat.NV21); + params.setPreviewSize((int)frameSize.width, (int)frameSize.height); + + List FocusModes = params.getSupportedFocusModes(); + if (FocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) + { + params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); + } + + mCamera.setParameters(params); + params = mCamera.getParameters(); + + mFrameWidth = params.getPreviewSize().width; + mFrameHeight = params.getPreviewSize().height; + + int size = mFrameWidth * mFrameHeight; + size = size * ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8; + mBuffer = new byte[size]; + + mCamera.addCallbackBuffer(mBuffer); + mCamera.setPreviewCallbackWithBuffer(this); + + mBaseMat = new Mat(mFrameHeight + (mFrameHeight/2), mFrameWidth, CvType.CV_8UC1); + + AllocateCache(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + SurfaceTexture tex = new SurfaceTexture(MAGIC_TEXTURE_ID); + getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); + mCamera.setPreviewTexture(tex); + } else + mCamera.setPreviewDisplay(null); + } catch (IOException e) { + e.printStackTrace(); + } + + /* Finally we are ready to start the preview */ + Log.d(TAG, "startPreview"); + mCamera.startPreview(); + } + + return true; + } + + protected void releaseCamera() { + synchronized (this) { + mCamera.stopPreview(); + mCamera.release(); + mCamera = null; + } + } + + @Override + protected boolean connectCamera(int width, int height) { + + /* 1. We need to instantiate camera + * 2. We need to start thread which will be getting frames + */ + /* First step - initialize camera connection */ + Log.d(TAG, "Connecting to camera"); + if (!initializeCamera(getWidth(), getHeight())) + return false; + + /* now we can start update thread */ + Log.d(TAG, "Starting processing thread"); + mStopThread = false; + mThread = new Thread(new CameraWorker()); + mThread.start(); + + return true; + } + + protected void disconnectCamera() { + /* 1. We need to stop thread which updating the frames + * 2. Stop camera and release it + */ + Log.d(TAG, "Disconnecting from camera"); + try { + mStopThread = true; + Log.d(TAG, "Notify thread"); + synchronized (this) { + this.notify(); + } + Log.d(TAG, "Wating for thread"); + mThread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + mThread = null; + } + + /* Now release camera */ + releaseCamera(); + } + + public void onPreviewFrame(byte[] frame, Camera arg1) { + Log.i(TAG, "Preview Frame received. Need to create MAT and deliver it to clients"); + Log.i(TAG, "Frame size is " + frame.length); + synchronized (this) + { + mBaseMat.put(0, 0, frame); + this.notify(); + } + if (mCamera != null) + mCamera.addCallbackBuffer(mBuffer); + } + + private class CameraWorker implements Runnable { + + public void run() { + do { + synchronized (JavaCameraView.this) { + try { + JavaCameraView.this.wait(); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + if (!mStopThread) { + Mat frameMat = new Mat(); + switch (mPreviewFormat) { + case Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA: + Imgproc.cvtColor(mBaseMat, frameMat, Imgproc.COLOR_YUV2RGBA_NV21, 4); + break; + case Highgui.CV_CAP_ANDROID_GREY_FRAME: + frameMat = mBaseMat.submat(0, mFrameHeight, 0, mFrameWidth); + break; + default: + Log.e(TAG, "Invalid frame format! Only RGBA and Gray Scale are supported!"); + }; + deliverAndDrawFrame(frameMat); + frameMat.release(); + } + } while (!mStopThread); + Log.d(TAG, "Finish processing thread"); + } + } +} diff --git a/modules/java/generator/src/java/android+NativeCameraView.java b/modules/java/generator/src/java/android+NativeCameraView.java new file mode 100644 index 0000000000..a62d6bb5c7 --- /dev/null +++ b/modules/java/generator/src/java/android+NativeCameraView.java @@ -0,0 +1,145 @@ +package org.opencv.android; + +import org.opencv.core.Mat; +import org.opencv.core.Size; +import org.opencv.highgui.Highgui; +import org.opencv.highgui.VideoCapture; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; + +/** + * This class is an implementation of a bridge between SurfaceView and native OpenCV camera. + * Due to the big amount of work done, by the base class this child is only responsible + * for creating camera, destroying camera and delivering frames while camera is enabled + */ +public class NativeCameraView extends CameraBridgeViewBase { + + public static final String TAG = "NativeCameraView"; + private boolean mStopThread; + private Thread mThread; + private VideoCapture mCamera; + + public NativeCameraView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected boolean connectCamera(int width, int height) { + + /* 1. We need to instantiate camera + * 2. We need to start thread which will be getting frames + */ + /* First step - initialize camera connection */ + if (!initializeCamera(getWidth(), getHeight())) + return false; + + /* now we can start update thread */ + mThread = new Thread(new CameraWorker()); + mThread.start(); + + return true; + } + + @Override + protected void disconnectCamera() { + /* 1. We need to stop thread which updating the frames + * 2. Stop camera and release it + */ + try { + mStopThread = true; + mThread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + mThread = null; + mStopThread = false; + } + + /* Now release camera */ + releaseCamera(); + } + + public static class OpenCvSizeAccessor implements ListItemAccessor { + + public int getWidth(Object obj) { + Size size = (Size)obj; + return (int)size.width; + } + + public int getHeight(Object obj) { + Size size = (Size)obj; + return (int)size.height; + } + + } + + private boolean initializeCamera(int width, int height) { + synchronized (this) { + mCamera = new VideoCapture(Highgui.CV_CAP_ANDROID); + + if (mCamera == null) + return false; + + //TODO: improve error handling + + java.util.List sizes = mCamera.getSupportedPreviewSizes(); + + /* Select the size that fits surface considering maximum size allowed */ + Size frameSize = calculateCameraFrameSize(sizes, new OpenCvSizeAccessor(), width, height); + + mFrameWidth = (int)frameSize.width; + mFrameHeight = (int)frameSize.height; + + AllocateCache(); + + mCamera.set(Highgui.CV_CAP_PROP_FRAME_WIDTH, frameSize.width); + mCamera.set(Highgui.CV_CAP_PROP_FRAME_HEIGHT, frameSize.height); + } + + Log.i(TAG, "Selected camera frame size = (" + mFrameWidth + ", " + mFrameHeight + ")"); + + return true; + } + + private void releaseCamera() { + synchronized (this) { + if (mCamera != null) { + mCamera.release(); + } + } + } + + private class CameraWorker implements Runnable { + + private Mat mRgba = new Mat(); + private Mat mGray = new Mat(); + + public void run() { + do { + if (!mCamera.grab()) { + Log.e(TAG, "Camera frame grab failed"); + break; + } + + switch (mPreviewFormat) { + case Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA: + { + mCamera.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA); + deliverAndDrawFrame(mRgba); + } break; + case Highgui.CV_CAP_ANDROID_GREY_FRAME: + mCamera.retrieve(mGray, Highgui.CV_CAP_ANDROID_GREY_FRAME); + deliverAndDrawFrame(mGray); + break; + default: + Log.e(TAG, "Invalid frame format! Only RGBA and Gray Scale are supported!"); + } + + } while (!mStopThread); + + } + } + +} diff --git a/samples/android/15-puzzle/.classpath b/samples/android/15-puzzle/.classpath index a4763d1eec..3f9691c5dd 100644 --- a/samples/android/15-puzzle/.classpath +++ b/samples/android/15-puzzle/.classpath @@ -1,8 +1,8 @@ - - + + diff --git a/samples/android/15-puzzle/.project b/samples/android/15-puzzle/.project index fdb9a40c42..d9387703f4 100644 --- a/samples/android/15-puzzle/.project +++ b/samples/android/15-puzzle/.project @@ -1,6 +1,6 @@ - OpenCV Sample - 15-puzzle + 15-puzzle diff --git a/samples/android/15-puzzle/.settings/org.eclipse.jdt.core.prefs b/samples/android/15-puzzle/.settings/org.eclipse.jdt.core.prefs index 53e0495157..f77b31c2d2 100644 --- a/samples/android/15-puzzle/.settings/org.eclipse.jdt.core.prefs +++ b/samples/android/15-puzzle/.settings/org.eclipse.jdt.core.prefs @@ -1,4 +1,3 @@ -#Wed Jun 29 04:36:40 MSD 2011 eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 org.eclipse.jdt.core.compiler.compliance=1.5 diff --git a/samples/android/15-puzzle/AndroidManifest.xml b/samples/android/15-puzzle/AndroidManifest.xml index 5c60b62a4c..184d3d761e 100644 --- a/samples/android/15-puzzle/AndroidManifest.xml +++ b/samples/android/15-puzzle/AndroidManifest.xml @@ -1,29 +1,28 @@ - - - - + package="org.opencv.samples.puzzle15" + android:versionCode="1" + android:versionName="1.0" > + + + + + + + + - - - - diff --git a/samples/android/15-puzzle/res/layout/activity_puzzle15.xml b/samples/android/15-puzzle/res/layout/activity_puzzle15.xml new file mode 100644 index 0000000000..3257ed801c --- /dev/null +++ b/samples/android/15-puzzle/res/layout/activity_puzzle15.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/samples/android/15-puzzle/res/menu/activity_puzzle15.xml b/samples/android/15-puzzle/res/menu/activity_puzzle15.xml new file mode 100644 index 0000000000..7810d81963 --- /dev/null +++ b/samples/android/15-puzzle/res/menu/activity_puzzle15.xml @@ -0,0 +1,6 @@ + + + + diff --git a/samples/android/15-puzzle/res/values/strings.xml b/samples/android/15-puzzle/res/values/strings.xml index 1261ead44e..1421a45051 100644 --- a/samples/android/15-puzzle/res/values/strings.xml +++ b/samples/android/15-puzzle/res/values/strings.xml @@ -1,4 +1,6 @@ - OCV 15 Puzzle - + Show/hide tile numbers + Start new game + + \ No newline at end of file diff --git a/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/Puzzle15Activity.java b/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/Puzzle15Activity.java new file mode 100644 index 0000000000..e72dbbaee3 --- /dev/null +++ b/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/Puzzle15Activity.java @@ -0,0 +1,130 @@ +package org.opencv.samples.puzzle15; + +import org.opencv.android.BaseLoaderCallback; +import org.opencv.android.LoaderCallbackInterface; +import org.opencv.android.OpenCVLoader; +import org.opencv.core.Mat; +import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener; +import org.opencv.android.JavaCameraView; + +import android.os.Bundle; +import android.app.Activity; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.Window; +import android.view.View; + +public class Puzzle15Activity extends Activity implements CvCameraViewListener, View.OnTouchListener { + + private static final String TAG = "Sample::Puzzle15::Activity"; + + private JavaCameraView mOpenCvCameraView; + private Puzzle15Processor mPuzzle15; + + private int mGameWidth; + private int mGameHeight; + + private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { + + @Override + public void onManagerConnected(int status) { + switch (status) { + case LoaderCallbackInterface.SUCCESS: + { + Log.i(TAG, "OpenCV loaded successfully"); + + /* Now enable camera view to start receiving frames */ + mOpenCvCameraView.setOnTouchListener(Puzzle15Activity.this); + mOpenCvCameraView.enableView(); + } break; + default: + { + super.onManagerConnected(status); + } break; + } + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + + setContentView(R.layout.activity_puzzle15); + + mOpenCvCameraView = (JavaCameraView) findViewById(R.id.puzzle_activity_surface_view); + mOpenCvCameraView.setCvCameraViewListener(this); + mPuzzle15 = new Puzzle15Processor(); + mPuzzle15.prepareNewGame(); + } + + @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(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.activity_puzzle15, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + Log.i(TAG, "Menu Item selected " + item); + if (item.getItemId() == R.id.menu_start_new_game) { + /* We need to start new game */ + mPuzzle15.prepareNewGame(); + } else if (item.getItemId() == R.id.menu_toggle_tile_numbers) { + /* We need to enable or disable drawing of the tile numbers */ + mPuzzle15.toggleTileNumbers(); + } + return true; + } + + public void onCameraViewStarted(int width, int height) { + mGameWidth = width; + mGameHeight = height; + mPuzzle15.prepareGameSize(width, height); + } + + public void onCameraViewStopped() { + } + + public Mat onCameraFrame(Mat inputFrame) { + return mPuzzle15.puzzleFrame(inputFrame); + } + + public boolean onTouch(View view, MotionEvent event) { + int xpos, ypos; + + xpos = (view.getWidth() - mGameWidth) / 2; + xpos = (int)event.getX() - xpos; + + ypos = (view.getHeight() - mGameHeight) / 2; + ypos = (int)event.getY() - ypos; + + if (xpos >=0 && xpos <= mGameWidth && ypos >=0 && ypos <= mGameHeight) { + /* click is inside the picture. Deliver this event to processor */ + mPuzzle15.deliverTouchEvent(xpos, ypos); + } + + return false; + } +} diff --git a/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/Puzzle15Processor.java b/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/Puzzle15Processor.java new file mode 100644 index 0000000000..bc2f042b42 --- /dev/null +++ b/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/Puzzle15Processor.java @@ -0,0 +1,193 @@ +package org.opencv.samples.puzzle15; + +import org.opencv.core.Core; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.Scalar; +import org.opencv.core.Size; +import org.opencv.core.Point; + +import android.util.Log; + + +/** + * This class is a controller for puzzle game. + * It converts the image from Camera into the shuffled image + */ +public class Puzzle15Processor { + + private static final int GRID_SIZE = 4; + private static final int GRID_AREA = GRID_SIZE * GRID_SIZE; + private static final int GRID_EMPTY_INDEX = GRID_AREA - 1; + private static final String TAG = "Puzzle15Processor"; + + private int[] mIndexes; + private int[] mTextWidths; + private int[] mTextHeights; + + private Mat mRgba15; + + private Mat[] mCells; + private Mat[] mCells15; + private boolean mShowTileNumbers = true; + + public Puzzle15Processor() { + mTextWidths = new int[GRID_AREA]; + mTextHeights = new int[GRID_AREA]; + + mIndexes = new int [GRID_AREA]; + + for (int i = 0; i < GRID_AREA; i++) + mIndexes[i] = i; + } + + /* this method is intended to make processor prepared for a new game */ + public synchronized void prepareNewGame() { + do { + shuffle(mIndexes); + } while (!isPuzzleSolvable()); + } + + /* This method is to make the processor know the size of the frames that + * will be delivered via puzzleFrame. + * If the frames will be different size - then the result is unpredictable + */ + public synchronized void prepareGameSize(int width, int height) { + mRgba15 = new Mat(height, width, CvType.CV_8UC4); + + mCells = new Mat[GRID_AREA]; + mCells15 = new Mat[GRID_AREA]; + + for (int i = 0; i < GRID_SIZE; i++) { + for (int j = 0; j < GRID_SIZE; j++) { + int k = i * GRID_SIZE + j; + mCells15[k] = mRgba15.submat(i * height / GRID_SIZE, (i + 1) * height / GRID_SIZE, j * width / GRID_SIZE, (j + 1) * width / GRID_SIZE); + } + } + + for (int i = 0; i < GRID_AREA; i++) { + Size s = Core.getTextSize(Integer.toString(i + 1), 3/* CV_FONT_HERSHEY_COMPLEX */, 1, 2, null); + mTextHeights[i] = (int) s.height; + mTextWidths[i] = (int) s.width; + } + } + + /* this method to be called from the outside. it processes the frame and shuffles + * the tiles as specified by mIndexes array + */ + public synchronized Mat puzzleFrame(Mat inputPicture) { + int rows = inputPicture.rows(); + int cols = inputPicture.cols(); + + rows = rows - rows%4; + cols = cols - cols%4; + + for (int i = 0; i < GRID_SIZE; i++) { + for (int j = 0; j < GRID_SIZE; j++) { + int k = i * GRID_SIZE + j; + mCells[k] = inputPicture.submat(i * inputPicture.rows() / GRID_SIZE, (i + 1) * inputPicture.rows() / GRID_SIZE, j * inputPicture.cols()/ GRID_SIZE, (j + 1) * inputPicture.cols() / GRID_SIZE); + } + } + + rows = rows - rows%4; + cols = cols - cols%4; + + // copy shuffled tiles + for (int i = 0; i < GRID_AREA; i++) { + int idx = mIndexes[i]; + if (idx == GRID_EMPTY_INDEX) + mCells15[i].setTo(new Scalar(0x33, 0x33, 0x33, 0xFF)); + else { + mCells[idx].copyTo(mCells15[i]); + if (mShowTileNumbers) { + Core.putText(mCells15[i], Integer.toString(1 + idx), new Point((cols / GRID_SIZE - mTextWidths[idx]) / 2, + (rows / GRID_SIZE + mTextHeights[idx]) / 2), 3/* CV_FONT_HERSHEY_COMPLEX */, 1, new Scalar(255, 0, 0, 255), 2); + } + } + } + + drawGrid(cols, rows, mRgba15); + + return mRgba15; + } + + public void toggleTileNumbers() { + mShowTileNumbers = !mShowTileNumbers; + } + + public void deliverTouchEvent(int x, int y) { + int rows = mRgba15.rows(); + int cols = mRgba15.cols(); + + int row = (int) Math.floor(y * GRID_SIZE / rows); + int col = (int) Math.floor(x * GRID_SIZE / cols); + + if (row < 0 || row >= GRID_SIZE || col < 0 || col >= GRID_SIZE) { + Log.e(TAG, "It is not expected to get touch event outside of picture"); + return ; + } + + int idx = row * GRID_SIZE + col; + int idxtoswap = -1; + + // left + if (idxtoswap < 0 && col > 0) + if (mIndexes[idx - 1] == GRID_EMPTY_INDEX) + idxtoswap = idx - 1; + // right + if (idxtoswap < 0 && col < GRID_SIZE - 1) + if (mIndexes[idx + 1] == GRID_EMPTY_INDEX) + idxtoswap = idx + 1; + // top + if (idxtoswap < 0 && row > 0) + if (mIndexes[idx - GRID_SIZE] == GRID_EMPTY_INDEX) + idxtoswap = idx - GRID_SIZE; + // bottom + if (idxtoswap < 0 && row < GRID_SIZE - 1) + if (mIndexes[idx + GRID_SIZE] == GRID_EMPTY_INDEX) + idxtoswap = idx + GRID_SIZE; + + // swap + if (idxtoswap >= 0) { + synchronized (this) { + int touched = mIndexes[idx]; + mIndexes[idx] = mIndexes[idxtoswap]; + mIndexes[idxtoswap] = touched; + } + } + } + + private void drawGrid(int cols, int rows, Mat drawMat) { + for (int i = 1; i < GRID_SIZE; i++) { + Core.line(drawMat, new Point(0, i * rows / GRID_SIZE), new Point(cols, i * rows / GRID_SIZE), new Scalar(0, 255, 0, 255), 3); + Core.line(drawMat, new Point(i * cols / GRID_SIZE, 0), new Point(i * cols / GRID_SIZE, rows), new Scalar(0, 255, 0, 255), 3); + } + } + + private static void shuffle(int[] array) { + for (int i = array.length; i > 1; i--) { + int temp = array[i - 1]; + int randIx = (int) (Math.random() * i); + array[i - 1] = array[randIx]; + array[randIx] = temp; + } + } + + private boolean isPuzzleSolvable() { + + int sum = 0; + for (int i = 0; i < GRID_AREA; i++) { + if (mIndexes[i] == GRID_EMPTY_INDEX) + sum += (i / GRID_SIZE) + 1; + else { + int smaller = 0; + for (int j = i + 1; j < GRID_AREA; j++) { + if (mIndexes[j] < mIndexes[i]) + smaller++; + } + sum += smaller; + } + } + return sum % 2 == 0; + } +} diff --git a/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/SampleCvViewBase.java b/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/SampleCvViewBase.java deleted file mode 100644 index 96020c3601..0000000000 --- a/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/SampleCvViewBase.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.opencv.samples.puzzle15; - -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; - - public SampleCvViewBase(Context context) { - super(context); - mHolder = getHolder(); - mHolder.addCallback(this); - 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"); - while (true) { - Bitmap bmp = null; - - synchronized (this) { - if (mCamera == null) - break; - - if (!mCamera.grab()) { - Log.e(TAG, "mCamera.grab() failed"); - break; - } - - bmp = processFrame(mCamera); - } - - 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); - mHolder.unlockCanvasAndPost(canvas); - } - bmp.recycle(); - } - } - Log.i(TAG, "Finished processing thread"); - } -} \ No newline at end of file diff --git a/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/puzzle15Activity.java b/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/puzzle15Activity.java deleted file mode 100644 index 96ef507767..0000000000 --- a/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/puzzle15Activity.java +++ /dev/null @@ -1,123 +0,0 @@ -package org.opencv.samples.puzzle15; - -import org.opencv.android.BaseLoaderCallback; -import org.opencv.android.LoaderCallbackInterface; -import org.opencv.android.OpenCVLoader; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.DialogInterface; -import android.os.Bundle; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.Window; -import android.view.WindowManager; - -/** Activity class implements LoaderCallbackInterface to handle OpenCV initialization status **/ -public class puzzle15Activity extends Activity { - private static final String TAG = "OCVSample::Activity"; - - private MenuItem mItemNewGame; - private MenuItem mItemToggleNumbers; - private puzzle15View mView = null; - - private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) { - @Override - public void onManagerConnected(int status) { - switch (status) { - case LoaderCallbackInterface.SUCCESS: - { - Log.i(TAG, "OpenCV loaded successfully"); - // Create and set View - mView = new puzzle15View(mAppContext); - setContentView(mView); - - // 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(); - } - }); - MarketErrorMessage.show(); - } break; - default: - { - super.onManagerConnected(status); - } break; - } - } - }; - - public puzzle15Activity() { - 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) { - Log.i(TAG, "called onCreate"); - super.onCreate(savedInstanceState); - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - Log.i(TAG, "called onCreateOptionsMenu"); - mItemNewGame = menu.add("Start new game"); - mItemToggleNumbers = menu.add("Show/hide tile numbers"); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - Log.i(TAG, "called onOptionsItemSelected; selected item: " + item); - if (item == mItemNewGame) - mView.startNewGame(); - else if (item == mItemToggleNumbers) - mView.tolggleTileNumbers(); - return true; - } -} \ No newline at end of file diff --git a/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/puzzle15View.java b/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/puzzle15View.java deleted file mode 100644 index 169fd69567..0000000000 --- a/samples/android/15-puzzle/src/org/opencv/samples/puzzle15/puzzle15View.java +++ /dev/null @@ -1,250 +0,0 @@ -package org.opencv.samples.puzzle15; - -import org.opencv.android.Utils; -import org.opencv.core.Core; -import org.opencv.core.Mat; -import org.opencv.core.Size; -import org.opencv.core.Point; -import org.opencv.core.Scalar; -import org.opencv.highgui.Highgui; -import org.opencv.highgui.VideoCapture; - -import android.content.Context; -import android.graphics.Bitmap; -import android.util.Log; -import android.view.MotionEvent; -import android.view.SurfaceHolder; -import android.view.View; -import android.view.View.OnTouchListener; - -public class puzzle15View extends SampleCvViewBase implements OnTouchListener { - private static final String TAG = "OCVSample::View"; - - private Mat mRgba; - private Mat mRgba15; - private Mat[] mCells; - private Mat[] mCells15; - private int[] mIndexses; - private int[] mTextWidths; - private int[] mTextHeights; - private boolean mShowTileNumbers = true; - - int gridSize = 4; - int gridArea = gridSize * gridSize; - int gridEmptyIdx = gridArea - 1; - - public puzzle15View(Context context) { - super(context); - setOnTouchListener(this); - - mTextWidths = new int[gridArea]; - mTextHeights = new int[gridArea]; - for (int i = 0; i < gridArea; i++) { - Size s = Core.getTextSize(Integer.toString(i + 1), 3/* CV_FONT_HERSHEY_COMPLEX */, 1, 2, null); - mTextHeights[i] = (int) s.height; - mTextWidths[i] = (int) s.width; - } - Log.i(TAG, "Instantiated new " + this.getClass()); - } - - @Override - public void surfaceCreated(SurfaceHolder holder) { - Log.i(TAG, "called surfaceCreated"); - synchronized (this) { - // initialize Mat before usage - mRgba = new Mat(); - } - - super.surfaceCreated(holder); - } - - public static void shuffle(int[] array) { - for (int i = array.length; i > 1; i--) { - int temp = array[i - 1]; - int randIx = (int) (Math.random() * i); - array[i - 1] = array[randIx]; - array[randIx] = temp; - } - } - - public boolean isPuzzleSolvable() { - if (gridSize != 4) - return true; - - int sum = 0; - for (int i = 0; i < gridArea; i++) { - if (mIndexses[i] == gridEmptyIdx) - sum += (i / gridSize) + 1; - else { - int smaller = 0; - for (int j = i + 1; j < gridArea; j++) { - if (mIndexses[j] < mIndexses[i]) - smaller++; - } - sum += smaller; - } - } - - return sum % 2 == 0; - } - - private void createPuzzle(int cols, int rows, int type) { - mCells = new Mat[gridArea]; - mCells15 = new Mat[gridArea]; - - mRgba15 = new Mat(rows, cols, type); - mIndexses = new int[gridArea]; - - for (int i = 0; i < gridSize; i++) { - for (int j = 0; j < gridSize; j++) { - int k = i * gridSize + j; - mIndexses[k] = k; - mCells[k] = mRgba.submat(i * rows / gridSize, (i + 1) * rows / gridSize, j * cols / gridSize, (j + 1) * cols / gridSize); - mCells15[k] = mRgba15.submat(i * rows / gridSize, (i + 1) * rows / gridSize, j * cols / gridSize, (j + 1) * cols / gridSize); - } - } - - startNewGame(); - } - - private void drawGrid(int cols, int rows) { - for (int i = 1; i < gridSize; i++) { - Core.line(mRgba15, new Point(0, i * rows / gridSize), new Point(cols, i * rows / gridSize), new Scalar(0, 255, 0, 255), 3); - Core.line(mRgba15, new Point(i * cols / gridSize, 0), new Point(i * cols / gridSize, rows), new Scalar(0, 255, 0, 255), 3); - } - } - - public synchronized void startNewGame() { - do { - shuffle(mIndexses); - } while (!isPuzzleSolvable()); - } - - public void tolggleTileNumbers() { - mShowTileNumbers = !mShowTileNumbers; - } - - @Override - protected Bitmap processFrame(VideoCapture capture) { - capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA); - int cols = mRgba.cols(); - int rows = mRgba.rows(); - - rows = rows - rows%4; - cols = cols - cols%4; - - if (mCells == null) - createPuzzle(cols, rows, mRgba.type()); - else if(mRgba15.cols() != cols || mRgba15.rows() != rows) { - releaseMats(); - createPuzzle(cols, rows, mRgba.type()); - } - - // copy shuffled tiles - for (int i = 0; i < gridArea; i++) { - int idx = mIndexses[i]; - if (idx == gridEmptyIdx) - mCells15[i].setTo(new Scalar(0x33, 0x33, 0x33, 0xFF)); - else { - mCells[idx].copyTo(mCells15[i]); - if (mShowTileNumbers) { - Core.putText(mCells15[i], Integer.toString(1 + idx), new Point((cols / gridSize - mTextWidths[idx]) / 2, - (rows / gridSize + mTextHeights[idx]) / 2), 3/* CV_FONT_HERSHEY_COMPLEX */, 1, new Scalar(255, 0, 0, 255), 2); - } - } - } - - drawGrid(cols, rows); - Bitmap bmp = Bitmap.createBitmap(cols, rows, Bitmap.Config.ARGB_8888); - try { - Utils.matToBitmap(mRgba15, bmp); - return bmp; - } catch(Exception e) { - Log.e("org.opencv.samples.puzzle15", "Utils.matToBitmap() throws an exception: " + e.getMessage()); - bmp.recycle(); - return null; - } - } - - @Override - public void run() { - super.run(); - - synchronized (this) { - releaseMats(); - - if (mRgba != null) - mRgba.release(); - mRgba = null; - } - } - - private void releaseMats() { - // Explicitly deallocate Mats - if (mCells != null) { - for (Mat m : mCells) - m.release(); - } - if (mCells15 != null) { - for (Mat m : mCells15) - m.release(); - } - - if (mRgba15 != null) - mRgba15.release(); - - mRgba15 = null; - mCells = null; - mCells15 = null; - mIndexses = null; - } - - public boolean onTouch(View v, MotionEvent event) { - if(mRgba==null) return false; - - int cols = mRgba.cols(); - int rows = mRgba.rows(); - float xoffset = (getWidth() - cols) / 2; - float yoffset = (getHeight() - rows) / 2; - - float x = event.getX() - xoffset; - float y = event.getY() - yoffset; - - int row = (int) Math.floor(y * gridSize / rows); - int col = (int) Math.floor(x * gridSize / cols); - - if (row < 0 || row >= gridSize || col < 0 || col >= gridSize) - return false; - - int idx = row * gridSize + col; - int idxtoswap = -1; - - // left - if (idxtoswap < 0 && col > 0) - if (mIndexses[idx - 1] == gridEmptyIdx) - idxtoswap = idx - 1; - // right - if (idxtoswap < 0 && col < gridSize - 1) - if (mIndexses[idx + 1] == gridEmptyIdx) - idxtoswap = idx + 1; - // top - if (idxtoswap < 0 && row > 0) - if (mIndexses[idx - gridSize] == gridEmptyIdx) - idxtoswap = idx - gridSize; - // bottom - if (idxtoswap < 0 && row < gridSize - 1) - if (mIndexses[idx + gridSize] == gridEmptyIdx) - idxtoswap = idx + gridSize; - - // swap - if (idxtoswap >= 0) { - synchronized (this) { - int touched = mIndexses[idx]; - mIndexses[idx] = mIndexses[idxtoswap]; - mIndexses[idxtoswap] = touched; - } - } - - return false;// don't need subsequent touch events - } -} diff --git a/samples/android/color-blob-detection/.classpath b/samples/android/color-blob-detection/.classpath index a4763d1eec..3f9691c5dd 100644 --- a/samples/android/color-blob-detection/.classpath +++ b/samples/android/color-blob-detection/.classpath @@ -1,8 +1,8 @@ - - + + diff --git a/samples/android/color-blob-detection/res/layout/color_blob_detection_surface_view.xml b/samples/android/color-blob-detection/res/layout/color_blob_detection_surface_view.xml new file mode 100644 index 0000000000..94531a60dc --- /dev/null +++ b/samples/android/color-blob-detection/res/layout/color_blob_detection_surface_view.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/samples/android/color-blob-detection/src/org/opencv/samples/colorblobdetect/ColorBlobDetectionActivity.java b/samples/android/color-blob-detection/src/org/opencv/samples/colorblobdetect/ColorBlobDetectionActivity.java index 2ee10135f7..d772bb8aa7 100644 --- a/samples/android/color-blob-detection/src/org/opencv/samples/colorblobdetect/ColorBlobDetectionActivity.java +++ b/samples/android/color-blob-detection/src/org/opencv/samples/colorblobdetect/ColorBlobDetectionActivity.java @@ -1,62 +1,53 @@ package org.opencv.samples.colorblobdetect; +import java.util.List; + import org.opencv.android.BaseLoaderCallback; 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.MatOfPoint; +import org.opencv.core.Rect; +import org.opencv.core.Scalar; +import org.opencv.core.Size; +import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener; +import org.opencv.android.JavaCameraView; +import org.opencv.imgproc.Imgproc; import android.app.Activity; -import android.app.AlertDialog; -import android.content.DialogInterface; import android.os.Bundle; import android.util.Log; +import android.view.MotionEvent; +import android.view.View; import android.view.Window; import android.view.WindowManager; +import android.view.View.OnTouchListener; + +public class ColorBlobDetectionActivity extends Activity implements OnTouchListener, CvCameraViewListener { + private static final String TAG = "OCVSample::Activity"; -public class ColorBlobDetectionActivity extends Activity { - private static final String TAG = "OCVSample::Activity"; + private boolean mIsColorSelected = false; + private Mat mRgba; + private Scalar mBlobColorRgba; + private Scalar mBlobColorHsv; + private ColorBlobDetector mDetector; + private Mat mSpectrum; + private Size SPECTRUM_SIZE; + private Scalar CONTOUR_COLOR; - private ColorBlobDetectionView mView; + private JavaCameraView mOpenCvCameraView; - private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) { + private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { @Override public void onManagerConnected(int status) { switch (status) { case LoaderCallbackInterface.SUCCESS: { Log.i(TAG, "OpenCV loaded successfully"); - // Create and set View - mView = new ColorBlobDetectionView(mAppContext); - setContentView(mView); - - // 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(); - } - }); - MarketErrorMessage.show(); + mOpenCvCameraView.enableView(); + mOpenCvCameraView.setOnTouchListener(ColorBlobDetectionActivity.this); } break; default: { @@ -70,31 +61,124 @@ public class ColorBlobDetectionActivity extends Activity { Log.i(TAG, "Instantiated new " + this.getClass()); } + /** Called when the activity is first created. */ @Override - protected void onPause() { - Log.i(TAG, "called onPause"); - if (null != mView) - mView.releaseCamera(); + public void onCreate(Bundle savedInstanceState) { + Log.i(TAG, "called onCreate"); + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + setContentView(R.layout.color_blob_detection_surface_view); + + mOpenCvCameraView = (JavaCameraView)findViewById(R.id.color_blob_detection_activity_surface_view); + mOpenCvCameraView.setCvCameraViewListener(this); + } + + @Override + public void onPause() + { + mOpenCvCameraView.disableView(); super.onPause(); } @Override - protected void onResume() { - Log.i(TAG, "called onResume"); + 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) { + mRgba = new Mat(height, width, CvType.CV_8UC4); + mDetector = new ColorBlobDetector(); + mSpectrum = new Mat(); + mBlobColorRgba = new Scalar(255); + mBlobColorHsv = new Scalar(255); + SPECTRUM_SIZE = new Size(200, 32); + CONTOUR_COLOR = new Scalar(255,0,0,255); + } + + public void onCameraViewStopped() { + mRgba.release(); + } + + public boolean onTouch(View v, MotionEvent event) { + int cols = mRgba.cols(); + int rows = mRgba.rows(); + + int xOffset = (mOpenCvCameraView.getWidth() - cols) / 2; + int yOffset = (mOpenCvCameraView.getHeight() - rows) / 2; + + int x = (int)event.getX() - xOffset; + int y = (int)event.getY() - yOffset; + + Log.i(TAG, "Touch image coordinates: (" + x + ", " + y + ")"); - 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"); + if ((x < 0) || (y < 0) || (x > cols) || (y > rows)) return false; + + Rect touchedRect = new Rect(); + + touchedRect.x = (x>4) ? x-4 : 0; + touchedRect.y = (y>4) ? y-4 : 0; + + touchedRect.width = (x+4 < cols) ? x + 4 - touchedRect.x : cols - touchedRect.x; + touchedRect.height = (y+4 < rows) ? y + 4 - touchedRect.y : rows - touchedRect.y; + + Mat touchedRegionRgba = mRgba.submat(touchedRect); + + Mat touchedRegionHsv = new Mat(); + Imgproc.cvtColor(touchedRegionRgba, touchedRegionHsv, Imgproc.COLOR_RGB2HSV_FULL); + + // Calculate average color of touched region + mBlobColorHsv = Core.sumElems(touchedRegionHsv); + int pointCount = touchedRect.width*touchedRect.height; + for (int i = 0; i < mBlobColorHsv.val.length; i++) + mBlobColorHsv.val[i] /= pointCount; + + mBlobColorRgba = converScalarHsv2Rgba(mBlobColorHsv); + + Log.i(TAG, "Touched rgba color: (" + mBlobColorRgba.val[0] + ", " + mBlobColorRgba.val[1] + + ", " + mBlobColorRgba.val[2] + ", " + mBlobColorRgba.val[3] + ")"); + + mDetector.setHsvColor(mBlobColorHsv); + + Imgproc.resize(mDetector.getSpectrum(), mSpectrum, SPECTRUM_SIZE); + + mIsColorSelected = true; + + return false; // don't need subsequent touch events + } + + public Mat onCameraFrame(Mat inputFrame) { + inputFrame.copyTo(mRgba); + + if (mIsColorSelected) { + mDetector.process(mRgba); + List contours = mDetector.getContours(); + Log.e(TAG, "Contours count: " + contours.size()); + Imgproc.drawContours(mRgba, contours, -1, CONTOUR_COLOR); + + Mat colorLabel = mRgba.submat(2, 34, 2, 34); + colorLabel.setTo(mBlobColorRgba); + + Mat spectrumLabel = mRgba.submat(2, 2 + mSpectrum.rows(), 38, 38 + mSpectrum.cols()); + mSpectrum.copyTo(spectrumLabel); } + + return mRgba; } - /** Called when the activity is first created. */ - @Override - public void onCreate(Bundle savedInstanceState) { - Log.i(TAG, "called onCreate"); - super.onCreate(savedInstanceState); - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + private Scalar converScalarHsv2Rgba(Scalar hsvColor) { + Mat pointMatRgba = new Mat(); + Mat pointMatHsv = new Mat(1, 1, CvType.CV_8UC3, hsvColor); + Imgproc.cvtColor(pointMatHsv, pointMatRgba, Imgproc.COLOR_HSV2RGB_FULL, 4); + + return new Scalar(pointMatRgba.get(0, 0)); } } \ No newline at end of file diff --git a/samples/android/color-blob-detection/src/org/opencv/samples/colorblobdetect/ColorBlobDetectionView.java b/samples/android/color-blob-detection/src/org/opencv/samples/colorblobdetect/ColorBlobDetectionView.java deleted file mode 100644 index 6ad57daa32..0000000000 --- a/samples/android/color-blob-detection/src/org/opencv/samples/colorblobdetect/ColorBlobDetectionView.java +++ /dev/null @@ -1,151 +0,0 @@ -package org.opencv.samples.colorblobdetect; - -import java.util.List; -import org.opencv.android.Utils; -import org.opencv.core.Core; -import org.opencv.core.CvType; -import org.opencv.core.Mat; -import org.opencv.core.MatOfPoint; -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.imgproc.Imgproc; - -import android.content.Context; -import android.graphics.Bitmap; -import android.util.Log; -import android.view.MotionEvent; -import android.view.SurfaceHolder; -import android.view.View; -import android.view.View.OnTouchListener; - -public class ColorBlobDetectionView extends SampleCvViewBase implements OnTouchListener { - private static final String TAG = "OCVSample::View"; - - private Mat mRgba; - private boolean mIsColorSelected = false; - private Scalar mBlobColorRgba = new Scalar(255); - private Scalar mBlobColorHsv = new Scalar(255); - private ColorBlobDetector mDetector = new ColorBlobDetector(); - private Mat mSpectrum = new Mat(); - private static Size SPECTRUM_SIZE = new Size(200, 32); - private static final Scalar CONTOUR_COLOR = new Scalar(255,0,0,255); - - - public ColorBlobDetectionView(Context context) { - super(context); - setOnTouchListener(this); - Log.i(TAG, "Instantiated new " + this.getClass()); - } - - @Override - public void surfaceCreated(SurfaceHolder holder) { - Log.i(TAG, "called surfaceCreated"); - synchronized (this) { - // initialize Mat before usage - mRgba = new Mat(); - } - - super.surfaceCreated(holder); - } - - private Scalar converScalarHsv2Rgba(Scalar hsvColor) { - Mat pointMatRgba = new Mat(); - Mat pointMatHsv = new Mat(1, 1, CvType.CV_8UC3, hsvColor); - Imgproc.cvtColor(pointMatHsv, pointMatRgba, Imgproc.COLOR_HSV2RGB_FULL, 4); - - return new Scalar(pointMatRgba.get(0, 0)); - } - - public boolean onTouch(View v, MotionEvent event) { - int cols = mRgba.cols(); - int rows = mRgba.rows(); - - int xOffset = (getWidth() - cols) / 2; - int yOffset = (getHeight() - rows) / 2; - - int x = (int)event.getX() - xOffset; - int y = (int)event.getY() - yOffset; - - Log.i(TAG, "Touch image coordinates: (" + x + ", " + y + ")"); - - if ((x < 0) || (y < 0) || (x > cols) || (y > rows)) return false; - - Rect touchedRect = new Rect(); - - touchedRect.x = (x>4) ? x-4 : 0; - touchedRect.y = (y>4) ? y-4 : 0; - - touchedRect.width = (x+4 < cols) ? x + 4 - touchedRect.x : cols - touchedRect.x; - touchedRect.height = (y+4 < rows) ? y + 4 - touchedRect.y : rows - touchedRect.y; - - Mat touchedRegionRgba = mRgba.submat(touchedRect); - - Mat touchedRegionHsv = new Mat(); - Imgproc.cvtColor(touchedRegionRgba, touchedRegionHsv, Imgproc.COLOR_RGB2HSV_FULL); - - // Calculate average color of touched region - mBlobColorHsv = Core.sumElems(touchedRegionHsv); - int pointCount = touchedRect.width*touchedRect.height; - for (int i = 0; i < mBlobColorHsv.val.length; i++) - mBlobColorHsv.val[i] /= pointCount; - - mBlobColorRgba = converScalarHsv2Rgba(mBlobColorHsv); - - Log.i(TAG, "Touched rgba color: (" + mBlobColorRgba.val[0] + ", " + mBlobColorRgba.val[1] + - ", " + mBlobColorRgba.val[2] + ", " + mBlobColorRgba.val[3] + ")"); - - mDetector.setHsvColor(mBlobColorHsv); - - Imgproc.resize(mDetector.getSpectrum(), mSpectrum, SPECTRUM_SIZE); - - mIsColorSelected = true; - - return false; // don't need subsequent touch events - } - - @Override - protected Bitmap processFrame(VideoCapture capture) { - capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA); - - Bitmap bmp = Bitmap.createBitmap(mRgba.cols(), mRgba.rows(), Bitmap.Config.ARGB_8888); - - if (mIsColorSelected) { - mDetector.process(mRgba); - List contours = mDetector.getContours(); - Log.e(TAG, "Contours count: " + contours.size()); - Imgproc.drawContours(mRgba, contours, -1, CONTOUR_COLOR); - - Mat colorLabel = mRgba.submat(2, 34, 2, 34); - colorLabel.setTo(mBlobColorRgba); - - Mat spectrumLabel = mRgba.submat(2, 2 + mSpectrum.rows(), 38, 38 + mSpectrum.cols()); - mSpectrum.copyTo(spectrumLabel); - } - - 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(); - - mRgba = null; - } - } -} diff --git a/samples/android/color-blob-detection/src/org/opencv/samples/colorblobdetect/SampleCvViewBase.java b/samples/android/color-blob-detection/src/org/opencv/samples/colorblobdetect/SampleCvViewBase.java deleted file mode 100644 index 47cc3c85a8..0000000000 --- a/samples/android/color-blob-detection/src/org/opencv/samples/colorblobdetect/SampleCvViewBase.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.opencv.samples.colorblobdetect; - -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; - - public SampleCvViewBase(Context context) { - super(context); - mHolder = getHolder(); - mHolder.addCallback(this); - 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"); - while (true) { - Bitmap bmp = null; - - synchronized (this) { - if (mCamera == null) - break; - - if (!mCamera.grab()) { - Log.e(TAG, "mCamera.grab() failed"); - break; - } - - bmp = processFrame(mCamera); - } - - 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); - mHolder.unlockCanvasAndPost(canvas); - } - bmp.recycle(); - } - } - Log.i(TAG, "Finished processing thread"); - } -} \ No newline at end of file diff --git a/samples/android/face-detection/.classpath b/samples/android/face-detection/.classpath index a4763d1eec..3f9691c5dd 100644 --- a/samples/android/face-detection/.classpath +++ b/samples/android/face-detection/.classpath @@ -1,8 +1,8 @@ - - + + 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..68d8fb4ec7 --- /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..7a2808cf54 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.android.JavaCameraView; +import org.opencv.android.CameraBridgeViewBase.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 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 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 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 int mDetectorType = JAVA_DETECTOR; + private String[] mDetectorName; - private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) { + private float mRelativeFaceSize = 0; + private int mAbsoluteFaceSize = 0; + + private JavaCameraView 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); - - // 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(); + 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); + + 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); } - } 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(); - } - }); - MarketErrorMessage.show(); + mOpenCvCameraView.enableView(); } break; default: { @@ -83,37 +109,88 @@ 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()); } + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + Log.i(TAG, "called onCreate"); + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + setContentView(R.layout.face_detect_surface_view); + + mOpenCvCameraView = (JavaCameraView)findViewById(R.id.fd_activity_surface_view); + mOpenCvCameraView.setCvCameraViewListener(this); + } + @Override - protected void onPause() { - Log.i(TAG, "called onPause"); - if (null != mView) - mView.releaseCamera(); + public void onPause() + { + mOpenCvCameraView.disableView(); super.onPause(); } @Override - protected void onResume() { - Log.i(TAG, "called onResume"); + public void onResume() + { super.onResume(); + OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_2, this, mLoaderCallback); + } - 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"); - } + public void onDestroy() { + super.onDestroy(); + mOpenCvCameraView.disableView(); } - /** Called when the activity is first created. */ - @Override - public void onCreate(Bundle savedInstanceState) { - Log.i(TAG, "called onCreate"); - super.onCreate(savedInstanceState); - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + 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 diff --git a/samples/android/image-manipulations/.classpath b/samples/android/image-manipulations/.classpath index a4763d1eec..3f9691c5dd 100644 --- a/samples/android/image-manipulations/.classpath +++ b/samples/android/image-manipulations/.classpath @@ -1,8 +1,8 @@ - - + + diff --git a/samples/android/image-manipulations/res/layout/image_manipulations_surface_view.xml b/samples/android/image-manipulations/res/layout/image_manipulations_surface_view.xml new file mode 100644 index 0000000000..06e2e5e905 --- /dev/null +++ b/samples/android/image-manipulations/res/layout/image_manipulations_surface_view.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/samples/android/image-manipulations/src/org/opencv/samples/imagemanipulations/ImageManipulationsActivity.java b/samples/android/image-manipulations/src/org/opencv/samples/imagemanipulations/ImageManipulationsActivity.java index 5cbc3af06d..19744f9eee 100644 --- a/samples/android/image-manipulations/src/org/opencv/samples/imagemanipulations/ImageManipulationsActivity.java +++ b/samples/android/image-manipulations/src/org/opencv/samples/imagemanipulations/ImageManipulationsActivity.java @@ -1,12 +1,23 @@ package org.opencv.samples.imagemanipulations; +import java.util.Arrays; + import org.opencv.android.BaseLoaderCallback; 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.MatOfFloat; +import org.opencv.core.MatOfInt; +import org.opencv.core.Point; +import org.opencv.core.Scalar; +import org.opencv.core.Size; +import org.opencv.android.JavaCameraView; +import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener; +import org.opencv.imgproc.Imgproc; import android.app.Activity; -import android.app.AlertDialog; -import android.content.DialogInterface; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -14,70 +25,64 @@ import android.view.MenuItem; import android.view.Window; import android.view.WindowManager; -public class ImageManipulationsActivity extends Activity { - private static final String TAG = "OCVSample::Activity"; - - public static final int VIEW_MODE_RGBA = 0; - public static final int VIEW_MODE_HIST = 1; - public static final int VIEW_MODE_CANNY = 2; - public static final int VIEW_MODE_SEPIA = 3; - public static final int VIEW_MODE_SOBEL = 4; - public static final int VIEW_MODE_ZOOM = 5; - public static final int VIEW_MODE_PIXELIZE = 6; - public static final int VIEW_MODE_POSTERIZE = 7; - - private MenuItem mItemPreviewRGBA; - private MenuItem mItemPreviewHist; - private MenuItem mItemPreviewCanny; - private MenuItem mItemPreviewSepia; - private MenuItem mItemPreviewSobel; - private MenuItem mItemPreviewZoom; - private MenuItem mItemPreviewPixelize; - private MenuItem mItemPreviewPosterize; - private ImageManipulationsView mView; +public class ImageManipulationsActivity extends Activity implements CvCameraViewListener { + private static final String TAG = "OCVSample::Activity"; + + public static final int VIEW_MODE_RGBA = 0; + public static final int VIEW_MODE_HIST = 1; + public static final int VIEW_MODE_CANNY = 2; + public static final int VIEW_MODE_SEPIA = 3; + public static final int VIEW_MODE_SOBEL = 4; + public static final int VIEW_MODE_ZOOM = 5; + public static final int VIEW_MODE_PIXELIZE = 6; + public static final int VIEW_MODE_POSTERIZE = 7; + + private MenuItem mItemPreviewRGBA; + private MenuItem mItemPreviewHist; + private MenuItem mItemPreviewCanny; + private MenuItem mItemPreviewSepia; + private MenuItem mItemPreviewSobel; + private MenuItem mItemPreviewZoom; + private MenuItem mItemPreviewPixelize; + private MenuItem mItemPreviewPosterize; + private JavaCameraView mOpenCvCameraView; + + private Size mSize0; + private Size mSizeRgba; + private Size mSizeRgbaInner; + + private Mat mRgba; + private Mat mGray; + private Mat mIntermediateMat; + private Mat mHist; + private Mat mMat0; + private MatOfInt mChannels[]; + private MatOfInt mHistSize; + private int mHistSizeNum; + private MatOfFloat mRanges; + private Scalar mColorsRGB[]; + private Scalar mColorsHue[]; + private Scalar mWhilte; + private Point mP1; + private Point mP2; + private float mBuff[]; + private Mat mRgbaInnerWindow; + private Mat mGrayInnerWindow; + private Mat mBlurWindow; + private Mat mZoomWindow; + private Mat mZoomCorner; + private Mat mSepiaKernel; public static int viewMode = VIEW_MODE_RGBA; - private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) { + private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { @Override public void onManagerConnected(int status) { switch (status) { case LoaderCallbackInterface.SUCCESS: { Log.i(TAG, "OpenCV loaded successfully"); - // Create and set View - mView = new ImageManipulationsView(mAppContext); - setContentView(mView); - - // 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(); - } - }); - MarketErrorMessage.show(); + mOpenCvCameraView.enableView(); } break; default: { @@ -91,32 +96,37 @@ public class ImageManipulationsActivity extends Activity { Log.i(TAG, "Instantiated new " + this.getClass()); } + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + Log.i(TAG, "called onCreate"); + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + setContentView(R.layout.image_manipulations_surface_view); + + mOpenCvCameraView = (JavaCameraView)findViewById(R.id.image_manipulations_activity_surface_view); + mOpenCvCameraView.setCvCameraViewListener(this); + } + @Override - protected void onPause() { - Log.i(TAG, "called onPause"); - if (null != mView) - mView.releaseCamera(); + public void onPause() + { + mOpenCvCameraView.disableView(); super.onPause(); } @Override - protected void onResume() { - Log.i(TAG, "called onResume"); + public void 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"); - } + OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_2, this, mLoaderCallback); } - /** Called when the activity is first created. */ - @Override - public void onCreate(Bundle savedInstanceState) { - Log.i(TAG, "called onCreate"); - super.onCreate(savedInstanceState); - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + public void onDestroy() { + super.onDestroy(); + mOpenCvCameraView.disableView(); } @Override @@ -154,4 +164,203 @@ public class ImageManipulationsActivity extends Activity { viewMode = VIEW_MODE_POSTERIZE; return true; } + + public void onCameraViewStarted(int width, int height) { + mGray = new Mat(); + mRgba = new Mat(); + mIntermediateMat = new Mat(); + mSize0 = new Size(); + mHist = new Mat(); + mChannels = new MatOfInt[] { new MatOfInt(0), new MatOfInt(1), new MatOfInt(2) }; + mHistSizeNum = 25; + mBuff = new float[mHistSizeNum]; + mHistSize = new MatOfInt(mHistSizeNum); + mRanges = new MatOfFloat(0f, 256f); + mMat0 = new Mat(); + mColorsRGB = new Scalar[] { new Scalar(200, 0, 0, 255), new Scalar(0, 200, 0, 255), new Scalar(0, 0, 200, 255) }; + mColorsHue = new Scalar[] { + new Scalar(255, 0, 0, 255), new Scalar(255, 60, 0, 255), new Scalar(255, 120, 0, 255), new Scalar(255, 180, 0, 255), new Scalar(255, 240, 0, 255), + new Scalar(215, 213, 0, 255), new Scalar(150, 255, 0, 255), new Scalar(85, 255, 0, 255), new Scalar(20, 255, 0, 255), new Scalar(0, 255, 30, 255), + new Scalar(0, 255, 85, 255), new Scalar(0, 255, 150, 255), new Scalar(0, 255, 215, 255), new Scalar(0, 234, 255, 255), new Scalar(0, 170, 255, 255), + new Scalar(0, 120, 255, 255), new Scalar(0, 60, 255, 255), new Scalar(0, 0, 255, 255), new Scalar(64, 0, 255, 255), new Scalar(120, 0, 255, 255), + new Scalar(180, 0, 255, 255), new Scalar(255, 0, 255, 255), new Scalar(255, 0, 215, 255), new Scalar(255, 0, 85, 255), new Scalar(255, 0, 0, 255) + }; + mWhilte = Scalar.all(255); + mP1 = new Point(); + mP2 = new Point(); + + // Fill sepia kernel + mSepiaKernel = new Mat(4, 4, CvType.CV_32F); + mSepiaKernel.put(0, 0, /* R */0.189f, 0.769f, 0.393f, 0f); + mSepiaKernel.put(1, 0, /* G */0.168f, 0.686f, 0.349f, 0f); + mSepiaKernel.put(2, 0, /* B */0.131f, 0.534f, 0.272f, 0f); + mSepiaKernel.put(3, 0, /* A */0.000f, 0.000f, 0.000f, 1f); + } + + private void CreateAuxiliaryMats() { + if (mRgba.empty()) + return; + + mSizeRgba = mRgba.size(); + + int rows = (int) mSizeRgba.height; + int cols = (int) mSizeRgba.width; + + int left = cols / 8; + int top = rows / 8; + + int width = cols * 3 / 4; + int height = rows * 3 / 4; + + if (mRgbaInnerWindow == null) + mRgbaInnerWindow = mRgba.submat(top, top + height, left, left + width); + mSizeRgbaInner = mRgbaInnerWindow.size(); + + if (mGrayInnerWindow == null && !mGray.empty()) + mGrayInnerWindow = mGray.submat(top, top + height, left, left + width); + + if (mBlurWindow == null) + mBlurWindow = mRgba.submat(0, rows, cols / 3, cols * 2 / 3); + + if (mZoomCorner == null) + mZoomCorner = mRgba.submat(0, rows / 2 - rows / 10, 0, cols / 2 - cols / 10); + + if (mZoomWindow == null) + mZoomWindow = mRgba.submat(rows / 2 - 9 * rows / 100, rows / 2 + 9 * rows / 100, cols / 2 - 9 * cols / 100, cols / 2 + 9 * cols / 100); + } + + public void onCameraViewStopped() { + // Explicitly deallocate Mats + if (mZoomWindow != null) + mZoomWindow.release(); + if (mZoomCorner != null) + mZoomCorner.release(); + if (mBlurWindow != null) + mBlurWindow.release(); + if (mGrayInnerWindow != null) + mGrayInnerWindow.release(); + if (mRgbaInnerWindow != null) + mRgbaInnerWindow.release(); + if (mRgba != null) + mRgba.release(); + if (mGray != null) + mGray.release(); + if (mIntermediateMat != null) + mIntermediateMat.release(); + + mRgba = null; + mGray = null; + mIntermediateMat = null; + mRgbaInnerWindow = null; + mGrayInnerWindow = null; + mBlurWindow = null; + mZoomCorner = null; + mZoomWindow = null; + } + + public Mat onCameraFrame(Mat inputFrame) { + inputFrame.copyTo(mRgba); + + switch (ImageManipulationsActivity.viewMode) { + case ImageManipulationsActivity.VIEW_MODE_RGBA: + break; + + case ImageManipulationsActivity.VIEW_MODE_HIST: + if ((mSizeRgba == null) || (mRgba.cols() != mSizeRgba.width) || (mRgba.height() != mSizeRgba.height)) + CreateAuxiliaryMats(); + int thikness = (int) (mSizeRgba.width / (mHistSizeNum + 10) / 5); + if(thikness > 5) thikness = 5; + int offset = (int) ((mSizeRgba.width - (5*mHistSizeNum + 4*10)*thikness)/2); + // RGB + for(int c=0; c<3; c++) { + Imgproc.calcHist(Arrays.asList(mRgba), mChannels[c], mMat0, mHist, mHistSize, mRanges); + Core.normalize(mHist, mHist, mSizeRgba.height/2, 0, Core.NORM_INF); + mHist.get(0, 0, mBuff); + for(int h=0; h 5) thikness = 5; - int offset = (int) ((mSizeRgba.width - (5*mHistSizeNum + 4*10)*thikness)/2); - // RGB - for(int c=0; c<3; c++) { - Imgproc.calcHist(Arrays.asList(mRgba), mChannels[c], mMat0, mHist, mHistSize, mRanges); - Core.normalize(mHist, mHist, mSizeRgba.height/2, 0, Core.NORM_INF); - mHist.get(0, 0, mBuff); - for(int h=0; h 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()), 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 diff --git a/samples/android/tutorial-0-androidcamera/.classpath b/samples/android/tutorial-0-androidcamera/.classpath index 6aed2ebfbe..d3bb07b44c 100644 --- a/samples/android/tutorial-0-androidcamera/.classpath +++ b/samples/android/tutorial-0-androidcamera/.classpath @@ -1,8 +1,8 @@ - - + + diff --git a/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/Sample0Base.java b/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/Sample0Base.java index 4706b74784..02a0a149ab 100644 --- a/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/Sample0Base.java +++ b/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/Sample0Base.java @@ -5,16 +5,11 @@ import android.app.AlertDialog; import android.content.DialogInterface; import android.os.Bundle; import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; import android.view.Window; public class Sample0Base extends Activity { private static final String TAG = "OCVSample::Activity"; - - private MenuItem mItemPreviewRGBA; - private MenuItem mItemPreviewGray; private Sample0View mView; public Sample0Base() { @@ -55,22 +50,4 @@ public class Sample0Base extends Activity { mView = new Sample0View(this); setContentView(mView); } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - Log.i(TAG, "called onCreateOptionsMenu"); - mItemPreviewRGBA = menu.add("Preview RGBA"); - mItemPreviewGray = menu.add("Preview GRAY"); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - Log.i(TAG, "called onOptionsItemSelected; selected item: " + item); - if (item == mItemPreviewRGBA) - mView.setViewMode(Sample0View.VIEW_MODE_RGBA); - else if (item == mItemPreviewGray) - mView.setViewMode(Sample0View.VIEW_MODE_GRAY); - return true; - } } diff --git a/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/SampleViewBase.java b/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/SampleViewBase.java index 79b0d43caa..8db6fae8ac 100644 --- a/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/SampleViewBase.java +++ b/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/SampleViewBase.java @@ -3,6 +3,7 @@ package org.opencv.samples.tutorial0; import java.io.IOException; import java.util.List; +import android.annotation.TargetApi; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -43,7 +44,8 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde return mFrameHeight; } - public void setPreview() throws IOException { + @TargetApi(11) + public void setPreview() throws IOException { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { mSf = new SurfaceTexture(10); mCamera.setPreviewTexture( mSf ); diff --git a/samples/android/tutorial-1-addopencv/.classpath b/samples/android/tutorial-1-addopencv/.classpath index a4763d1eec..3f9691c5dd 100644 --- a/samples/android/tutorial-1-addopencv/.classpath +++ b/samples/android/tutorial-1-addopencv/.classpath @@ -1,8 +1,8 @@ - - + + diff --git a/samples/android/tutorial-1-addopencv/res/layout/tutorial1_surface_view.xml b/samples/android/tutorial-1-addopencv/res/layout/tutorial1_surface_view.xml new file mode 100644 index 0000000000..52a73323ff --- /dev/null +++ b/samples/android/tutorial-1-addopencv/res/layout/tutorial1_surface_view.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/Sample1Java.java b/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/Sample1Java.java index 03da4eefae..e53b55ce1c 100644 --- a/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/Sample1Java.java +++ b/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/Sample1Java.java @@ -3,65 +3,29 @@ package org.opencv.samples.tutorial1; import org.opencv.android.BaseLoaderCallback; import org.opencv.android.LoaderCallbackInterface; import org.opencv.android.OpenCVLoader; +import org.opencv.core.Mat; +import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener; +import org.opencv.android.JavaCameraView; import android.app.Activity; -import android.app.AlertDialog; -import android.content.DialogInterface; import android.os.Bundle; import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; import android.view.Window; import android.view.WindowManager; -public class Sample1Java extends Activity { +public class Sample1Java extends Activity implements CvCameraViewListener { private static final String TAG = "OCVSample::Activity"; - private MenuItem mItemPreviewRGBA; - private MenuItem mItemPreviewGray; - private MenuItem mItemPreviewCanny; - private Sample1View mView; + private JavaCameraView mOpenCvCameraView; - private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) { + private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { @Override public void onManagerConnected(int status) { switch (status) { case LoaderCallbackInterface.SUCCESS: { Log.i(TAG, "OpenCV loaded successfully"); - // Create and set View - mView = new Sample1View(mAppContext); - setContentView(mView); - - // 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(); - } - }); - MarketErrorMessage.show(); + mOpenCvCameraView.enableView(); } break; default: { @@ -75,25 +39,6 @@ public class Sample1Java extends Activity { 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) { @@ -101,27 +46,39 @@ public class Sample1Java extends Activity { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + setContentView(R.layout.tutorial1_surface_view); + + mOpenCvCameraView = (JavaCameraView)findViewById(R.id.tutorial1_activity_surface_view); + mOpenCvCameraView.setCvCameraViewListener(this); } @Override - public boolean onCreateOptionsMenu(Menu menu) { - Log.i(TAG, "called onCreateOptionsMenu"); - mItemPreviewRGBA = menu.add("Preview RGBA"); - mItemPreviewGray = menu.add("Preview GRAY"); - mItemPreviewCanny = menu.add("Canny"); - return true; + public void onPause() + { + mOpenCvCameraView.disableView(); + super.onPause(); } @Override - public boolean onOptionsItemSelected(MenuItem item) { - Log.i(TAG, "called onOptionsItemSelected; selected item: " + item); - if (item == mItemPreviewRGBA) { - mView.setViewMode(Sample1View.VIEW_MODE_RGBA); - } else if (item == mItemPreviewGray) { - mView.setViewMode(Sample1View.VIEW_MODE_GRAY); - } else if (item == mItemPreviewCanny) { - mView.setViewMode(Sample1View.VIEW_MODE_CANNY); - } - return true; + 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) { + } + + public void onCameraViewStopped() { + } + + public Mat onCameraFrame(Mat inputFrame) { + return inputFrame; } } diff --git a/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/Sample1View.java b/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/Sample1View.java deleted file mode 100644 index fd949cc31b..0000000000 --- a/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/Sample1View.java +++ /dev/null @@ -1,113 +0,0 @@ -package org.opencv.samples.tutorial1; - -import org.opencv.android.Utils; -import org.opencv.core.Core; -import org.opencv.core.CvType; -import org.opencv.core.Mat; -import org.opencv.core.Point; -import org.opencv.core.Scalar; -import org.opencv.imgproc.Imgproc; - -import android.content.Context; -import android.graphics.Bitmap; -import android.util.Log; - -class Sample1View extends SampleViewBase { - private static final String TAG = "OCVSample::View"; - - public static final int VIEW_MODE_RGBA = 0; - public static final int VIEW_MODE_GRAY = 1; - public static final int VIEW_MODE_CANNY = 2; - - private Mat mYuv; - private Mat mRgba; - private Mat mGraySubmat; - private Mat mIntermediateMat; - private Bitmap mBitmap; - private int mViewMode; - - public Sample1View(Context context) { - super(context); - mViewMode = VIEW_MODE_RGBA; - Log.i(TAG, "Instantiated new " + this.getClass()); - } - - @Override - protected void onPreviewStarted(int previewWidth, int previewHeight) { - Log.i(TAG, "called onPreviewStarted("+previewWidth+", "+previewHeight+")"); - - // initialize Mats before usage - mYuv = new Mat(getFrameHeight() + getFrameHeight() / 2, getFrameWidth(), CvType.CV_8UC1); - mGraySubmat = mYuv.submat(0, getFrameHeight(), 0, getFrameWidth()); - - mRgba = new Mat(); - mIntermediateMat = new Mat(); - - mBitmap = Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888); - } - - @Override - protected void onPreviewStopped() { - Log.i(TAG, "called onPreviewStopped"); - - if(mBitmap != null) { - mBitmap.recycle(); - mBitmap = null; - } - - synchronized (this) { - // Explicitly deallocate Mats - if (mYuv != null) - mYuv.release(); - if (mRgba != null) - mRgba.release(); - if (mGraySubmat != null) - mGraySubmat.release(); - if (mIntermediateMat != null) - mIntermediateMat.release(); - - mYuv = null; - mRgba = null; - mGraySubmat = null; - mIntermediateMat = null; - } - } - - @Override - protected Bitmap processFrame(byte[] data) { - mYuv.put(0, 0, data); - - final int viewMode = mViewMode; - - switch (viewMode) { - case VIEW_MODE_GRAY: - Imgproc.cvtColor(mGraySubmat, mRgba, Imgproc.COLOR_GRAY2RGBA, 4); - break; - case VIEW_MODE_RGBA: - Imgproc.cvtColor(mYuv, mRgba, Imgproc.COLOR_YUV420sp2RGB, 4); - Core.putText(mRgba, "OpenCV + Android", new Point(10, 100), 3/* CV_FONT_HERSHEY_COMPLEX */, 2, new Scalar(255, 0, 0, 255), 3); - break; - case VIEW_MODE_CANNY: - Imgproc.Canny(mGraySubmat, mIntermediateMat, 80, 100); - Imgproc.cvtColor(mIntermediateMat, mRgba, Imgproc.COLOR_GRAY2BGRA, 4); - break; - } - - Bitmap bmp = mBitmap; - - try { - Utils.matToBitmap(mRgba, bmp); - } catch(Exception e) { - Log.e("org.opencv.samples.tutorial1", "Utils.matToBitmap() throws an exception: " + e.getMessage()); - bmp.recycle(); - bmp = null; - } - return bmp; - } - - public void setViewMode(int viewMode) { - Log.i(TAG, "called setViewMode("+viewMode+")"); - mViewMode = viewMode; - } - -} diff --git a/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/SampleViewBase.java b/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/SampleViewBase.java deleted file mode 100644 index d8b406dddf..0000000000 --- a/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/SampleViewBase.java +++ /dev/null @@ -1,229 +0,0 @@ -package org.opencv.samples.tutorial1; - -import java.io.IOException; -import java.util.List; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.ImageFormat; -import android.graphics.SurfaceTexture; -import android.hardware.Camera; -import android.hardware.Camera.PreviewCallback; -import android.os.Build; -import android.util.Log; -import android.view.SurfaceHolder; -import android.view.SurfaceView; - -public abstract class SampleViewBase extends SurfaceView implements SurfaceHolder.Callback, Runnable { - private static final String TAG = "OCVSample::BaseView"; - - private Camera mCamera; - private SurfaceHolder mHolder; - private int mFrameWidth; - private int mFrameHeight; - private byte[] mFrame; - private volatile boolean mThreadRun; - private byte[] mBuffer; - private SurfaceTexture mSf; - - - public SampleViewBase(Context context) { - super(context); - mHolder = getHolder(); - mHolder.addCallback(this); - Log.i(TAG, "Instantiated new " + this.getClass()); - } - - public int getFrameWidth() { - return mFrameWidth; - } - - public int getFrameHeight() { - return mFrameHeight; - } - - public void setPreview() throws IOException { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - mSf = new SurfaceTexture(10); - mCamera.setPreviewTexture( mSf ); - } - else - mCamera.setPreviewDisplay(null); - } - - public boolean openCamera() { - Log.i(TAG, "Opening Camera"); - mCamera = null; - - try { - mCamera = Camera.open(); - } - catch (Exception e){ - Log.e(TAG, "Camera is not available (in use or does not exist): " + e.getLocalizedMessage()); - } - - if(mCamera == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { - for (int camIdx = 0; camIdx < Camera.getNumberOfCameras(); ++camIdx) { - try { - mCamera = Camera.open(camIdx); - } - catch (RuntimeException e) { - Log.e(TAG, "Camera #" + camIdx + "failed to open: " + e.getLocalizedMessage()); - } - } - } - - if(mCamera == null) { - Log.e(TAG, "Can't open any camera"); - return false; - } - - mCamera.setPreviewCallbackWithBuffer(new PreviewCallback() { - public void onPreviewFrame(byte[] data, Camera camera) { - synchronized (SampleViewBase.this) { - System.arraycopy(data, 0, mFrame, 0, data.length); - SampleViewBase.this.notify(); - } - camera.addCallbackBuffer(mBuffer); - } - }); - - return true; - } - - public void releaseCamera() { - Log.i(TAG, "Releasing Camera"); - mThreadRun = false; - synchronized (this) { - if (mCamera != null) { - mCamera.stopPreview(); - mCamera.release(); - mCamera = null; - } - } - onPreviewStopped(); - } - - public synchronized void setupCamera(int width, int height) { - if (mCamera != null) { - Log.i(TAG, "Setup Camera - " + width + "x" + height); - Camera.Parameters params = mCamera.getParameters(); - List sizes = params.getSupportedPreviewSizes(); - mFrameWidth = width; - mFrameHeight = height; - - // selecting optimal camera preview size - { - int minDiff = Integer.MAX_VALUE; - for (Camera.Size size : sizes) { - if (Math.abs(size.height - height) < minDiff) { - mFrameWidth = size.width; - mFrameHeight = size.height; - minDiff = Math.abs(size.height - height); - } - } - } - - params.setPreviewSize(getFrameWidth(), getFrameHeight()); - - List FocusModes = params.getSupportedFocusModes(); - if (FocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) - { - params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); - } - - mCamera.setParameters(params); - - /* Now allocate the buffer */ - params = mCamera.getParameters(); - int size = params.getPreviewSize().width * params.getPreviewSize().height; - size = size * ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8; - mBuffer = new byte[size]; - /* The buffer where the current frame will be copied */ - mFrame = new byte [size]; - mCamera.addCallbackBuffer(mBuffer); - - /* Notify that the preview is about to be started and deliver preview size */ - onPreviewStarted(params.getPreviewSize().width, params.getPreviewSize().height); - - try { - setPreview(); - } catch (IOException e) { - Log.e(TAG, "mCamera.setPreviewDisplay/setPreviewTexture fails: " + e); - } - - /* Now we can start a preview */ - mCamera.startPreview(); - } - } - - public void surfaceChanged(SurfaceHolder _holder, int format, int width, int height) { - Log.i(TAG, "called surfaceChanged"); - // stop preview before making changes - try { - mCamera.stopPreview(); - } catch (Exception e){ - // ignore: tried to stop a non-existent preview - } - - // start preview with new settings - 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"); - } - - /* The bitmap returned by this method shall be owned by the child and released in onPreviewStopped() */ - protected abstract Bitmap processFrame(byte[] data); - - /** - * This method is called when the preview process is being started. It is called before the first frame delivered and processFrame is called - * It is called with the width and height parameters of the preview process. It can be used to prepare the data needed during the frame processing. - * @param previewWidth - the width of the preview frames that will be delivered via processFrame - * @param previewHeight - the height of the preview frames that will be delivered via processFrame - */ - protected abstract void onPreviewStarted(int previewWidtd, int previewHeight); - - /** - * This method is called when preview is stopped. When this method is called the preview stopped and all the processing of frames already completed. - * If the Bitmap object returned via processFrame is cached - it is a good time to recycle it. - * Any other resources used during the preview can be released. - */ - protected abstract void onPreviewStopped(); - - public void run() { - mThreadRun = true; - Log.i(TAG, "Started processing thread"); - while (mThreadRun) { - Bitmap bmp = null; - - synchronized (this) { - try { - this.wait(); - if (!mThreadRun) - break; - bmp = processFrame(mFrame); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - if (bmp != null) { - Canvas canvas = mHolder.lockCanvas(); - if (canvas != null) { - canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR); - canvas.drawBitmap(bmp, (canvas.getWidth() - getFrameWidth()) / 2, (canvas.getHeight() - getFrameHeight()) / 2, null); - mHolder.unlockCanvasAndPost(canvas); - } - } - } - Log.i(TAG, "Finished processing thread"); - } -} \ No newline at end of file diff --git a/samples/android/tutorial-2-opencvcamera/.classpath b/samples/android/tutorial-2-opencvcamera/.classpath index a4763d1eec..3f9691c5dd 100644 --- a/samples/android/tutorial-2-opencvcamera/.classpath +++ b/samples/android/tutorial-2-opencvcamera/.classpath @@ -1,8 +1,8 @@ - - + + diff --git a/samples/android/tutorial-2-opencvcamera/res/layout/tutorial2_surface_view.xml b/samples/android/tutorial-2-opencvcamera/res/layout/tutorial2_surface_view.xml new file mode 100644 index 0000000000..b8f760494e --- /dev/null +++ b/samples/android/tutorial-2-opencvcamera/res/layout/tutorial2_surface_view.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/samples/android/tutorial-2-opencvcamera/src/org/opencv/samples/tutorial2/Sample2NativeCamera.java b/samples/android/tutorial-2-opencvcamera/src/org/opencv/samples/tutorial2/Sample2NativeCamera.java index 67a2c755fb..0676d5bb2d 100644 --- a/samples/android/tutorial-2-opencvcamera/src/org/opencv/samples/tutorial2/Sample2NativeCamera.java +++ b/samples/android/tutorial-2-opencvcamera/src/org/opencv/samples/tutorial2/Sample2NativeCamera.java @@ -3,10 +3,17 @@ package org.opencv.samples.tutorial2; import org.opencv.android.BaseLoaderCallback; 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.Point; +import org.opencv.core.Scalar; +import org.opencv.android.NativeCameraView; +import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener; +import org.opencv.highgui.Highgui; +import org.opencv.imgproc.Imgproc; import android.app.Activity; -import android.app.AlertDialog; -import android.content.DialogInterface; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -14,60 +21,30 @@ import android.view.MenuItem; import android.view.Window; import android.view.WindowManager; -public class Sample2NativeCamera extends Activity { +public class Sample2NativeCamera extends Activity implements CvCameraViewListener { private static final String TAG = "OCVSample::Activity"; public static final int VIEW_MODE_RGBA = 0; public static final int VIEW_MODE_GRAY = 1; public static final int VIEW_MODE_CANNY = 2; + private static int viewMode = VIEW_MODE_RGBA; private MenuItem mItemPreviewRGBA; private MenuItem mItemPreviewGray; private MenuItem mItemPreviewCanny; - private Sample2View mView; + private Mat mRgba; + private Mat mIntermediateMat; - public static int viewMode = VIEW_MODE_RGBA; + private NativeCameraView mOpenCvCameraView; - private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) { + private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { @Override public void onManagerConnected(int status) { switch (status) { case LoaderCallbackInterface.SUCCESS: { Log.i(TAG, "OpenCV loaded successfully"); - // Create and set View - mView = new Sample2View(mAppContext); - setContentView(mView); - - // 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(); - } - }); - MarketErrorMessage.show(); + mOpenCvCameraView.enableView(); } break; default: { @@ -81,32 +58,68 @@ public class Sample2NativeCamera extends Activity { Log.i(TAG, "Instantiated new " + this.getClass()); } + /** Called when the activity is first created. */ @Override - protected void onPause() { - Log.i(TAG, "called onPause"); - if (null != mView) - mView.releaseCamera(); + public void onCreate(Bundle savedInstanceState) { + Log.i(TAG, "called onCreate"); + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + setContentView(R.layout.tutorial2_surface_view); + + mOpenCvCameraView = (NativeCameraView)findViewById(R.id.tutorial2_activity_surface_view); + mOpenCvCameraView.setCvCameraViewListener(this); + } + + @Override + public void onPause() + { + mOpenCvCameraView.disableView(); super.onPause(); } @Override - protected void onResume() { - Log.i(TAG, "called onResume"); + public void onResume() + { super.onResume(); + OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_2, this, mLoaderCallback); + } - 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"); - } + public void onDestroy() { + super.onDestroy(); + mOpenCvCameraView.disableView(); } - /** Called when the activity is first created. */ - @Override - public void onCreate(Bundle savedInstanceState) { - Log.i(TAG, "called onCreate"); - super.onCreate(savedInstanceState); - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + public void onCameraViewStarted(int width, int height) { + mRgba = new Mat(height, width, CvType.CV_8UC4); + mIntermediateMat = new Mat(height, width, CvType.CV_8UC4); + } + + public void onCameraViewStopped() { + mRgba.release(); + mIntermediateMat.release(); + } + + public Mat onCameraFrame(Mat inputFrame) { + switch (Sample2NativeCamera.viewMode) { + case Sample2NativeCamera.VIEW_MODE_GRAY: + { + Imgproc.cvtColor(inputFrame, mRgba, Imgproc.COLOR_GRAY2RGBA, 4); + } break; + case Sample2NativeCamera.VIEW_MODE_RGBA: + { + inputFrame.copyTo(mRgba); + Core.putText(mRgba, "OpenCV+Android", new Point(10, 50), 3, 1, new Scalar(255, 0, 0, 255), 2); + } break; + case Sample2NativeCamera.VIEW_MODE_CANNY: + { + Imgproc.Canny(inputFrame, mIntermediateMat, 80, 100); + Imgproc.cvtColor(mIntermediateMat, mRgba, Imgproc.COLOR_GRAY2BGRA, 4); + } break; + } + + return mRgba; } @Override @@ -122,11 +135,21 @@ public class Sample2NativeCamera extends Activity { public boolean onOptionsItemSelected(MenuItem item) { Log.i(TAG, "called onOptionsItemSelected; selected item: " + item); if (item == mItemPreviewRGBA) + { + mOpenCvCameraView.SetCaptureFormat(Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA); viewMode = VIEW_MODE_RGBA; + } else if (item == mItemPreviewGray) + { + mOpenCvCameraView.SetCaptureFormat(Highgui.CV_CAP_ANDROID_GREY_FRAME); viewMode = VIEW_MODE_GRAY; + } else if (item == mItemPreviewCanny) + { + mOpenCvCameraView.SetCaptureFormat(Highgui.CV_CAP_ANDROID_GREY_FRAME); viewMode = VIEW_MODE_CANNY; + } + return true; } } diff --git a/samples/android/tutorial-2-opencvcamera/src/org/opencv/samples/tutorial2/Sample2View.java b/samples/android/tutorial-2-opencvcamera/src/org/opencv/samples/tutorial2/Sample2View.java deleted file mode 100644 index e61dd4043e..0000000000 --- a/samples/android/tutorial-2-opencvcamera/src/org/opencv/samples/tutorial2/Sample2View.java +++ /dev/null @@ -1,90 +0,0 @@ -package org.opencv.samples.tutorial2; - -import org.opencv.android.Utils; -import org.opencv.core.Core; -import org.opencv.core.Mat; -import org.opencv.core.Point; -import org.opencv.core.Scalar; -import org.opencv.highgui.Highgui; -import org.opencv.highgui.VideoCapture; -import org.opencv.imgproc.Imgproc; - -import android.content.Context; -import android.graphics.Bitmap; -import android.util.Log; -import android.view.SurfaceHolder; - -class Sample2View extends SampleCvViewBase { - private static final String TAG = "OCVSample::View"; - - private Mat mRgba; - private Mat mGray; - private Mat mIntermediateMat; - - public Sample2View(Context context) { - super(context); - 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(); - mIntermediateMat = new Mat(); - } - - super.surfaceCreated(holder); - } - - @Override - protected Bitmap processFrame(VideoCapture capture) { - switch (Sample2NativeCamera.viewMode) { - case Sample2NativeCamera.VIEW_MODE_GRAY: - capture.retrieve(mGray, Highgui.CV_CAP_ANDROID_GREY_FRAME); - Imgproc.cvtColor(mGray, mRgba, Imgproc.COLOR_GRAY2RGBA, 4); - break; - case Sample2NativeCamera.VIEW_MODE_RGBA: - capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA); - Core.putText(mRgba, "OpenCV+Android", new Point(10, 50), 3, 1, new Scalar(255, 0, 0, 255), 2); - break; - case Sample2NativeCamera.VIEW_MODE_CANNY: - capture.retrieve(mGray, Highgui.CV_CAP_ANDROID_GREY_FRAME); - Imgproc.Canny(mGray, mIntermediateMat, 80, 100); - Imgproc.cvtColor(mIntermediateMat, mRgba, Imgproc.COLOR_GRAY2BGRA, 4); - break; - } - - Bitmap bmp = Bitmap.createBitmap(mRgba.cols(), mRgba.rows(), Bitmap.Config.ARGB_8888); - - try { - Utils.matToBitmap(mRgba, bmp); - return bmp; - } catch(Exception e) { - Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage()); - bmp.recycle(); - return null; - } - } - - @Override - public void run() { - super.run(); - - synchronized (this) { - // Explicitly deallocate Mats - if (mRgba != null) - mRgba.release(); - if (mGray != null) - mGray.release(); - if (mIntermediateMat != null) - mIntermediateMat.release(); - - mRgba = null; - mGray = null; - mIntermediateMat = null; - } - } -} diff --git a/samples/android/tutorial-2-opencvcamera/src/org/opencv/samples/tutorial2/SampleCvViewBase.java b/samples/android/tutorial-2-opencvcamera/src/org/opencv/samples/tutorial2/SampleCvViewBase.java deleted file mode 100644 index 76924570ee..0000000000 --- a/samples/android/tutorial-2-opencvcamera/src/org/opencv/samples/tutorial2/SampleCvViewBase.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.opencv.samples.tutorial2; - -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; - - public SampleCvViewBase(Context context) { - super(context); - mHolder = getHolder(); - mHolder.addCallback(this); - 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"); - while (true) { - Bitmap bmp = null; - - synchronized (this) { - if (mCamera == null) - break; - - if (!mCamera.grab()) { - Log.e(TAG, "mCamera.grab() failed"); - break; - } - - bmp = processFrame(mCamera); - } - - 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); - mHolder.unlockCanvasAndPost(canvas); - } - bmp.recycle(); - } - } - Log.i(TAG, "Finished processing thread"); - } -} \ No newline at end of file diff --git a/samples/android/tutorial-3-native/.classpath b/samples/android/tutorial-3-native/.classpath index 6aed2ebfbe..d3bb07b44c 100644 --- a/samples/android/tutorial-3-native/.classpath +++ b/samples/android/tutorial-3-native/.classpath @@ -1,8 +1,8 @@ - - + + diff --git a/samples/android/tutorial-3-native/jni/jni_part.cpp b/samples/android/tutorial-3-native/jni/jni_part.cpp index f2e98e0a80..2448948d1a 100644 --- a/samples/android/tutorial-3-native/jni/jni_part.cpp +++ b/samples/android/tutorial-3-native/jni/jni_part.cpp @@ -8,28 +8,16 @@ using namespace std; using namespace cv; extern "C" { -JNIEXPORT void JNICALL Java_org_opencv_samples_tutorial3_Sample3View_FindFeatures(JNIEnv* env, jobject, jint width, jint height, jbyteArray yuv, jintArray bgra) +JNIEXPORT void JNICALL Java_org_opencv_samples_tutorial3_Sample3Native_FindFeatures(JNIEnv*, jobject, jlong addrGray, jlong addrRgba) { - jbyte* _yuv = env->GetByteArrayElements(yuv, 0); - jint* _bgra = env->GetIntArrayElements(bgra, 0); - - Mat myuv(height + height/2, width, CV_8UC1, (unsigned char *)_yuv); - Mat mbgra(height, width, CV_8UC4, (unsigned char *)_bgra); - Mat mgray(height, width, CV_8UC1, (unsigned char *)_yuv); - - //Please make attention about BGRA byte order - //ARGB stored in java as int array becomes BGRA at native level - cvtColor(myuv, mbgra, CV_YUV420sp2BGR, 4); - + Mat* pMatGr=(Mat*)addrGray; + Mat* pMatRgb=(Mat*)addrRgba; vector v; FastFeatureDetector detector(50); - detector.detect(mgray, v); + detector.detect(*pMatGr, v); for( size_t i = 0; i < v.size(); i++ ) - circle(mbgra, Point(v[i].pt.x, v[i].pt.y), 10, Scalar(0,0,255,255)); - - env->ReleaseIntArrayElements(bgra, _bgra, 0); - env->ReleaseByteArrayElements(yuv, _yuv, 0); + circle(*pMatRgb, Point(v[i].pt.x, v[i].pt.y), 10, Scalar(255,0,0,255)); } } diff --git a/samples/android/tutorial-3-native/res/layout/tutorial3_surface_view.xml b/samples/android/tutorial-3-native/res/layout/tutorial3_surface_view.xml new file mode 100644 index 0000000000..71cd6e04cb --- /dev/null +++ b/samples/android/tutorial-3-native/res/layout/tutorial3_surface_view.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/samples/android/tutorial-3-native/src/org/opencv/samples/tutorial3/Sample3Native.java b/samples/android/tutorial-3-native/src/org/opencv/samples/tutorial3/Sample3Native.java index 7dfdf16dc8..9e17793d17 100644 --- a/samples/android/tutorial-3-native/src/org/opencv/samples/tutorial3/Sample3Native.java +++ b/samples/android/tutorial-3-native/src/org/opencv/samples/tutorial3/Sample3Native.java @@ -3,21 +3,26 @@ package org.opencv.samples.tutorial3; import org.opencv.android.BaseLoaderCallback; import org.opencv.android.LoaderCallbackInterface; import org.opencv.android.OpenCVLoader; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.android.JavaCameraView; +import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener; +import org.opencv.imgproc.Imgproc; import android.app.Activity; -import android.app.AlertDialog; -import android.content.DialogInterface; import android.os.Bundle; import android.util.Log; import android.view.Window; import android.view.WindowManager; -public class Sample3Native extends Activity { +public class Sample3Native extends Activity implements CvCameraViewListener { private static final String TAG = "OCVSample::Activity"; - private Sample3View mView; + private Mat mRgba; + private Mat mGrayMat; + private JavaCameraView mOpenCvCameraView; - private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) { + private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { @Override public void onManagerConnected(int status) { switch (status) { @@ -28,37 +33,7 @@ public class Sample3Native extends Activity { // Load native library after(!) OpenCV initialization System.loadLibrary("native_sample"); - // Create and set View - mView = new Sample3View(mAppContext); - setContentView(mView); - // 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(); - } - }); - MarketErrorMessage.show(); + mOpenCvCameraView.enableView(); } break; default: { @@ -72,31 +47,56 @@ public class Sample3Native extends Activity { Log.i(TAG, "Instantiated new " + this.getClass()); } + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + Log.i(TAG, "called onCreate"); + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + setContentView(R.layout.tutorial3_surface_view); + + mOpenCvCameraView = (JavaCameraView)findViewById(R.id.tutorial4_activity_surface_view); + mOpenCvCameraView.setCvCameraViewListener(this); + } + @Override - protected void onPause() { - Log.i(TAG, "called onPause"); - if (null != mView) - mView.releaseCamera(); + public void onPause() + { + mOpenCvCameraView.disableView(); super.onPause(); } @Override - protected void onResume() { - Log.i(TAG, "called onResume"); + public void onResume() + { super.onResume(); + OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_2, this, mLoaderCallback); + } - 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"); - } + public void onDestroy() { + super.onDestroy(); + mOpenCvCameraView.disableView(); } - /** Called when the activity is first created. */ - @Override - public void onCreate(Bundle savedInstanceState) { - Log.i(TAG, "called onCreate"); - super.onCreate(savedInstanceState); - requestWindowFeature(Window.FEATURE_NO_TITLE); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + public void onCameraViewStarted(int width, int height) { + mRgba = new Mat(height, width, CvType.CV_8UC4); + mGrayMat = new Mat(height, width, CvType.CV_8UC1); + } + + public void onCameraViewStopped() { + mRgba.release(); + mGrayMat.release(); } + + public Mat onCameraFrame(Mat inputFrame) { + inputFrame.copyTo(mRgba); + Imgproc.cvtColor(mRgba, mGrayMat, Imgproc.COLOR_RGBA2GRAY); + FindFeatures(mGrayMat.getNativeObjAddr(), mRgba.getNativeObjAddr()); + + return mRgba; + } + + public native void FindFeatures(long matAddrGr, long matAddrRgba); } diff --git a/samples/android/tutorial-3-native/src/org/opencv/samples/tutorial3/Sample3View.java b/samples/android/tutorial-3-native/src/org/opencv/samples/tutorial3/Sample3View.java deleted file mode 100644 index c4b1caa06b..0000000000 --- a/samples/android/tutorial-3-native/src/org/opencv/samples/tutorial3/Sample3View.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.opencv.samples.tutorial3; - -import android.content.Context; -import android.graphics.Bitmap; -import android.util.Log; - -class Sample3View extends SampleViewBase { - private static final String TAG = "OCVSample::View"; - - private int mFrameSize; - private Bitmap mBitmap; - private int[] mRGBA; - - public Sample3View(Context context) { - super(context); - Log.i(TAG, "Instantiated new " + this.getClass()); - } - - @Override - protected void onPreviewStarted(int previewWidth, int previewHeight) { - Log.i(TAG, "called onPreviewStarted("+previewWidth+", "+previewHeight+")"); - - mFrameSize = previewWidth * previewHeight; - mRGBA = new int[mFrameSize]; - mBitmap = Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888); - } - - @Override - protected void onPreviewStopped() { - if(mBitmap != null) { - mBitmap.recycle(); - mBitmap = null; - } - mRGBA = null; - } - - @Override - protected Bitmap processFrame(byte[] data) { - int[] rgba = mRGBA; - - FindFeatures(getFrameWidth(), getFrameHeight(), data, rgba); - - Bitmap bmp = mBitmap; - bmp.setPixels(rgba, 0/* offset */, getFrameWidth() /* stride */, 0, 0, getFrameWidth(), getFrameHeight()); - return bmp; - } - - public native void FindFeatures(int width, int height, byte yuv[], int[] rgba); -} diff --git a/samples/android/tutorial-3-native/src/org/opencv/samples/tutorial3/SampleViewBase.java b/samples/android/tutorial-3-native/src/org/opencv/samples/tutorial3/SampleViewBase.java deleted file mode 100644 index 767806790a..0000000000 --- a/samples/android/tutorial-3-native/src/org/opencv/samples/tutorial3/SampleViewBase.java +++ /dev/null @@ -1,229 +0,0 @@ -package org.opencv.samples.tutorial3; - -import java.io.IOException; -import java.util.List; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.ImageFormat; -import android.graphics.SurfaceTexture; -import android.hardware.Camera; -import android.hardware.Camera.PreviewCallback; -import android.os.Build; -import android.util.Log; -import android.view.SurfaceHolder; -import android.view.SurfaceView; - -public abstract class SampleViewBase extends SurfaceView implements SurfaceHolder.Callback, Runnable { - private static final String TAG = "OCVSample::BaseView"; - - private Camera mCamera; - private SurfaceHolder mHolder; - private int mFrameWidth; - private int mFrameHeight; - private byte[] mFrame; - private volatile boolean mThreadRun; - private byte[] mBuffer; - private SurfaceTexture mSf; - - - public SampleViewBase(Context context) { - super(context); - mHolder = getHolder(); - mHolder.addCallback(this); - Log.i(TAG, "Instantiated new " + this.getClass()); - } - - public int getFrameWidth() { - return mFrameWidth; - } - - public int getFrameHeight() { - return mFrameHeight; - } - - public void setPreview() throws IOException { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - mSf = new SurfaceTexture(10); - mCamera.setPreviewTexture( mSf ); - } - else - mCamera.setPreviewDisplay(null); - } - - public boolean openCamera() { - Log.i(TAG, "Opening Camera"); - mCamera = null; - - try { - mCamera = Camera.open(); - } - catch (Exception e){ - Log.e(TAG, "Camera is not available (in use or does not exist): " + e.getLocalizedMessage()); - } - - if(mCamera == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { - for (int camIdx = 0; camIdx < Camera.getNumberOfCameras(); ++camIdx) { - try { - mCamera = Camera.open(camIdx); - } - catch (RuntimeException e) { - Log.e(TAG, "Camera #" + camIdx + "failed to open: " + e.getLocalizedMessage()); - } - } - } - - if(mCamera == null) { - Log.e(TAG, "Can't open any camera"); - return false; - } - - mCamera.setPreviewCallbackWithBuffer(new PreviewCallback() { - public void onPreviewFrame(byte[] data, Camera camera) { - synchronized (SampleViewBase.this) { - System.arraycopy(data, 0, mFrame, 0, data.length); - SampleViewBase.this.notify(); - } - camera.addCallbackBuffer(mBuffer); - } - }); - - return true; - } - - public void releaseCamera() { - Log.i(TAG, "Releasing Camera"); - mThreadRun = false; - synchronized (this) { - if (mCamera != null) { - mCamera.stopPreview(); - mCamera.setPreviewCallback(null); - mCamera.release(); - mCamera = null; - } - } - onPreviewStopped(); - } - - public synchronized void setupCamera(int width, int height) { - if (mCamera != null) { - Log.i(TAG, "Setup Camera - " + width + "x" + height); - Camera.Parameters params = mCamera.getParameters(); - List sizes = params.getSupportedPreviewSizes(); - mFrameWidth = width; - mFrameHeight = height; - - // selecting optimal camera preview size - { - int minDiff = Integer.MAX_VALUE; - for (Camera.Size size : sizes) { - if (Math.abs(size.height - height) < minDiff) { - mFrameWidth = size.width; - mFrameHeight = size.height; - minDiff = Math.abs(size.height - height); - } - } - } - - params.setPreviewSize(getFrameWidth(), getFrameHeight()); - - List FocusModes = params.getSupportedFocusModes(); - if (FocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) - { - params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); - } - - mCamera.setParameters(params); - - /* Now allocate the buffer */ - params = mCamera.getParameters(); - int size = params.getPreviewSize().width * params.getPreviewSize().height; - size = size * ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8; - mBuffer = new byte[size]; - /* The buffer where the current frame will be copied */ - mFrame = new byte [size]; - mCamera.addCallbackBuffer(mBuffer); - - /* Notify that the preview is about to be started and deliver preview size */ - onPreviewStarted(params.getPreviewSize().width, params.getPreviewSize().height); - - try { - setPreview(); - } catch (IOException e) { - Log.e(TAG, "mCamera.setPreviewDisplay/setPreviewTexture fails: " + e); - } - - /* Now we can start a preview */ - mCamera.startPreview(); - } - } - - public void surfaceChanged(SurfaceHolder _holder, int format, int width, int height) { - Log.i(TAG, "called surfaceChanged"); - // stop preview before making changes - try { - mCamera.stopPreview(); - } catch (Exception e){ - // ignore: tried to stop a non-existent preview - } - - // start preview with new settings - 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"); - } - - /* The bitmap returned by this method shall be owned by the child and released in onPreviewStopped() */ - protected abstract Bitmap processFrame(byte[] data); - - /** - * This method is called when the preview process is being started. It is called before the first frame delivered and processFrame is called - * It is called with the width and height parameters of the preview process. It can be used to prepare the data needed during the frame processing. - * @param previewWidth - the width of the preview frames that will be delivered via processFrame - * @param previewHeight - the height of the preview frames that will be delivered via processFrame - */ - protected abstract void onPreviewStarted(int previewWidtd, int previewHeight); - - /** - * This method is called when preview is stopped. When this method is called the preview stopped and all the processing of frames already completed. - * If the Bitmap object returned via processFrame is cached - it is a good time to recycle it. - * Any other resources used during the preview can be released. - */ - protected abstract void onPreviewStopped(); - - public void run() { - mThreadRun = true; - Log.i(TAG, "Started processing thread"); - while (mThreadRun) { - Bitmap bmp = null; - - synchronized (this) { - try { - this.wait(); - if (!mThreadRun) - break; - bmp = processFrame(mFrame); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - if (bmp != null) { - Canvas canvas = mHolder.lockCanvas(); - if (canvas != null) { - canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR); - canvas.drawBitmap(bmp, (canvas.getWidth() - getFrameWidth()) / 2, (canvas.getHeight() - getFrameHeight()) / 2, null); - mHolder.unlockCanvasAndPost(canvas); - } - } - } - Log.i(TAG, "Finished processing thread"); - } -} \ No newline at end of file diff --git a/samples/android/tutorial-4-mixed/.classpath b/samples/android/tutorial-4-mixed/.classpath index a4763d1eec..3f9691c5dd 100644 --- a/samples/android/tutorial-4-mixed/.classpath +++ b/samples/android/tutorial-4-mixed/.classpath @@ -1,8 +1,8 @@ - - + + diff --git a/samples/android/tutorial-4-mixed/jni/jni_part.cpp b/samples/android/tutorial-4-mixed/jni/jni_part.cpp index 43015df6d6..6b0b48ee86 100644 --- a/samples/android/tutorial-4-mixed/jni/jni_part.cpp +++ b/samples/android/tutorial-4-mixed/jni/jni_part.cpp @@ -8,7 +8,7 @@ using namespace std; using namespace cv; extern "C" { -JNIEXPORT void JNICALL Java_org_opencv_samples_tutorial4_Sample4View_FindFeatures(JNIEnv*, jobject, jlong addrGray, jlong addrRgba) +JNIEXPORT void JNICALL Java_org_opencv_samples_tutorial4_Sample4Mixed_FindFeatures(JNIEnv*, jobject, jlong addrGray, jlong addrRgba) { Mat* pMatGr=(Mat*)addrGray; Mat* pMatRgb=(Mat*)addrRgba; diff --git a/samples/android/tutorial-4-mixed/res/layout/tutorial4_surface_view.xml b/samples/android/tutorial-4-mixed/res/layout/tutorial4_surface_view.xml new file mode 100644 index 0000000000..71cd6e04cb --- /dev/null +++ b/samples/android/tutorial-4-mixed/res/layout/tutorial4_surface_view.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/Sample4Mixed.java b/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/Sample4Mixed.java index ece3c20f99..2a3eb99733 100644 --- a/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/Sample4Mixed.java +++ b/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/Sample4Mixed.java @@ -3,10 +3,14 @@ package org.opencv.samples.tutorial4; import org.opencv.android.BaseLoaderCallback; import org.opencv.android.LoaderCallbackInterface; import org.opencv.android.OpenCVLoader; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.android.JavaCameraView; +import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener; +import org.opencv.highgui.Highgui; +import org.opencv.imgproc.Imgproc; import android.app.Activity; -import android.app.AlertDialog; -import android.content.DialogInterface; import android.os.Bundle; import android.util.Log; import android.view.Menu; @@ -14,16 +18,27 @@ import android.view.MenuItem; import android.view.Window; import android.view.WindowManager; -public class Sample4Mixed extends Activity { +public class Sample4Mixed extends Activity implements CvCameraViewListener { private static final String TAG = "OCVSample::Activity"; - private MenuItem mItemPreviewRGBA; - private MenuItem mItemPreviewGray; - private MenuItem mItemPreviewCanny; - private MenuItem mItemPreviewFeatures; - private Sample4View mView; + private static final int VIEW_MODE_RGBA = 0; + private static final int VIEW_MODE_GRAY = 1; + private static final int VIEW_MODE_CANNY = 2; + private static final int VIEW_MODE_FEATURES = 5; - private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) { + private int mViewMode; + private Mat mRgba; + private Mat mIntermediateMat; + private Mat mGrayMat; + + private MenuItem mItemPreviewRGBA; + private MenuItem mItemPreviewGray; + private MenuItem mItemPreviewCanny; + private MenuItem mItemPreviewFeatures; + + private JavaCameraView mOpenCvCameraView; + + private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { @Override public void onManagerConnected(int status) { switch (status) { @@ -34,39 +49,7 @@ public class Sample4Mixed extends Activity { // Load native library after(!) OpenCV initialization System.loadLibrary("mixed_sample"); - // Create and set View - mView = new Sample4View(mAppContext); - setContentView(mView); - - // 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(); - } - }); - MarketErrorMessage.show(); + mOpenCvCameraView.enableView(); } break; default: { @@ -80,25 +63,6 @@ public class Sample4Mixed extends Activity { 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) { @@ -106,6 +70,11 @@ public class Sample4Mixed extends Activity { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + setContentView(R.layout.tutorial4_surface_view); + + mOpenCvCameraView = (JavaCameraView)findViewById(R.id.tutorial4_activity_surface_view); + mOpenCvCameraView.setCvCameraViewListener(this); } @Override @@ -119,17 +88,83 @@ public class Sample4Mixed extends Activity { } @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) { + mRgba = new Mat(height, width, CvType.CV_8UC4); + mIntermediateMat = new Mat(height, width, CvType.CV_8UC4); + mGrayMat = new Mat(height, width, CvType.CV_8UC1); + } + + public void onCameraViewStopped() { + mRgba.release(); + mGrayMat.release(); + mIntermediateMat.release(); + } + + public Mat onCameraFrame(Mat inputFrame) { + final int viewMode = mViewMode; + + switch (viewMode) { + case VIEW_MODE_GRAY: + // input frame has gray scale format + Imgproc.cvtColor(inputFrame, mRgba, Imgproc.COLOR_GRAY2RGBA, 4); + break; + case VIEW_MODE_RGBA: + // input frame has RBGA format + inputFrame.copyTo(mRgba); + break; + case VIEW_MODE_CANNY: + // input frame has gray scale format + Imgproc.Canny(inputFrame, mIntermediateMat, 80, 100); + Imgproc.cvtColor(mIntermediateMat, mRgba, Imgproc.COLOR_GRAY2BGRA, 4); + break; + case VIEW_MODE_FEATURES: + // input frame has RGBA format + inputFrame.copyTo(mRgba); + Imgproc.cvtColor(mRgba, mGrayMat, Imgproc.COLOR_RGBA2GRAY); + FindFeatures(mGrayMat.getNativeObjAddr(), mRgba.getNativeObjAddr()); + break; + } + + return mRgba; + } + public boolean onOptionsItemSelected(MenuItem item) { Log.i(TAG, "called onOptionsItemSelected; selected item: " + item); + if (item == mItemPreviewRGBA) { - mView.setViewMode(Sample4View.VIEW_MODE_RGBA); + mOpenCvCameraView.SetCaptureFormat(Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA); + mViewMode = VIEW_MODE_RGBA; } else if (item == mItemPreviewGray) { - mView.setViewMode(Sample4View.VIEW_MODE_GRAY); + mOpenCvCameraView.SetCaptureFormat(Highgui.CV_CAP_ANDROID_GREY_FRAME); + mViewMode = VIEW_MODE_GRAY; } else if (item == mItemPreviewCanny) { - mView.setViewMode(Sample4View.VIEW_MODE_CANNY); + mOpenCvCameraView.SetCaptureFormat(Highgui.CV_CAP_ANDROID_GREY_FRAME); + mViewMode = VIEW_MODE_CANNY; } else if (item == mItemPreviewFeatures) { - mView.setViewMode(Sample4View.VIEW_MODE_FEATURES); + mViewMode = VIEW_MODE_FEATURES; + mOpenCvCameraView.SetCaptureFormat(Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA); } + return true; } + + public native void FindFeatures(long matAddrGr, long matAddrRgba); } diff --git a/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/Sample4View.java b/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/Sample4View.java deleted file mode 100644 index 2b44ecac98..0000000000 --- a/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/Sample4View.java +++ /dev/null @@ -1,116 +0,0 @@ -package org.opencv.samples.tutorial4; - -import org.opencv.android.Utils; -import org.opencv.core.CvType; -import org.opencv.core.Mat; -import org.opencv.imgproc.Imgproc; - -import android.content.Context; -import android.graphics.Bitmap; -import android.util.Log; - -class Sample4View extends SampleViewBase { - private static final String TAG = "OCVSample::View"; - - public static final int VIEW_MODE_RGBA = 0; - public static final int VIEW_MODE_GRAY = 1; - public static final int VIEW_MODE_CANNY = 2; - public static final int VIEW_MODE_FEATURES = 5; - - private Mat mYuv; - private Mat mRgba; - private Mat mGraySubmat; - private Mat mIntermediateMat; - private Bitmap mBitmap; - private int mViewMode; - - public Sample4View(Context context) { - super(context); - } - - @Override - protected void onPreviewStarted(int previewWidth, int previewHeight) { - Log.i(TAG, "called onPreviewStarted("+previewWidth+", "+previewHeight+")"); - - // initialize Mats before usage - mYuv = new Mat(getFrameHeight() + getFrameHeight() / 2, getFrameWidth(), CvType.CV_8UC1); - mGraySubmat = mYuv.submat(0, getFrameHeight(), 0, getFrameWidth()); - - mRgba = new Mat(); - mIntermediateMat = new Mat(); - - mBitmap = Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888); - } - - @Override - protected void onPreviewStopped() { - Log.i(TAG, "called onPreviewStopped"); - - if (mBitmap != null) { - mBitmap.recycle(); - mBitmap = null; - } - - synchronized (this) { - // Explicitly deallocate Mats - if (mYuv != null) - mYuv.release(); - if (mRgba != null) - mRgba.release(); - if (mGraySubmat != null) - mGraySubmat.release(); - if (mIntermediateMat != null) - mIntermediateMat.release(); - - mYuv = null; - mRgba = null; - mGraySubmat = null; - mIntermediateMat = null; - } - - } - - - @Override - protected Bitmap processFrame(byte[] data) { - mYuv.put(0, 0, data); - - final int viewMode = mViewMode; - - switch (viewMode) { - case VIEW_MODE_GRAY: - Imgproc.cvtColor(mGraySubmat, mRgba, Imgproc.COLOR_GRAY2RGBA, 4); - break; - case VIEW_MODE_RGBA: - Imgproc.cvtColor(mYuv, mRgba, Imgproc.COLOR_YUV420sp2RGB, 4); - break; - case VIEW_MODE_CANNY: - Imgproc.Canny(mGraySubmat, mIntermediateMat, 80, 100); - Imgproc.cvtColor(mIntermediateMat, mRgba, Imgproc.COLOR_GRAY2BGRA, 4); - break; - case VIEW_MODE_FEATURES: - Imgproc.cvtColor(mYuv, mRgba, Imgproc.COLOR_YUV420sp2RGB, 4); - FindFeatures(mGraySubmat.getNativeObjAddr(), mRgba.getNativeObjAddr()); - break; - } - - Bitmap bmp = mBitmap; - - try { - Utils.matToBitmap(mRgba, bmp); - } catch(Exception e) { - Log.e("org.opencv.samples.puzzle15", "Utils.matToBitmap() throws an exception: " + e.getMessage()); - bmp.recycle(); - bmp = null; - } - - return bmp; - } - - public native void FindFeatures(long matAddrGr, long matAddrRgba); - - public void setViewMode(int viewMode) { - Log.i(TAG, "called setViewMode("+viewMode+")"); - mViewMode = viewMode; - } -} diff --git a/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/SampleViewBase.java b/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/SampleViewBase.java deleted file mode 100644 index cddb22f056..0000000000 --- a/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/SampleViewBase.java +++ /dev/null @@ -1,229 +0,0 @@ -package org.opencv.samples.tutorial4; - -import java.io.IOException; -import java.util.List; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.ImageFormat; -import android.graphics.SurfaceTexture; -import android.hardware.Camera; -import android.hardware.Camera.PreviewCallback; -import android.os.Build; -import android.util.Log; -import android.view.SurfaceHolder; -import android.view.SurfaceView; - -public abstract class SampleViewBase extends SurfaceView implements SurfaceHolder.Callback, Runnable { - private static final String TAG = "OCVSample::BaseView"; - - private Camera mCamera; - private SurfaceHolder mHolder; - private int mFrameWidth; - private int mFrameHeight; - private byte[] mFrame; - private volatile boolean mThreadRun; - private byte[] mBuffer; - private SurfaceTexture mSf; - - - public SampleViewBase(Context context) { - super(context); - mHolder = getHolder(); - mHolder.addCallback(this); - Log.i(TAG, "Instantiated new " + this.getClass()); - } - - public int getFrameWidth() { - return mFrameWidth; - } - - public int getFrameHeight() { - return mFrameHeight; - } - - public void setPreview() throws IOException { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - mSf = new SurfaceTexture(10); - mCamera.setPreviewTexture( mSf ); - } - else - mCamera.setPreviewDisplay(null); - } - - public boolean openCamera() { - Log.i(TAG, "Opening Camera"); - mCamera = null; - - try { - mCamera = Camera.open(); - } - catch (Exception e){ - Log.e(TAG, "Camera is not available (in use or does not exist): " + e.getLocalizedMessage()); - } - - if(mCamera == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { - for (int camIdx = 0; camIdx < Camera.getNumberOfCameras(); ++camIdx) { - try { - mCamera = Camera.open(camIdx); - } - catch (RuntimeException e) { - Log.e(TAG, "Camera #" + camIdx + "failed to open: " + e.getLocalizedMessage()); - } - } - } - - if(mCamera == null) { - Log.e(TAG, "Can't open any camera"); - return false; - } - - mCamera.setPreviewCallbackWithBuffer(new PreviewCallback() { - public void onPreviewFrame(byte[] data, Camera camera) { - synchronized (SampleViewBase.this) { - System.arraycopy(data, 0, mFrame, 0, data.length); - SampleViewBase.this.notify(); - } - camera.addCallbackBuffer(mBuffer); - } - }); - - return true; - } - - public void releaseCamera() { - Log.i(TAG, "Releasing Camera"); - mThreadRun = false; - synchronized (this) { - if (mCamera != null) { - mCamera.stopPreview(); - mCamera.release(); - mCamera = null; - } - } - onPreviewStopped(); - } - - public synchronized void setupCamera(int width, int height) { - if (mCamera != null) { - Log.i(TAG, "Setup Camera - " + width + "x" + height); - Camera.Parameters params = mCamera.getParameters(); - List sizes = params.getSupportedPreviewSizes(); - mFrameWidth = width; - mFrameHeight = height; - - // selecting optimal camera preview size - { - int minDiff = Integer.MAX_VALUE; - for (Camera.Size size : sizes) { - if (Math.abs(size.height - height) < minDiff) { - mFrameWidth = size.width; - mFrameHeight = size.height; - minDiff = Math.abs(size.height - height); - } - } - } - - params.setPreviewSize(getFrameWidth(), getFrameHeight()); - - List FocusModes = params.getSupportedFocusModes(); - if (FocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) - { - params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); - } - - mCamera.setParameters(params); - - /* Now allocate the buffer */ - params = mCamera.getParameters(); - int size = params.getPreviewSize().width * params.getPreviewSize().height; - size = size * ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8; - mBuffer = new byte[size]; - /* The buffer where the current frame will be copied */ - mFrame = new byte [size]; - mCamera.addCallbackBuffer(mBuffer); - - /* Notify that the preview is about to be started and deliver preview size */ - onPreviewStarted(params.getPreviewSize().width, params.getPreviewSize().height); - - try { - setPreview(); - } catch (IOException e) { - Log.e(TAG, "mCamera.setPreviewDisplay/setPreviewTexture fails: " + e); - } - - /* Now we can start a preview */ - mCamera.startPreview(); - } - } - - public void surfaceChanged(SurfaceHolder _holder, int format, int width, int height) { - Log.i(TAG, "called surfaceChanged"); - // stop preview before making changes - try { - mCamera.stopPreview(); - } catch (Exception e){ - // ignore: tried to stop a non-existent preview - } - - // start preview with new settings - 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"); - } - - /* The bitmap returned by this method shall be owned by the child and released in onPreviewStopped() */ - protected abstract Bitmap processFrame(byte[] data); - - /** - * This method is called when the preview process is being started. It is called before the first frame delivered and processFrame is called - * It is called with the width and height parameters of the preview process. It can be used to prepare the data needed during the frame processing. - * @param previewWidth - the width of the preview frames that will be delivered via processFrame - * @param previewHeight - the height of the preview frames that will be delivered via processFrame - */ - protected abstract void onPreviewStarted(int previewWidtd, int previewHeight); - - /** - * This method is called when preview is stopped. When this method is called the preview stopped and all the processing of frames already completed. - * If the Bitmap object returned via processFrame is cached - it is a good time to recycle it. - * Any other resources used during the preview can be released. - */ - protected abstract void onPreviewStopped(); - - public void run() { - mThreadRun = true; - Log.i(TAG, "Started processing thread"); - while (mThreadRun) { - Bitmap bmp = null; - - synchronized (this) { - try { - this.wait(); - if (!mThreadRun) - break; - bmp = processFrame(mFrame); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - if (bmp != null) { - Canvas canvas = mHolder.lockCanvas(); - if (canvas != null) { - canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR); - canvas.drawBitmap(bmp, (canvas.getWidth() - getFrameWidth()) / 2, (canvas.getHeight() - getFrameHeight()) / 2, null); - mHolder.unlockCanvasAndPost(canvas); - } - } - } - Log.i(TAG, "Finished processing thread"); - } -} \ No newline at end of file