Commit c7e7b770 authored by Alexander Smorkalov's avatar Alexander Smorkalov

Tutorial for Android synchronized with actual application framework.

parent 03f7d2f1
...@@ -78,18 +78,16 @@ See the "15-puzzle" OpenCV sample for details. ...@@ -78,18 +78,16 @@ See the "15-puzzle" OpenCV sample for details.
.. code-block:: java .. code-block:: java
:linenos: :linenos:
public class MyActivity extends Activity implements HelperCallbackInterface public class Sample1Java extends Activity implements CvCameraViewListener {
{
private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) { private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override @Override
public void onManagerConnected(int status) { public void onManagerConnected(int status) {
switch (status) { switch (status) {
case LoaderCallbackInterface.SUCCESS: case LoaderCallbackInterface.SUCCESS:
{ {
Log.i(TAG, "OpenCV loaded successfully"); Log.i(TAG, "OpenCV loaded successfully");
// Create and set View mOpenCvCameraView.enableView();
mView = new puzzle15View(mAppContext);
setContentView(mView);
} break; } break;
default: default:
{ {
...@@ -99,18 +97,14 @@ See the "15-puzzle" OpenCV sample for details. ...@@ -99,18 +97,14 @@ See the "15-puzzle" OpenCV sample for details.
} }
}; };
/** Call on every application resume **/
@Override @Override
protected void onResume() public void onResume()
{ {
Log.i(TAG, "called onResume");
super.onResume(); super.onResume();
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, 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");
} }
...
} }
It this case application works with OpenCV Manager in asynchronous fashion. ``OnManagerConnected`` 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 ...@@ -297,220 +291,165 @@ application. It will be capable of accessing camera output, processing it and di
result. result.
#. Open Eclipse IDE, create a new clean workspace, create a new Android project #. Open Eclipse IDE, create a new clean workspace, create a new Android project
:menuselection:`File --> New --> Android Project`. :menuselection:`File --> New --> Android Project`
#. Set name, target, package and ``minSDKVersion`` accordingly. #. 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.
#. Create a new class :menuselection:`File -> New -> Class`. Name it for example: #. Allow Eclipse to create default activity. Lets name the activity ``HelloOpenCvActivity``.
*HelloOpenCVView*.
.. image:: images/dev_OCV_new_class.png #. Choose Blank Activity with full screen layout. Lets name the layout ``HelloOpenCvLayout``.
:alt: Add a new class.
:align: center
* It should extend ``SurfaceView`` class. #. Import OpenCV library project to your workspace.
* It also should implement ``SurfaceHolder.Callback``, ``Runnable``.
#. Edit ``HelloOpenCVView`` class. #. Reference OpenCV library within your project properties.
* Add an ``import`` line for ``android.content.context``. .. image:: images/dev_OCV_reference.png
:alt: Reference OpenCV library.
:align: center
* Modify autogenerated stubs: ``HelloOpenCVView``, ``surfaceCreated``, ``surfaceDestroyed`` and #. Edit your layout file as xml file and pass the following layout there:
``surfaceChanged``.
.. code-block:: java .. code-block:: xml
:linenos: :linenos:
package com.hello.opencv.test; <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" >
import android.content.Context; <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" />
public class HelloOpenCVView extends SurfaceView implements Callback, Runnable { </LinearLayout>
public HelloOpenCVView(Context context) { #. Add the following permissions to the :file:`AndroidManifest.xml` file:
super(context);
getHolder().addCallback(this);
}
public void surfaceCreated(SurfaceHolder holder) { .. code-block:: xml
(new Thread(this)).start(); :linenos:
}
public void surfaceDestroyed(SurfaceHolder holder) { </application>
cameraRelease();
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { <uses-permission android:name="android.permission.CAMERA"/>
cameraSetup(width, height);
}
* Add ``cameraOpen``, ``cameraRelease`` and ``cameraSetup`` voids as shown below. <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"/>
* Also, don't forget to add the public void ``run()`` as follows: #. Set application theme in AndroidManifest.xml to hide title and system buttons.
.. code-block:: java .. code-block:: xml
:linenos: :linenos:
public void run() { <application
// TODO: loop { getFrame(), processFrame(), drawFrame() } android:icon="@drawable/icon"
} android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
public boolean cameraOpen() {
return false; //TODO: open camera
}
private void cameraRelease() { #. Add OpenCV library initialization to your activity. Fix errors by adding requited imports.
// 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.
.. code-block:: java .. code-block:: java
:linenos: :linenos:
public void onCreate (Bundle savedInstanceState) { private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
super.onCreate(savedInstanceState); @Override
mView = new HelloOpenCVView(this); public void onManagerConnected(int status) {
setContentView (mView); switch (status) {
case LoaderCallbackInterface.SUCCESS:
{
Log.i(TAG, "OpenCV loaded successfully");
mOpenCvCameraView.enableView();
} break;
default:
{
super.onManagerConnected(status);
} break;
} }
protected void onPause() {
super.onPause();
mView.cameraRelease();
} }
};
protected void onResume() { @Override
public void onResume()
{
super.onResume(); super.onResume();
if( !mView.cameraOpen() ) { OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
// 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:
.. code-block:: xml
:linenos:
</application>
<uses-permission android:name="android.permission.CAMERA" /> #. Defines that your activity implements CvViewFrameListener interface and fix activity related
<uses-feature android:name="android.hardware.camera" /> errors by defining missed methods. For this activity define ``onCreate``, ``onDestroy`` and
<uses-feature android:name="android.hardware.camera.autofocus" /> ``onPause`` and implement them according code snippet bellow. Fix errors by adding requited
imports.
#. Reference OpenCV library within your project properties.
.. image:: images/dev_OCV_reference.png
:alt: Reference OpenCV library.
:align: center
#. We now need some code to handle the camera. Update the ``HelloOpenCVView`` class as follows:
.. code-block:: java .. code-block:: java
:linenos: :linenos:
private VideoCapture mCamera; private CameraBridgeViewBase mOpenCvCameraView;
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;
}
public void cameraRelease() { @Override
synchronized(this) { public void onCreate(Bundle savedInstanceState) {
if (mCamera != null) { Log.i(TAG, "called onCreate");
mCamera.release(); super.onCreate(savedInstanceState);
mCamera = null; 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) { @Override
synchronized (this) { public void onPause()
if (mCamera != null && mCamera.isOpened()) { {
List<Size> sizes = mCamera.getSupportedPreviewSizes(); super.onPause();
int mFrameWidth = width; if (mOpenCvCameraView != null)
int mFrameHeight = height; mOpenCvCameraView.disableView();
{ // 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 onDestroy() {
super.onDestroy();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
} }
public void onCameraViewStarted(int width, int height) {
} }
#. The last step would be to update the ``run()`` void in ``HelloOpenCVView`` class as follows: public void onCameraViewStopped() {
}
.. code-block:: java public Mat onCameraFrame(Mat inputFrame) {
:linenos: return inputFrame;
}
public void run() { #. Run your application on device or emulator.
while (true) {
Bitmap bmp = null;
synchronized (this) {
if (mCamera == null)
break;
if (!mCamera.grab())
break;
bmp = processFrame(mCamera); 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
if (bmp != null) { OpenCV-centric application must implement OpenCV initialization, create its own view to show
Canvas canvas = getHolder().lockCanvas(); preview from camera and implements ``CvViewFrameListener`` interface to get frames from camera and
if (canvas != null) { process it.
canvas.drawBitmap(bmp, (canvas.getWidth() - bmp.getWidth()) / 2,
(canvas.getHeight() - bmp.getHeight()) / 2, null);
getHolder().unlockCanvasAndPost(canvas);
} First of all we create our application view using xml layout. Our layout consists of the only
bmp.recycle(); 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.
protected Bitmap processFrame(VideoCapture capture) { After creating layout we need to implement ``Activity`` class. OpenCV initialization process has
Mat mRgba = new Mat(); been already discussed above. In this sample we use asynchronous initialization. Implementation of
capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA); ``CvCameraViewListener`` interface allows you to add processing steps after frame grabbing from
//process mRgba camera and before its rendering on screen. The most important function is ``onCameraFrame``. It is
Bitmap bmp = Bitmap.createBitmap(mRgba.cols(), mRgba.rows(), Bitmap.Config.ARGB_8888); callback function and it is called on retrieving frame from camera. The callback input is frame
try { from camera. RGBA format is used by default. You can change this behavior by ``SetCaptureFormat``
Utils.matToBitmap(mRgba, bmp); method of ``View`` class. ``Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA`` and
} catch(Exception e) { ``Highgui.CV_CAP_ANDROID_GREY_FRAME`` are supported. It expects that function returns RGBA frame
Log.e("processFrame", "Utils.matToBitmap() throws an exception: " + e.getMessage()); that will be drawn on the screen.
bmp.recycle();
bmp = null;
}
return bmp;
}
...@@ -137,7 +137,7 @@ Here you can read tutorials about how to set up your computer to work with the O ...@@ -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` |AndroidLogo| **Title:** :ref:`dev_with_OCV_on_Android`
*Compatibility:* > OpenCV 2.4.2 *Compatibility:* > OpenCV 2.4.3
*Author:* |Author_VsevolodG| *Author:* |Author_VsevolodG|
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment