|
|
|
@ -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. |
|
|
|
|