1/25/13

Android Native Camera With OpenCV and OpenGLES

Today I will show a how to use OpenCV and OpenGL to make your own camera preview.it can be used later for image processing or like in my case in a Augmented Reality app. Some questions,assumptions and info need to be considered.

Why accessing the camera natively?
because Image processing is done with OpenCV which is written in C and C++.so you don't want to get your frames in Java and send them to using JNI which is slow get the result back from it then render the resulting frame on the screen.so what  we gonna do is to use the camera,openCV and OpenGL all written in C++.

I heard that's it is not supported for all phones,is that right?
So here is the problem android developers changes their native camera module very frequently.OpenCV people tries to cover all the implemented modules till now and they come up with 7 native libraries to cover the majority of the phones till now.So I guess the answer is yes and no.

My code was tested on Samsung Galaxy Note & I got >30 FPS(Frame Per Second) but due to the camera hardware device you can't get more than 30 FPS.

I am using OpenGLES 1.1 because it's easier maybe not efficient because there is no FBOs (Frame Buffer Object) but actually I din't care so much because I got my target FPS and even more.

No camera controles is made here like taking pictures and adjusting focus,just taking frames and render them with OpenGL.

This topic does't have lots of resources and that made me create a blog and write this post.

So lets begin :D



I am assuming that you have installed OpenCV & NDK and know how to use them.

First of all,You need to have a JNI folder so create one if not there.Then you need to add inside it another folder called build.inside build you need to put openCV include folder and libs folder.Both folders comes with the openCV sdk.

OpenCV_Directory->sdk->native->jni->include
OpenCV_Directory->sdk->native->libs

Now you are ready to go.

I will explain the structure of my code in term of threads.
So we have the main thread(1) that controls that whole app.Then after creating a GLSurface and setting its renderer we get another thread called GLThread(2) that is responsible for drawing the the frames that we grab from the camera and render it on the screen.And the last thread is the Frame-Grabber thread(3) (really slow thread),all what it's doing is just taking frames and store them into the a buffer so lately they can be drawn to the screen.

Here we have the our main Activity class called CameraPreviewer.java


package com.mesai.nativecamera;

import java.util.List;

import org.opencv.android.CameraBridgeViewBase.ListItemAccessor;
import org.opencv.android.NativeCameraView.OpenCvSizeAccessor;
import org.opencv.core.Size;
import org.opencv.highgui.Highgui;
import org.opencv.highgui.VideoCapture;

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.Display;

public class CameraPreviewer extends Activity {

    GLSurfaceView mView;
    
    @Override protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        Native.loadlibs();
        VideoCapture mCamera = new VideoCapture(Highgui.CV_CAP_ANDROID);
        java.util.List<Size> sizes = mCamera.getSupportedPreviewSizes();
        mCamera.release();
 
        mView = new GLSurfaceView(getApplication()){
         @Override
         public void onPause() {
          super.onPause();
          Native.releaseCamera();
         }
        };
        Size size = calculateCameraFrameSize(sizes,new OpenCvSizeAccessor());
        mView.setRenderer(new CameraRenderer(this,size));
        setContentView(mView);
    }
    
 protected Size calculateCameraFrameSize(List supportedSizes,
   ListItemAccessor accessor) {
  int calcWidth = Integer.MAX_VALUE;
  int calcHeight = Integer.MAX_VALUE;

  Display display = getWindowManager().getDefaultDisplay();

  int maxAllowedWidth = 1024;
  int maxAllowedHeight = 1024;

  for (Object size : supportedSizes) {
   int width = accessor.getWidth(size);
   int height = accessor.getHeight(size);

   if (width <= maxAllowedWidth && height <= maxAllowedHeight) {
    if ( width <= calcWidth 
      && width>=(maxAllowedWidth/2)
      &&(display.getWidth()%width==0||display.getHeight()%height==0)) {
     calcWidth = (int) width;
     calcHeight = (int) height;
    }
   }
  }

  return new Size(calcWidth, calcHeight);
 }
    @Override protected void onPause() {
        super.onPause();
        mView.onPause();
       
    }

    @Override protected void onResume() {
        super.onResume();
        mView.onResume();
        
    }
}

Here we can see that there is some weird lines @23-25.this is because the method getSupportedPreviewSizes() is not supported in the C++ version.And I needed the supported resolutions of the camera so that I can pick one that suits me.After that I create the GLSurface that can be used for video rendering.

Now our Custom Renderer CameraRenderer.java
package com.mesai.nativecamera;


import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import org.opencv.core.Size;

import android.content.Context;
import android.opengl.GLSurfaceView.Renderer;


public class CameraRenderer implements Renderer {

 private Size size;
 private Context context;
 public CameraRenderer(Context c,Size size) {
  super();
  context = c;
  this.size = size;
 }
 
 public void onSurfaceCreated(GL10 gl, EGLConfig config) {
  Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
  Native.initCamera((int)size.width,(int)size.height);
 }

 public void onDrawFrame(GL10 gl) {
//  long startTime   = System.currentTimeMillis();
  Native.renderBackground();
//  long endTime   = System.currentTimeMillis();
//  if(30-(endTime-startTime)>0){
//   try {
//    Thread.sleep(30-(endTime-startTime));
//   } catch (InterruptedException e) {}
//  }
//  endTime   = System.currentTimeMillis();
  //System.out.println(endTime-startTime+" ms");
 }
 
 public void onSurfaceChanged(GL10 gl, int width, int height) {
  Native.surfaceChanged(width,height,context.getResources().getConfiguration().orientation);
 }
} 
uncommenting the part @lines 29-38 will make your camera have a steady 30 fps and will print the time needed to draw each frame.(just for debugging)
The renderer is referring to a class called Native in all it's Method
so here it's
package com.mesai.nativecamera;

public class Native {
 public static void loadlibs(){
  System.loadLibrary("opencv_java");
  System.loadLibrary("NativeCamera");
 }
 public static native void initCamera(int width,int height);
 public static native void releaseCamera();
 public static native void renderBackground();
 public static native void surfaceChanged(int width,int height,int orientation);
}
Ok now for the native part here is the code for CameraRenderer.cpp
#include <jni.h>
#include <GLES/gl.h>
#include <GLES/glext.h>
#include <android/log.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv/cv.h>
#include <pthread.h>
#include <time.h>
#include <Math.h>

// Utility for logging:
#define LOG_TAG    "CAMERA_RENDERER"
#define LOG(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

GLuint texture;
cv::VideoCapture capture;
cv::Mat buffer[30];
cv::Mat rgbFrame;
cv::Mat inframe;
cv::Mat outframe;
int bufferIndex;
int rgbIndex;
int frameWidth;
int frameHeight;
int screenWidth;
int screenHeight;
int orientation;
pthread_mutex_t FGmutex;
pthread_t frameGrabber;
pthread_attr_t attr;
struct sched_param param;

GLfloat vertices[] = {
      -1.0f, -1.0f, 0.0f, // V1 - bottom left
      -1.0f,  1.0f, 0.0f, // V2 - top left
       1.0f, -1.0f, 0.0f, // V3 - bottom right
       1.0f,  1.0f, 0.0f // V4 - top right
       };

GLfloat textures[8];

extern "C" {

void drawBackground();
void createTexture();
void destroyTexture();
void *frameRetriever(void*);

JNIEXPORT void JNICALL Java_com_mesai_nativecamera_Native_initCamera(JNIEnv*, jobject,jint width,jint height)
{
 LOG("Camera Created");
 capture.open(CV_CAP_ANDROID + 0);
 capture.set(CV_CAP_PROP_FRAME_WIDTH, width);
 capture.set(CV_CAP_PROP_FRAME_HEIGHT, height);
 frameWidth =width;
 frameHeight = height;
 LOG("frameWidth = %d",frameWidth);
 LOG("frameHeight = %d",frameHeight);
 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
 glShadeModel(GL_SMOOTH);
 glClearDepthf(1.0f);
 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
 pthread_attr_t attr;
 pthread_attr_init(&attr);
 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
 memset(&param, 0, sizeof(param));
 param.sched_priority = 100;
 pthread_attr_setschedparam(&attr, &param);
 pthread_create(&frameGrabber, &attr, frameRetriever, NULL);
 pthread_attr_destroy(&attr);

}

JNIEXPORT void JNICALL Java_com_mesai_nativecamera_Native_surfaceChanged(JNIEnv*, jobject,jint width,jint height,jint orien)
{
 LOG("Surface Changed");
 glViewport(0, 0, width,height);
 if(orien==1) {
   screenWidth = width;
   screenHeight = height;
   orientation = 1;
  } else {
   screenWidth = height;
   screenHeight = width;
   orientation = 2;
  }


 LOG("screenWidth = %d",screenWidth);
 LOG("screenHeight = %d",screenHeight);
 glMatrixMode(GL_PROJECTION);
 glLoadIdentity();
 float aspect = screenWidth / screenHeight;
 float bt = (float) tan(45 / 2);
 float lr = bt * aspect;
 glFrustumf(-lr * 0.1f, lr * 0.1f, -bt * 0.1f, bt * 0.1f, 0.1f,
   100.0f);
 glMatrixMode(GL_MODELVIEW);
 glLoadIdentity();
 glEnable(GL_TEXTURE_2D);
 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
 glClearDepthf(1.0f);
 glEnable(GL_DEPTH_TEST);
 glDepthFunc(GL_LEQUAL);
 createTexture();
}

JNIEXPORT void JNICALL Java_com_mesai_nativecamera_Native_releaseCamera(JNIEnv*, jobject)
{
 LOG("Camera Released");
 capture.release();
 destroyTexture();

}

void createTexture() {
  textures[0] = ((1024.0f-frameWidth*1.0f)/2.0f)/1024.0f;
  textures[1] = ((1024.0f-frameHeight*1.0f)/2.0f)/1024.0f + (frameHeight*1.0f/1024.0f);
  textures[2] = ((1024.0f-frameWidth*1.0f)/2.0f)/1024.0f + (frameWidth*1.0f/1024.0f);
  textures[3] = ((1024.0f-frameHeight*1.0f)/2.0f)/1024.0f + (frameHeight*1.0f/1024.0f);
  textures[4] = ((1024.0f-frameWidth*1.0f)/2.0f)/1024.0f;
  textures[5] = ((1024.0f-frameHeight*1.0f)/2.0f)/1024.0f;
  textures[6] = ((1024.0f-frameWidth*1.0f)/2.0f)/1024.0f + (frameWidth*1.0f/1024.0f);
  textures[7] = ((1024.0f-frameHeight*1.0f)/2.0f)/1024.0f;
 LOG("Texture Created");
 glGenTextures(1, &texture);
 glBindTexture(GL_TEXTURE_2D, texture);
 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1024,1024, 0, GL_RGB,
   GL_UNSIGNED_SHORT_5_6_5, NULL);
 glBindTexture(GL_TEXTURE_2D, 0);
}

void destroyTexture() {
 LOG("Texture destroyed");
 glDeleteTextures(1, &texture);
}

JNIEXPORT void JNICALL Java_com_mesai_nativecamera_Native_renderBackground(
  JNIEnv*, jobject) {
 drawBackground();
}

void drawBackground() {
 glClear (GL_COLOR_BUFFER_BIT);
 glBindTexture(GL_TEXTURE_2D, texture);
 if(bufferIndex>0){
 pthread_mutex_lock(&FGmutex);
 cvtColor(buffer[(bufferIndex - 1) % 30], outframe, CV_BGR2BGR565);
 pthread_mutex_unlock(&FGmutex);
 cv::flip(outframe, rgbFrame, 1);
 if (texture != 0)
   glTexSubImage2D(GL_TEXTURE_2D, 0, (1024-frameWidth)/2, (1024-frameHeight)/2, frameWidth, frameHeight,
   GL_RGB, GL_UNSIGNED_SHORT_5_6_5, rgbFrame.ptr());
 }
 glEnableClientState (GL_VERTEX_ARRAY);
 glEnableClientState (GL_TEXTURE_COORD_ARRAY);
 glLoadIdentity();
 if(orientation!=1){
  glRotatef( 90,0,0,1);
 }
 // Set the face rotation
 glFrontFace (GL_CW);
 // Point to our vertex buffer
 glVertexPointer(3, GL_FLOAT, 0, vertices);
 glTexCoordPointer(2, GL_FLOAT, 0, textures);
 // Draw the vertices as triangle strip
 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 //Disable the client state before leaving
 glDisableClientState(GL_VERTEX_ARRAY);
 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

void *frameRetriever(void*) {
 while (capture.isOpened()) {
  capture.read(inframe);
  if (!inframe.empty()) {
   pthread_mutex_lock(&FGmutex);
   inframe.copyTo(buffer[(bufferIndex++) % 30]);
   pthread_mutex_unlock(&FGmutex);
  }
 }
 LOG("Camera Closed");
 pthread_exit (NULL);
}


}
Here we have a lot of things that need to be explained :
  1. In initCamera :We initialize our camera here by saying which camera we want to access then we set the resolution we want to our frame to be.
  2. initCamera (cont.):We initialize a new thread(Frame-Grabber) for more information on native threads you can search posix thread or pthreads.
  3. surfaceChanged :it 's all Opengl initialization stuff.
  4. destroyTexture and releaseCamera is for exiting the app.
  5. drawBackground: is for rendering the frames.
  6. frameRetriever is the method called by the Frame Grabber thread.
The now you need android.mk file to compile the .cpp

LOCAL_PATH := $(call my-dir)




include $(CLEAR_VARS)
LOCAL_MODULE := opencv-prebuilt
LOCAL_SRC_FILES = build/libs/$(TARGET_ARCH_ABI)/libopencv_java.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/build/include
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := camera1-prebuilt
LOCAL_SRC_FILES = build/libs/$(TARGET_ARCH_ABI)/libnative_camera_r4.2.0.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/build/include
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := camera2-prebuilt
LOCAL_SRC_FILES = build/libs/$(TARGET_ARCH_ABI)/libnative_camera_r4.1.1.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/build/include
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := camera3-prebuilt
LOCAL_SRC_FILES = build/libs/$(TARGET_ARCH_ABI)/libnative_camera_r4.0.3.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/build/include
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := camera4-prebuilt
LOCAL_SRC_FILES = build/libs/$(TARGET_ARCH_ABI)/libnative_camera_r4.0.0.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/build/include
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := camera5-prebuilt
LOCAL_SRC_FILES = build/libs/$(TARGET_ARCH_ABI)/libnative_camera_r3.0.1.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/build/include
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := camera6-prebuilt
LOCAL_SRC_FILES = build/libs/$(TARGET_ARCH_ABI)/libnative_camera_r2.3.3.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/build/include
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := camera7-prebuilt
LOCAL_SRC_FILES = build/libs/$(TARGET_ARCH_ABI)/libnative_camera_r2.2.0.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/build/include
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
OPENGLES_LIB  := -lGLESv1_CM
OPENGLES_DEF  := -DUSE_OPENGL_ES_1_1
LOCAL_MODULE    := NativeCamera
LOCAL_SHARED_LIBRARIES := opencv-prebuilt 
LOCAL_SRC_FILES := CameraRenderer.cpp
LOCAL_LDLIBS +=  $(OPENGLES_LIB) -llog -ldl -lEGL

include $(BUILD_SHARED_LIBRARY)

I hope that you have enjoyed my first tutorial post and if you have any questions just leave a comment and feel free to ask.

UPDATE 1:

Here you can find the source code:
https://github.com/MESAI/NativeCamera
Add the openCV java shared library.
Add the build folder and its content in the JNI folder.
Then ndk-build.

88 comments:

  1. Hi! I've tried use this tutorial since the one that comes with OpenCV for Android ( version 2.4.2 ) on my Samsung Galaxy Nexus runs at about 4-5 FPS. When I copied the files into my project, I get the errors:

    "The import org.opencv.android.NativeCameraView cannot be resolved"
    and
    "The import org.opencv.android.CameraBridgeViewBase cannot be resolved"

    in the imports.
    Do you happen to know why Eclipse doesn't recognise those imports?

    thanks

    ReplyDelete
    Replies
    1. Did you add the openCV for android as a shared library project ?
      Right click on the project proprieties->Android->I guess you will see a red X in the bottom area.
      You should include the openCV project here.

      Delete
    2. Tahnks for your reply!
      Actually, I did, it was among the first things I checked.
      I've copied the face detection tutorial, added the OpenCV library as you suggested, erased every class and the .cpp and .h files and copied your files inside the project.
      It really bothers me a lot because going from 4-5 FPS to 30 FPS sounds really great. Again, I'm still unexperienced with OpenCV and I might have forgotten someting important. I know that the samples work because I've tested them on my device...

      Delete
    3. can you create a brand new project?
      because I can't really trace where the error is coming from.
      it 's not that hard I can do the steps with you if you like.
      and what is you openCV version ?

      Delete
    4. Thanks for your fast reply!
      I'm using OpenCV-2.4.2-android-sdk.
      I never tried to create a JNI based project from scratch, so I'm following your instructions ;)

      I've added both the folders and copied inside "build" the folders "include" and "libs" from the original OpenCV folder. I've repeated all the other steps (and added the files Android.mk and Application.mk). Still get the same error...

      Delete
    5. can you send me a screenshot of your workspace and the error?

      Delete
  2. Done, I've uploaded the screenshot on: http://postimage.org/image/61as98jj1/full/
    Hope it makes some sense for you (because it doesn't for me XD )

    ReplyDelete
    Replies
    1. Ok,if our are importing(added as shared library) the above project called "OpenCV Library - 2.4.2" while it have errors it wont be seen by the Ztry so you need to fix it first.
      so why "OpenCV Library - 2.4.2" have errors ?

      Delete
    2. I don't know why, it always had. I thought it wasn't important since the samples worked fine... There is no error inside the subfolders...

      Delete
    3. but you are using it in your project?
      if yes can you check the android target of the "OpenCV Library - 2.4.2"
      it must be like the target you have used when you have imported the project.
      and always try "clean" and "refresh" when doing any changes with any project.

      Delete
    4. Thanks for helping me so much! ;)
      Yeah, the face detection sample doesn't work without the OpenCV Library - 2.4.2 library. I've deleted and added all the files. Now the library does not have any error now but samples "15-puzzle", "Tutorial1" and "Tutorial4" do (without any real error inside the subfolders, just like the library did).
      Of course my project has still the same error XD

      Delete
    5. I found your problem :D
      I downloaded your version 4.2.2
      and I didn't find the two files causing the errors
      so you need 4.2.3 or higher to solve your issue.

      Delete
    6. sorry I mean :2.4.2 and 2.4.3 or higher :D

      Delete
    7. Wow! thanks a lot! I'm going to try it tomorrow as soon as possible (I live in Italy and it is a qurter to eleven PM )

      Delete
    8. urw,so tomorrow.
      btw I am from Egypt and it's 11:45 pm.

      Delete
    9. Hi! I had a busy morning and could try only now. Your suggestion worked and solved THAT problme. I say THAT because now my application crashes as soon as it starts...

      By reading the log I think the error comes from these instructions:

      System.loadLibrary("opencv_java");
      System.loadLibrary("NativeCamera");

      in the Native class.

      Thanks again!

      In case you want to take a look, is the full log:

      02-27 14:42:35.978: E/AndroidRuntime(20801): FATAL EXCEPTION: main
      02-27 14:42:35.978: E/AndroidRuntime(20801): java.lang.UnsatisfiedLinkError: Couldn't load opencv_java from loader dalvik.system.PathClassLoader[dexPath=/data/app/com.example.ztry-1.apk,libraryPath=/data/app-lib/com.example.ztry-1]: findLibrary returned null
      02-27 14:42:35.978: E/AndroidRuntime(20801): at java.lang.Runtime.loadLibrary(Runtime.java:365)
      02-27 14:42:35.978: E/AndroidRuntime(20801): at java.lang.System.loadLibrary(System.java:535)
      02-27 14:42:35.978: E/AndroidRuntime(20801): at com.example.ztry.Native.loadlibs(Native.java:6)
      02-27 14:42:35.978: E/AndroidRuntime(20801): at com.example.ztry.CameraPreviewer.onCreate(CameraPreviewer.java:22)
      02-27 14:42:35.978: E/AndroidRuntime(20801): at android.app.Activity.performCreate(Activity.java:5104)
      02-27 14:42:35.978: E/AndroidRuntime(20801): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
      02-27 14:42:35.978: E/AndroidRuntime(20801): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144)
      02-27 14:42:35.978: E/AndroidRuntime(20801): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
      02-27 14:42:35.978: E/AndroidRuntime(20801): at android.app.ActivityThread.access$600(ActivityThread.java:141)
      02-27 14:42:35.978: E/AndroidRuntime(20801): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
      02-27 14:42:35.978: E/AndroidRuntime(20801): at android.os.Handler.dispatchMessage(Handler.java:99)
      02-27 14:42:35.978: E/AndroidRuntime(20801): at android.os.Looper.loop(Looper.java:137)
      02-27 14:42:35.978: E/AndroidRuntime(20801): at android.app.ActivityThread.main(ActivityThread.java:5041)
      02-27 14:42:35.978: E/AndroidRuntime(20801): at java.lang.reflect.Method.invokeNative(Native Method)
      02-27 14:42:35.978: E/AndroidRuntime(20801): at java.lang.reflect.Method.invoke(Method.java:511)
      02-27 14:42:35.978: E/AndroidRuntime(20801): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
      02-27 14:42:35.978: E/AndroidRuntime(20801): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
      02-27 14:42:35.978: E/AndroidRuntime(20801): at dalvik.system.NativeStart.main(Native Method)

      Delete
    10. Did you build the native code?
      "ndk-build"

      Delete
    11. yep, always used it. these two "opencv_java" and "NativeCamera" where do you get them from?

      Delete
    12. opencv_java is found in build/libs/libopencv_java.so but you don't need to modify anything with it.
      the other you can't find it you must build it.

      After you build the project you should have in the root of project a folder called libs,there you can find
      armeabi-v7a directory which have all the needed libraries.(you must find the 2 libs in it)
      if it's empty or not found that mean that the build is not successful.

      Delete
    13. I'm starting to think that i've messed up somewhere with cygwin configuration... You're working on windows too or linux? Btw, the folder is empty...

      Delete
    14. Or maybe I didn't. This is the error I get from cigwin:


      $ ndk-build
      Android NDK: ERROR:jni/Android.mk:camera1-prebuilt: LOCAL_SRC_FILES points to a missing file
      Android NDK: Check that jni/build/libs/armeabi-v7a/libnative_camera_r4.2.0.so exists or that its path is correct
      /cygdrive/c/android/NDK/android-ndk-r8c/build/core/prebuilt-library.mk:43: *** Android NDK: Aborting.

      I'm still working with the edited copy of the face detection project.
      In the folder armeabi-v7a from the root is infact empty but the one inside JNI->build->libs->armeabi-v7a has many libraries among which libnative_camera_r4.2.0.so is missing...

      Do you happen to have any guess? (I'm completely lost...)

      Delete
    15. It 's the same problem : You didn't add the libs of the new version 2.4.3 or higher because 2.4.2 didn't have the libnative_camera_r4.2.0.so.

      Delete
    16. Ok, I've downloaded the 2.4.4 beta libraries and I think I really have an issue with cigwin, when I run ndk-build I get:

      $ ndk-build
      Gdbserver : [arm-linux-androideabi-4.6] libs/armeabi/gdbserver
      Gdbsetup : libs/armeabi/gdb.setup
      Install : libnative_camera_r4.2.0.so => libs/armeabi/libnative_camera_r4.2.0.so
      install: impossibile aprire "./obj/local/armeabi/libnative_camera_r4.2.0.so" per la lettura: Permission denied
      /cygdrive/c/android/NDK/android-ndk-r8c/build/core/build-binary.mk:428: recipe for target `libs/armeabi/libnative_camera_r4.2.0.so' failed
      make: *** [libs/armeabi/libnative_camera_r4.2.0.so] Error 1

      which is strange 'cause I'm using the computer as admin...

      Delete
    17. just in case can make your application.mk be
      APP_STL := gnustl_static
      APP_CPPFLAGS := -frtti -fexceptions
      APP_ABI := armeabi-v7a
      APP_PLATFORM := android-9

      Delete
    18. thanks for your help, this is driving me crazy... I tried your suggestion and got still the same error:

      impossible open "./obj/local/armeabi-v7a/libnative_camera_r4.2.0.so" for reading: Permission denied

      Delete
    19. if you have an obj file in your project,can you delete it and try again?

      Delete
    20. Cygwin creates the obj folder every time I use the command ndk-build and puts inside it the folder "local" that contains the folder "armabi-v7a" that contains the file "libnative_camera_r4.2.0.so"... I've deleted it but still it creates again and still doesn't work

      Delete
    21. Really, really thanks for your time and your patience!!

      Builded! It was a cygwin permission issue! Following the suggestion from here:
      http://forum.thegamecreators.com/?m=forum_view&t=194719&b=46

      to build it all I had to do was
      chmod -R 777 /cygdrive/c/android/workspace

      and worked!

      I compared the current log with the previous one and noticed that still the main error is in load library...

      Now, back to the errors, the application crashes when I test it XD

      The only difference now in the log is that it says

      java.lang.UnsatisfiedLinkError: Couldn't load NativeCamera from loader dalvik.system.PathClassLoader[dexPath=/data/app/com.example.ztry-1.apk,libraryPath=/data/app-lib/com.example.ztry-1]: findLibrary returned null

      instead of

      java.lang.UnsatisfiedLinkError: Couldn't load opencv_java from loader dalvik.system.PathClassLoader[dexPath=/data/app/com.example.ztry-1.apk,libraryPath=/data/app-lib/com.example.ztry-1]: findLibrary returned null

      Delete
    22. And now can you find the libnativecamera.so in the libs/amreabi-v7a ?
      if yes then you need just to refresh the whole project and run this is a known issue when your using the ndk after each build you need to refresh the whole project.

      Delete
    23. I can see these libraries:

      libnative_camera_r2.2.0.so
      libnative_camera_r2.3.3.so
      libnative_camera_r3.0.1.so
      libnative_camera_r4.0.0.so
      libnative_camera_r4.0.3.so
      libnative_camera_r4.1.1.so
      libnative_camera_r4.2.0.so

      but not libnativecamera.so in the libs folde. I've tried to refresh it but it didn't work...

      Delete
  3. Hi, thanks for this post !
    This is exactly what I was looking for :-)

    But I have some problems implementing it.

    First, I get warning for deprecated methods display.getWidth() and display.getHeight(). But it compiles.
    If I try to replace it with suggested methods like getSize() or getRectSize(), the app crashes because of these methods.

    Second, when I run my app, the camera preview is mirrored and upside down. When I try on a HTC Desire HD, we can see the image but with black and white noise. No noise on a Galaxy S3...

    Do you have any idea of how to fix that ?

    Thanks !

    ReplyDelete
  4. Hi !

    I managed to get an image right side up.

    In drawBackground() you use method flip() :
    cv::flip(outframe, rgbFrame, 1);

    But you use outframe instead of rgbFrame here :
    if (texture != 0)
    glTexSubImage2D(GL_TEXTURE_2D, 0, (1024-frameWidth)/2, (1024-frameHeight)/2, frameWidth, frameHeight,
    GL_RGB, GL_UNSIGNED_SHORT_5_6_5, outframe.ptr());
    }

    I replaced outframe with rgbFrame and it works well.

    But I still have a noisy preview on the HTC Desire HD.

    Any idea to fix it ?

    Thanks.

    ReplyDelete
    Replies
    1. I saw your message yesterday,I was too busy sorry.I I will take a look to your problem and I will contact you if if I have a solution for it.btw flip is one solution to the problem the other one is to rotate the texture coordinates.but if flip work with you that's ok.also the code was made for landscape mode that's is the main reason that made your screen flip I guess.so I will see the noise issue and I will contact you.

      Delete
    2. Thanks for your reply.

      Here is a screenshot of my camera preview on HTC Desire HD :
      https://dl.dropboxusercontent.com/u/4662606/noisycampreview.png

      Moreover, the image is distorted.

      No problem with SGS3.

      Delete
    3. I replaced this line :
      if ( width <= calcWidth && width>=(maxAllowedWidth/2) && (display.getWidth()%width==0||display.getHeight()%height==0)) {

      With this one :
      if (width >= calcWidth && height >= calcHeight) {

      and initialize calcWidth and calcHeight with 0 to get the biggest display size available. And this fixes the distortion of the preview.

      But I still get noise...

      Delete
    4. ok the problem with using a high resolution is that it will slow down your performance that's why I am using an upper bound.Regarding the CV_BGR2RGB565 this mean convert the frame from Blue-Green-Red to Red-Green-Blue and reduce the color space from 32->16 bit.so this maybe cause the noise you'r talking about try CV_BGR2RGB and do other changes in the texture proprieties.But again this will make your performance to be bad twice because instead of drawing 16 for each pixel you will draw 32 bit for each pixel

      Delete
    5. Ok thanks for your help.

      I can't find how to avoid the noise on HTC Desire. I'm trying to process the frame anyway.

      I have some questions :
      - Where can I do my processing ? I try to call my C++ functions with rgbFrame (just after flip()) but I'm not sure it's the best way.

      - In my C++ classes, i have a method to draw rect. But the origin seems to be in the upper right corner of the image... Because of the flip() method I think. Why ?

      - Another method binarise the frame and returns a Rect. But my frame seems to have the wrong color space and/or depth... When I call cvtColor() to convert from BGR to RGB, the preview displays noisy image. You said I have to do changes in the texture proprieties, but I'm not very familiar with OpenGL. If I convert from BGR to RGB, which texture properties have I to change ?

      Thanks for spending time on this.

      Delete
    6. You can process your frame using two ways (1) in sync, just after flipping you can do your processing,this will slow down your performance little bit depending on your processing.(2) out of sync,you make another thread for processing that pull a frame from a buffer and then do your processing.this mean that you will skip some frames while processing.so it depends on the application (for an Augmented Reality app I recommend (1) ).

      how are you drawing your rect ? if using openCV maybe flip cause your origin issue.if using openGL it maybe because of the viewport settings.in all cases you can create a method to convert the coordinate to the new coordinate system.

      you can change
      in line 131
      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1024,1024, 0, GL_RGB,
      GL_UNSIGNED_SHORT_5_6_5, NULL); the GL_UNSIGNED_SHORT_5_6_5
      i think it will be GL_UNSIGNED_BYTE and CV_BGR2BGR565 will be CV_BGR2RGB

      You are Welcome anytime.

      Delete
  5. This comment has been removed by the author.

    ReplyDelete
  6. This comment has been removed by the author.

    ReplyDelete
  7. Hello, I'm needing help!! When I run my project, appears this error:

    Caused by: java.lang.UnsatisfiedLinkError: Couldn't load opencv_java from loader dalvik.system.PathClassLoader[dexPath=/data/app/com.ndu.mobile.darwinwallet-1.apk,libraryPath=/data/app-lib/com.ndu.mobile.darwinwallet-1]: findLibrary returned null

    What do I have to do?

    Thanks

    ReplyDelete
    Replies
    1. this mean that you don't have the openCV native library or you didn't build the project using ndk-build

      Delete
    2. check the path of OpenCV.mk in your Android.mk :

      include /sdk/native/jni/OpenCV.mk

      Delete
    3. I don't can understand. I downloaded OPENCV 2.4.5 and I'm trying to execute the Darwin Wallet project in Android (Eclipse). The link to downloaded Darwin Wallet project is https://github.com/matthill/darwinwallet
      I need get run this application, thanks for help!!!!!

      Delete
  8. I'm trying to display a smaller preview. I initialize CameraRender with a smaller size but I get an error :

    OpenCV::camera(13475): ERROR reading YUV buffer: width=400, height=240, size=144000, receivedSize=460800
    OpenCV::camera(13475): CvCapture_Android::grabFrame: NO new frame

    My second problem is that the camera displays in landscape.

    On iOS I have created a library using camera preview. And now I just have to instantiate it with a CGRect in portrait mode and add it to my view.
    I would like to do the same on Android but it seems to be more complex...

    ReplyDelete
    Replies
    1. you can't just set the width and the height using any values it must be compatible with your camera sensor and that's why I get all the supported sizes then I choose one of them

      if I understand your problem right,I guess you want to have the previewer as a view inside a layout
      you can use any layout you want as your contentView then add mView to it.

      Delete
  9. I try to use the ratio of a supported size and devide by two, but I get these errors.

    I have created a LinearLayout with a size, and I want to display the camera in his bounds.
    But I get a black screen... If i set the width and height to fill_parent, it works but the camera is full screen.
    Have I to crop cv::Mat before display it ?

    ReplyDelete
  10. Do you have full sample code somewhere or do I need to create a template project myself (no worries if I do, but no point if there is a download link I am just too stupid to find :)

    ReplyDelete
    Replies
    1. No there is no full sample code ,sorry :).

      Delete
    2. I got this code working, works fine on samsung note 2 (Mali GPU), and a samsung s4 (PowerVR GPU), I get corruption on a nexus 7 (running 4.2).

      Is the corruption on the nexus 7 (tegra 3 GPU) a known issue ?

      Delete
    3. I am not sure if there is a known issue or not.
      what do you mean by corruption ? A deformed image or just a black image with error ?

      I know that nexus 7 doesn't have a back camera maybe this cause the issue try to change the line 52 in the cpp file
      capture.open(CV_CAP_ANDROID + 1); instead of capture.open(CV_CAP_ANDROID + 0);
      maybe this solve the issue.

      Delete
    4. Image corruption.

      Its like random memory has been drawn over the texture, the pattern is fixed for each time the camera start, but different between different runs of the application. Behind the corruption you can see the expected camera feed.

      I have screenshots I could send you but you appear to have hidden your email address :)

      Delete
    5. Interesting, if I glTexSubImage2D a tmp buffer instead of the camera frame, where every pixel is just 0xFFFF, I get the same patterns of corruption. Corruption is apparently always black (so if I set pixels to 0x0000 its invisible).

      Delete
    6. Yes, I'm getting the corruption on an HTC One X too which also has Tegra 3 GPU. Were you able to find a fix?

      Delete
    7. My HTC one X was giving me a black screen with no corruption.
      it's working now. OpenCV developers fixed it in the latest update.
      Maybe you need to use OpenCV manager instead of having the libraries inside the your app.
      I will add how to do this but not now may be tomorrow or something.

      Sorry, for not been able to respond to any of your comments these last period,I was finishing my bachelor thesis :).

      Delete
    8. Yes, I'm using the OpenCV Manager. I only get the corruption on enabling depth test with glEnable(GL_DEPTH_TEST).

      Delete
    9. Btw how much FPS are you getting on the One X. I'm only getting about 20.

      Delete
    10. Yeah, same here the one X is too slow in general, it is not your fault.

      Delete
    11. My task is to have about 30 on the Galaxy S3. I guess if you're getting 30 on the Galaxy Note, then S3 should too, shouldn't it?

      Delete
    12. I think I have tested my app on a S3 last week and I didn't see any problems with the frame rate.

      Delete
    13. I'm just confirming, you got 30 fps on the Galaxy Note, right? Not the Galaxy Note 2.

      Delete
    14. yes, Note not Note 2, actually I am working on it right now :D

      Delete
  11. Hi,
    This tutorial helps me get rid of the pressure to optimise the important code for processing a frame.
    I have one small question:
    How did you manage to get the ndk-build automatically install all the native_camera libraries? I have to do it manually.
    Only the opencv_java.so library gets automatic installation.
    Here's my Android.mk, I got rid of all the "LOCAL_MODULE := camera-prebuilt" and also the "LOCAL_MODULE := opencv-prebuild" because they produce no difference.

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    OPENCV_INSTALL_MODULES:=on
    OPENCV_CAMERA_MODULES:=on
    OPENCV_LIB_TYPE:=SHARED

    OPENCV_PATH := /home/tran/OpenCV-2.4.5-android-sdk/sdk/native

    include $(OPENCV_PATH)/jni/OpenCV.mk
    LOCAL_SRC_FILES := cameramodule.cpp
    LOCAL_C_INCLUDES += $(LOCAL_PATH) $(LOCAL_PATH)/fann
    LOCAL_LDLIBS += -lGLESv1_CM -ldl -llog
    LOCAL_MODULE := native
    include $(BUILD_SHARED_LIBRARY)

    ReplyDelete
  12. Hey,

    I am making an application which requires me to access the camera and do some pixel manipulation on the real time image. I think this is exactly what I want but the problem is I have no experience with JNI projects. Can you help me with this?

    ReplyDelete
  13. Hi
    I am new to OpenCV and android working on thesis project. I have problems accessing OpenCV camera on motorolla mobile, I think this post has the solution. I run your code but it doesn't work. I got the following in my LogCat.

    Frames received: 0 grabbed: 0
    CameraHandler destructor is called
    ..
    ..
    No Implemntation found for native com/../Native;.initCamera
    thread exiting with uncaught exception

    FATAL EXCEPTION: GLThread 11
    java.lang.UnsatisfiedLinkError: initCamera

    ReplyDelete
    Replies
    1. I solved the above errors but the camera doesn't show any frames. the Frames received is still 0. It only show black screen.

      Delete
  14. very good blog.

    can you please provide source project?
    I tried to run it on droid ultra but crashes at camera preview. I don't know what I did wrong. I am new to opencv and opengl.

    thank you.

    ReplyDelete
    Replies
    1. Here you can find the source code:
      https://github.com/MESAI/NativeCamera

      urw

      Delete
    2. thank you.

      I tried to deploy your apk into droid ultra version 4.2.2 but the app crashes at camera preview.

      Delete
    3. I guess that means that droid ultra is not compatible

      Delete
    4. Thank you for this tutorial.. I received a "BlogNativeCamera has stopped" error. I deployed apk into Samsung Galaxy Tab 3 (Android 4.1.2). Is there any other way to check if this is due to apk error or camera compatibility issue? I would want to make sure my apk is generated with the correct settings since I've gone through many errors after downloading the NativeCamera files.

      Delete
  15. Hi, Thank you.

    I tried it on Samsung Galxay 3 and it works. I can get about 15 fps.

    Do you know if I can put some condition so for Samsung phones, I use native camera, for Motorola phones, I use java camaera? do you have any idea how to add this into your current code? if you can point me some example, it will be very helpful.

    thank you very much.

    ReplyDelete
  16. Hi Mina,

    Thank you very much for this blog! you are my hero!
    I got your code to run on a HTC Desire HD. I had to comment the glEnable(GL_DEPTH_TEST) in surfaceChanged to prevent getting a corrupted image.
    Now I want to run it in portrait mode. I see there is code to handle this in surfaceChanged(), and I tried to use orientation = 2 but I just see a black screen. Do you know how to get it to work in portrait mode?

    Thanks
    Martin

    ReplyDelete
  17. Hi thanks for your detailed tutorial.
    I have still a problem with loading libnative_camera. I user ndk r8d, opencv 2.4.7 and android api 14. My project setup is correctly I guess because the ndk build looks good:

    Install : libNativeCamera.so => libs/armeabi-v7a/libNativeCamera.so
    Install : libnative_camera_r4.2.0.so => libs/armeabi-v7a/libnative_camera_r4.2.0.so
    Install : libnative_camera_r4.1.1.so => libs/armeabi-v7a/libnative_camera_r4.1.1.so
    Install : libnative_camera_r4.0.3.so => libs/armeabi-v7a/libnative_camera_r4.0.3.so
    Install : libnative_camera_r4.0.0.so => libs/armeabi-v7a/libnative_camera_r4.0.0.so
    Install : libnative_camera_r3.0.1.so => libs/armeabi-v7a/libnative_camera_r3.0.1.so
    Install : libnative_camera_r2.3.3.so => libs/armeabi-v7a/libnative_camera_r2.3.3.so
    Install : libnative_camera_r2.2.0.so => libs/armeabi-v7a/libnative_camera_r2.2.0.so
    Install : libopencv_java.so => libs/armeabi-v7a/libopencv_java.so

    libNativeCamera.so and libopencv_java.so are in libs/armeabi-v7a/ right now.

    But when I run the app on my nexus 4 he can't load native_camera_rX.X.X.so

    12-02 21:44:37.289: D/OpenCV::camera(10743): Libraries folder found: /data/app-lib/com.blogspot.mesai0-1/
    12-02 21:44:37.289: D/OpenCV::camera(10743): CameraWrapperConnector::connectToLib: folderPath=/data/app-lib/com.blogspot.mesai0-1/
    12-02 21:44:37.289: E/OpenCV::camera(10743): ||libnative_camera_r2.3.3.so
    12-02 21:44:37.289: E/OpenCV::camera(10743): ||libnative_camera_r4.0.3.so
    12-02 21:44:37.289: E/OpenCV::camera(10743): ||libnative_camera_r4.1.1.so
    12-02 21:44:37.289: E/OpenCV::camera(10743): ||libnative_camera_r4.2.0.so
    12-02 21:44:37.289: E/OpenCV::camera(10743): ||libnative_camera_r2.2.0.so
    12-02 21:44:37.289: E/OpenCV::camera(10743): ||libnative_camera_r3.0.1.so
    12-02 21:44:37.289: E/OpenCV::camera(10743): ||libnative_camera_r4.0.0.so
    12-02 21:44:37.289: D/OpenCV::camera(10743): try to load library 'libnative_camera_r4.2.0.so'
    12-02 21:44:37.289: D/OpenCV::camera(10743): CameraWrapperConnector::connectToLib ERROR: cannot dlopen camera wrapper library /data/app-lib/com.blogspot.mesai0-1/libnative_camera_r4.2.0.so, dlerror="dlopen failed: cannot locate symbol "_ZN7android6Camera10disconnectEv" referenced by "libnative_camera_r4.2.0.so"…"

    12-02 21:44:37.289: E/OpenCV::camera(10743): CameraWrapperConnector::connectToLib ERROR: cannot dlopen camera wrapper library
    12-02 21:44:37.289: E/OpenCV::camera(10743): Native_camera returned opening error: 4
    12-02 21:44:37.289: D/AndroidRuntime(10743): Shutting down VM

    Can you tell me whats going wrong please. What can I do?

    ReplyDelete
    Replies
    1. can you try the apk on my repo
      https://github.com/MESAI/NativeCamera/blob/master/BlogNativeCamera/bin/BlogNativeCamera.apk
      and tell if it works

      Delete
    2. I did it. Starting your apk causes an error too.
      I'm using an nexus 4 with android 4.4 and have installed latest OpenCV Manager 2.14 from Play Store.
      Do you have any idea what could be the problem?

      Delete
    3. @rs Hey have you been able to figure it out? I'm getting similar error on my Nexus 4, it can't find the ZN7android6Camera10disconnectEv symbol.

      Delete
  18. This comment has been removed by the author.

    ReplyDelete
  19. Hi I successfully able to run your sample I need your help to get frame of camera , In your sample can we receive the each frame byte so over that I can scan the byte array of each frame. I do not know the opengl but I can logically able to understood the flow, In below draw method you comments the fps rate but can I receive the 30 bytes per frame here.

    public void onDrawFrame(GL10 gl) {
    // long startTime = System.currentTimeMillis();
    Native.renderBackground();
    // long endTime = System.currentTimeMillis();
    // if(30-(endTime-startTime)>0){
    // try {
    // Thread.sleep(30-(endTime-startTime));
    // } catch (InterruptedException e) {}
    // }
    // endTime = System.currentTimeMillis();
    //System.out.println(endTime-startTime+" ms");
    }

    ReplyDelete
    Replies
    1. In this case it is better to use the Android camera previewer.
      The goal of this code is to make you process later the frame in the native part instead of getting the frame from java then send it down to the native code. This is too slow. So simply use the Android java camera previewer, this one can provide you with the frame directly instead of getting it from the native code then send it up to the java part (which "I guess" will be slow).

      Delete
    2. In general, the CV::Mat rgbFrame variable is holding the camera frame, if you know OpenCV you can use it directly in the native part.

      Delete
  20. Thank you for reply Mind Saad,

    Currently I used an android camera preview callback, but I could not able to receive constant frame rate, I also found that It might be hardware dependencies. My issue is I could not able to received the constant 28 or 30 fps frame using android camera preview callback. I want to scan each frame data, I want to move on opengl for scan each frame so that it can fast not block the preview callback , but as you guess "will be slow" native to java then Guess I just need to rethink it.

    Do you have any idea about constant frame rate in android even using OpenGL.

    ReplyDelete
  21. hello thanks in advance for nice tutorial i ma using OpenCV Library - 2.4.11 but i got follwing error


    "d:\\AANDROIDWORKFOLDER\\android-ndk-r10e\\ndk-build.cmd"
    Android NDK: ERROR:jni/Android.mk:opencv-prebuilt: LOCAL_SRC_FILES points to a missing file
    d:/AANDROIDWORKFOLDER/android-ndk-r10e/build/core/prebuilt-library.mk:45: *** Android NDK: Aborting . Stop.
    Android NDK: Check that jni/build/libs/armeabi-v7a/libopencv_java.so exists or that its path is correct


    please help me

    ReplyDelete
    Replies
    1. do you have the content of OpenCV_Directory/sdk/native/libs/armeabi-v7a in the jni/build/libs/armeabi-v7a/ of your project ?
      Also you need the include files too.

      Delete
    2. This comment has been removed by the author.

      Delete
  22. thanks in advance
    hello sir i include thid directory but when i run this project than i am getting following error

    10-05 12:32:04.927: E/AndroidRuntime(2055): java.lang.UnsatisfiedLinkError: Native method not found: org.opencv.highgui.VideoCapture.n_VideoCapture:(I)J

    ReplyDelete
  23. All your hard work is much appreciated. Nobody can stop to admire you. Lots of appreciation. end zone video

    ReplyDelete