Tutorial for Android synchronized with actual application framework.
This commit is contained in:
parent
03f7d2f1ca
commit
c7e7b77093
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
public class Sample1Java extends Activity implements CvCameraViewListener {
|
||||
|
||||
/** Call on every application resume **/
|
||||
@Override
|
||||
protected void onResume()
|
||||
{
|
||||
Log.i(TAG, "called onResume");
|
||||
super.onResume();
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Log.i(TAG, "Trying to load OpenCV library");
|
||||
if (!OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_2, this, mOpenCVCallBack))
|
||||
@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,110 +291,43 @@ 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`.
|
||||
: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:
|
||||
*HelloOpenCVView*.
|
||||
#. Allow Eclipse to create default activity. Lets name the activity ``HelloOpenCvActivity``.
|
||||
|
||||
.. image:: images/dev_OCV_new_class.png
|
||||
:alt: Add a new class.
|
||||
#. Choose Blank Activity with full screen layout. Lets name the layout ``HelloOpenCvLayout``.
|
||||
|
||||
#. Import OpenCV library project to your workspace.
|
||||
|
||||
#. Reference OpenCV library within your project properties.
|
||||
|
||||
.. image:: images/dev_OCV_reference.png
|
||||
:alt: Reference OpenCV library.
|
||||
:align: center
|
||||
|
||||
* It should extend ``SurfaceView`` class.
|
||||
* It also should implement ``SurfaceHolder.Callback``, ``Runnable``.
|
||||
#. Edit your layout file as xml file and pass the following layout there:
|
||||
|
||||
#. Edit ``HelloOpenCVView`` class.
|
||||
|
||||
* Add an ``import`` line for ``android.content.context``.
|
||||
|
||||
* Modify autogenerated stubs: ``HelloOpenCVView``, ``surfaceCreated``, ``surfaceDestroyed`` and
|
||||
``surfaceChanged``.
|
||||
|
||||
.. code-block:: java
|
||||
.. code-block:: xml
|
||||
: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 {
|
||||
|
||||
public HelloOpenCVView(Context context) {
|
||||
super(context);
|
||||
getHolder().addCallback(this);
|
||||
}
|
||||
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
(new Thread(this)).start();
|
||||
}
|
||||
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
cameraRelease();
|
||||
}
|
||||
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
cameraSetup(width, height);
|
||||
}
|
||||
|
||||
* Add ``cameraOpen``, ``cameraRelease`` and ``cameraSetup`` voids as shown below.
|
||||
|
||||
* Also, don't forget to add the public void ``run()`` as follows:
|
||||
|
||||
.. code-block:: java
|
||||
: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.
|
||||
|
||||
.. code-block:: java
|
||||
: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();
|
||||
}
|
||||
}
|
||||
</LinearLayout>
|
||||
|
||||
#. Add the following permissions to the :file:`AndroidManifest.xml` file:
|
||||
|
||||
@ -409,108 +336,120 @@ result.
|
||||
|
||||
</application>
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
|
||||
#. Reference OpenCV library within your project properties.
|
||||
<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"/>
|
||||
|
||||
.. image:: images/dev_OCV_reference.png
|
||||
:alt: Reference OpenCV library.
|
||||
:align: center
|
||||
#. Set application theme in AndroidManifest.xml to hide title and system buttons.
|
||||
|
||||
#. We now need some code to handle the camera. Update the ``HelloOpenCVView`` class as follows:
|
||||
.. code-block:: xml
|
||||
:linenos:
|
||||
|
||||
<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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onResume()
|
||||
{
|
||||
super.onResume();
|
||||
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
|
||||
}
|
||||
|
||||
#. 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;
|
||||
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;
|
||||
}
|
||||
@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);
|
||||
}
|
||||
|
||||
public void cameraRelease() {
|
||||
synchronized(this) {
|
||||
if (mCamera != null) {
|
||||
mCamera.release();
|
||||
mCamera = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onPause()
|
||||
{
|
||||
super.onPause();
|
||||
if (mOpenCvCameraView != null)
|
||||
mOpenCvCameraView.disableView();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mOpenCvCameraView != null)
|
||||
mOpenCvCameraView.disableView();
|
||||
}
|
||||
|
||||
#. The last step would be to update the ``run()`` void in ``HelloOpenCVView`` class as follows:
|
||||
public void onCameraViewStarted(int width, int height) {
|
||||
}
|
||||
|
||||
.. code-block:: java
|
||||
:linenos:
|
||||
public void onCameraViewStopped() {
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (true) {
|
||||
Bitmap bmp = null;
|
||||
synchronized (this) {
|
||||
if (mCamera == null)
|
||||
break;
|
||||
if (!mCamera.grab())
|
||||
break;
|
||||
public Mat onCameraFrame(Mat inputFrame) {
|
||||
return inputFrame;
|
||||
}
|
||||
|
||||
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);
|
||||
#. Run your application on device or emulator.
|
||||
|
||||
}
|
||||
bmp.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
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.
|
||||
|
||||
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;
|
||||
}
|
||||
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.
|
||||
|
@ -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|
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user