From c7e7b770934bd01786a0bf8937fd84de41ffad62 Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Fri, 8 Feb 2013 12:03:13 +0400 Subject: [PATCH] Tutorial for Android synchronized with actual application framework. --- .../dev_with_OCV_on_Android.rst | 365 +++++++++------------ .../table_of_content_introduction.rst | 2 +- 2 files changed, 153 insertions(+), 214 deletions(-) diff --git a/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.rst b/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.rst index 9a96307..0bfc6ac 100644 --- a/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.rst +++ b/doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.rst @@ -78,39 +78,33 @@ See the "15-puzzle" OpenCV sample for details. .. code-block:: java :linenos: - public class MyActivity extends Activity implements HelperCallbackInterface - { - 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); - } break; - default: - { - super.onManagerConnected(status); - } break; - } - } - }; - - /** Call on every application resume **/ - @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)) + public class Sample1Java extends Activity implements CvCameraViewListener { + + private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { + @Override + public void onManagerConnected(int status) { + switch (status) { + case LoaderCallbackInterface.SUCCESS: + { + Log.i(TAG, "OpenCV loaded successfully"); + mOpenCvCameraView.enableView(); + } break; + default: + { + super.onManagerConnected(status); + } break; + } + } + }; + + @Override + public void onResume() { - Log.e(TAG, "Cannot connect to OpenCV Manager"); + super.onResume(); + OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback); } + + ... } It this case application works with OpenCV Manager in asynchronous fashion. ``OnManagerConnected`` @@ -297,220 +291,165 @@ application. It will be capable of accessing camera output, processing it and di result. #. Open Eclipse IDE, create a new clean workspace, create a new Android project - :menuselection:`File --> New --> Android Project`. - -#. Set name, target, package and ``minSDKVersion`` accordingly. - -#. Create a new class :menuselection:`File -> New -> Class`. Name it for example: - *HelloOpenCVView*. - - .. image:: images/dev_OCV_new_class.png - :alt: Add a new class. - :align: center + :menuselection:`File --> New --> Android Project` - * It should extend ``SurfaceView`` class. - * It also should implement ``SurfaceHolder.Callback``, ``Runnable``. +#. Set name, target, package and ``minSDKVersion`` accordingly. The minimal SDK version for build + with OpenCV4Android SDK is 11. Minimal device API Level (for application manifest) is 8. -#. Edit ``HelloOpenCVView`` class. +#. Allow Eclipse to create default activity. Lets name the activity ``HelloOpenCvActivity``. - * Add an ``import`` line for ``android.content.context``. +#. Choose Blank Activity with full screen layout. Lets name the layout ``HelloOpenCvLayout``. - * Modify autogenerated stubs: ``HelloOpenCVView``, ``surfaceCreated``, ``surfaceDestroyed`` and - ``surfaceChanged``. - - .. code-block:: java - :linenos: - - package com.hello.opencv.test; - - import android.content.Context; - - public class HelloOpenCVView extends SurfaceView implements Callback, Runnable { - - public HelloOpenCVView(Context context) { - super(context); - getHolder().addCallback(this); - } - - public void surfaceCreated(SurfaceHolder holder) { - (new Thread(this)).start(); - } +#. Import OpenCV library project to your workspace. - public void surfaceDestroyed(SurfaceHolder holder) { - cameraRelease(); - } - - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - cameraSetup(width, height); - } +#. Reference OpenCV library within your project properties. - * Add ``cameraOpen``, ``cameraRelease`` and ``cameraSetup`` voids as shown below. + .. image:: images/dev_OCV_reference.png + :alt: Reference OpenCV library. + :align: center - * Also, don't forget to add the public void ``run()`` as follows: +#. Edit your layout file as xml file and pass the following layout there: - .. code-block:: java + .. code-block:: xml :linenos: - public void run() { - // TODO: loop { getFrame(), processFrame(), drawFrame() } - } + - public boolean cameraOpen() { - return false; //TODO: open camera - } + - private void cameraRelease() { - // TODO release camera - } - - private void cameraSetup(int width, int height) { - // TODO setup camera - } + -#. Create a new ``Activity`` :menuselection:`New -> Other -> Android -> Android Activity` and name - it, for example: *HelloOpenCVActivity*. For this activity define ``onCreate``, ``onResume`` and - ``onPause`` voids. +#. Add the following permissions to the :file:`AndroidManifest.xml` file: - .. code-block:: java + .. code-block:: xml :linenos: - public void onCreate (Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mView = new HelloOpenCVView(this); - setContentView (mView); - } + - protected void onPause() { - super.onPause(); - mView.cameraRelease(); - } + - protected void onResume() { - super.onResume(); - if( !mView.cameraOpen() ) { - // MessageBox and exit app - AlertDialog ad = new AlertDialog.Builder(this).create(); - ad.setCancelable(false); // This blocks the "BACK" button - ad.setMessage("Fatal error: can't open camera!"); - ad.setButton("OK", new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - finish(); - } - }); - ad.show(); - } - } + + + + -#. Add the following permissions to the :file:`AndroidManifest.xml` file: +#. Set application theme in AndroidManifest.xml to hide title and system buttons. .. code-block:: xml :linenos: - + + +#. Add OpenCV library initialization to your activity. Fix errors by adding requited imports. + + .. code-block:: java + :linenos: + + private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { + @Override + public void onManagerConnected(int status) { + switch (status) { + case LoaderCallbackInterface.SUCCESS: + { + Log.i(TAG, "OpenCV loaded successfully"); + mOpenCvCameraView.enableView(); + } break; + default: + { + super.onManagerConnected(status); + } break; + } + } + }; - - - - -#. Reference OpenCV library within your project properties. - - .. image:: images/dev_OCV_reference.png - :alt: Reference OpenCV library. - :align: center + @Override + public void onResume() + { + super.onResume(); + OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback); + } -#. We now need some code to handle the camera. Update the ``HelloOpenCVView`` class as follows: +#. Defines that your activity implements CvViewFrameListener interface and fix activity related + errors by defining missed methods. For this activity define ``onCreate``, ``onDestroy`` and + ``onPause`` and implement them according code snippet bellow. Fix errors by adding requited + imports. .. code-block:: java :linenos: - private VideoCapture mCamera; - - public boolean cameraOpen() { - synchronized (this) { - cameraRelease(); - mCamera = new VideoCapture(Highgui.CV_CAP_ANDROID); - if (!mCamera.isOpened()) { - mCamera.release(); - mCamera = null; - Log.e("HelloOpenCVView", "Failed to open native camera"); - return false; - } - } - return true; - } + private CameraBridgeViewBase mOpenCvCameraView; - public void cameraRelease() { - synchronized(this) { - if (mCamera != null) { - mCamera.release(); - mCamera = null; - } - } - } + @Override + public void onCreate(Bundle savedInstanceState) { + Log.i(TAG, "called onCreate"); + super.onCreate(savedInstanceState); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + setContentView(R.layout.HelloOpenCvLayout); + mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.HelloOpenCvView); + mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE); + mOpenCvCameraView.setCvCameraViewListener(this); + } - private void cameraSetup(int width, int height) { - synchronized (this) { - if (mCamera != null && mCamera.isOpened()) { - 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); - } - } - } + @Override + public void onPause() + { + super.onPause(); + if (mOpenCvCameraView != null) + mOpenCvCameraView.disableView(); + } -#. The last step would be to update the ``run()`` void in ``HelloOpenCVView`` class as follows: + public void onDestroy() { + super.onDestroy(); + if (mOpenCvCameraView != null) + mOpenCvCameraView.disableView(); + } - .. code-block:: java - :linenos: + public void onCameraViewStarted(int width, int height) { + } - public void run() { - while (true) { - Bitmap bmp = null; - synchronized (this) { - if (mCamera == null) - break; - if (!mCamera.grab()) - break; - - bmp = processFrame(mCamera); - } - if (bmp != null) { - Canvas canvas = getHolder().lockCanvas(); - if (canvas != null) { - canvas.drawBitmap(bmp, (canvas.getWidth() - bmp.getWidth()) / 2, - (canvas.getHeight() - bmp.getHeight()) / 2, null); - getHolder().unlockCanvasAndPost(canvas); - - } - bmp.recycle(); - } - } - } + public void onCameraViewStopped() { + } - protected Bitmap processFrame(VideoCapture capture) { - Mat mRgba = new Mat(); - capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA); - //process mRgba - Bitmap bmp = Bitmap.createBitmap(mRgba.cols(), mRgba.rows(), Bitmap.Config.ARGB_8888); - try { - Utils.matToBitmap(mRgba, bmp); - } catch(Exception e) { - Log.e("processFrame", "Utils.matToBitmap() throws an exception: " + e.getMessage()); - bmp.recycle(); - bmp = null; - } - return bmp; - } + public Mat onCameraFrame(Mat inputFrame) { + return inputFrame; + } + +#. Run your application on device or emulator. + +Lets discuss some most important steps. Every Android application with UI must implement Activity +and View. By the first steps we create blank activity and default view layout. The simplest +OpenCV-centric application must implement OpenCV initialization, create its own view to show +preview from camera and implements ``CvViewFrameListener`` interface to get frames from camera and +process it. + +First of all we create our application view using xml layout. Our layout consists of the only +one full screen component of class ``org.opencv.android.JavaCameraView``. This class is +implemented inside OpenCV library. It is inherited from ``CameraBridgeViewBase``, that extends +``SurfaceView`` and uses standard Android camera API. Alternatively you can use +``org.opencv.android.NativeCameraView`` class, that implements the same interface, but uses +``VideoCapture`` class as camera access back-end. ``opencv:show_fps="true"`` and +``opencv:camera_id="any"`` options enable FPS message and allow to use any camera on device. +Application tries to use back camera first. + +After creating layout we need to implement ``Activity`` class. OpenCV initialization process has +been already discussed above. In this sample we use asynchronous initialization. Implementation of +``CvCameraViewListener`` interface allows you to add processing steps after frame grabbing from +camera and before its rendering on screen. The most important function is ``onCameraFrame``. It is +callback function and it is called on retrieving frame from camera. The callback input is frame +from camera. RGBA format is used by default. You can change this behavior by ``SetCaptureFormat`` +method of ``View`` class. ``Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA`` and +``Highgui.CV_CAP_ANDROID_GREY_FRAME`` are supported. It expects that function returns RGBA frame +that will be drawn on the screen. diff --git a/doc/tutorials/introduction/table_of_content_introduction/table_of_content_introduction.rst b/doc/tutorials/introduction/table_of_content_introduction/table_of_content_introduction.rst index 3abf2aa..d918c14 100644 --- a/doc/tutorials/introduction/table_of_content_introduction/table_of_content_introduction.rst +++ b/doc/tutorials/introduction/table_of_content_introduction/table_of_content_introduction.rst @@ -137,7 +137,7 @@ Here you can read tutorials about how to set up your computer to work with the O ================ ================================================= |AndroidLogo| **Title:** :ref:`dev_with_OCV_on_Android` - *Compatibility:* > OpenCV 2.4.2 + *Compatibility:* > OpenCV 2.4.3 *Author:* |Author_VsevolodG| -- 2.7.4