Tutorial for Android synchronized with actual application framework.
authorAlexander Smorkalov <alexander.smorkalov@itseez.com>
Fri, 8 Feb 2013 08:03:13 +0000 (12:03 +0400)
committerAlexander Smorkalov <alexander.smorkalov@itseez.com>
Fri, 8 Feb 2013 12:53:06 +0000 (16:53 +0400)
doc/tutorials/introduction/android_binary_package/dev_with_OCV_on_Android.rst
doc/tutorials/introduction/table_of_content_introduction/table_of_content_introduction.rst

index 9a96307..0bfc6ac 100644 (file)
@@ -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() }
-        }
+        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:tools="http://schemas.android.com/tools"
+            xmlns:opencv="http://schemas.android.com/apk/res-auto"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" >
 
-        public boolean cameraOpen() {
-            return false; //TODO: open camera
-        }
+            <org.opencv.android.JavaCameraView
+                android:layout_width="fill_parent"
+                android:layout_height="fill_parent"
+                android:visibility="gone"
+                android:id="@+id/HelloOpenCvView"
+                opencv:show_fps="true"
+                opencv:camera_id="any" />
 
-        private void cameraRelease() {
-            // TODO release camera
-        }
-
-        private void cameraSetup(int width, int height) {
-            // TODO setup camera
-        }
+        </LinearLayout>
 
-#. 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);
-      }
+      </application>
 
-      protected void onPause() {
-          super.onPause();
-          mView.cameraRelease();
-      }
+      <uses-permission android:name="android.permission.CAMERA"/>
 
-      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();
-          }
-      }
+      <uses-feature android:name="android.hardware.camera" android:required="false"/>
+      <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
+      <uses-feature android:name="android.hardware.camera.front" android:required="false"/>
+      <uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>
 
-#. 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:
 
-      </application>
+      <application
+          android:icon="@drawable/icon"
+          android:label="@string/app_name"
+          android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
+
+#. 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;
+               }
+           }
+       };
 
-      <uses-permission android:name="android.permission.CAMERA" />
-      <uses-feature android:name="android.hardware.camera" />
-      <uses-feature android:name="android.hardware.camera.autofocus" />
-
-#. 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<Size> 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.
index 3abf2aa..d918c14 100644 (file)
@@ -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|