diff --git a/samples/android/tutorial-4-opencl/.classpath b/samples/android/tutorial-4-opencl/.classpath new file mode 100644 index 0000000000..b76ec6cd48 --- /dev/null +++ b/samples/android/tutorial-4-opencl/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/samples/android/tutorial-4-opencl/.cproject b/samples/android/tutorial-4-opencl/.cproject new file mode 100644 index 0000000000..9f3b5fd84f --- /dev/null +++ b/samples/android/tutorial-4-opencl/.cproject @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/android/tutorial-4-opencl/.project b/samples/android/tutorial-4-opencl/.project new file mode 100644 index 0000000000..c86a2386c4 --- /dev/null +++ b/samples/android/tutorial-4-opencl/.project @@ -0,0 +1,49 @@ + + + OpenCV Tutorial 4 - Use OpenCL + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/samples/android/tutorial-4-opencl/.settings/org.eclipse.jdt.core.prefs b/samples/android/tutorial-4-opencl/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000000..48ab4c6b11 --- /dev/null +++ b/samples/android/tutorial-4-opencl/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/samples/android/tutorial-4-opencl/AndroidManifest.xml b/samples/android/tutorial-4-opencl/AndroidManifest.xml new file mode 100644 index 0000000000..6bce3c7191 --- /dev/null +++ b/samples/android/tutorial-4-opencl/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/samples/android/tutorial-4-opencl/CMakeLists.txt b/samples/android/tutorial-4-opencl/CMakeLists.txt new file mode 100644 index 0000000000..2d529ffb7a --- /dev/null +++ b/samples/android/tutorial-4-opencl/CMakeLists.txt @@ -0,0 +1,12 @@ +set(sample example-tutorial-4-opencl) + +if(BUILD_FAT_JAVA_LIB) + set(native_deps opencv_java) +else() + set(native_deps opencv_imgproc) +endif() + +add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS ${OpenCV_BINARY_DIR} SDK_TARGET 21 ${ANDROID_SDK_TARGET} NATIVE_DEPS ${native_deps}) +if(TARGET ${sample}) + add_dependencies(opencv_android_examples ${sample}) +endif() diff --git a/samples/android/tutorial-4-opencl/jni/Android.mk b/samples/android/tutorial-4-opencl/jni/Android.mk new file mode 100644 index 0000000000..1981bbdc0c --- /dev/null +++ b/samples/android/tutorial-4-opencl/jni/Android.mk @@ -0,0 +1,9 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := JNIrender +LOCAL_SRC_FILES := jni.c GLrender.cpp +LOCAL_LDLIBS += -llog -lGLESv2 -lEGL + +include $(BUILD_SHARED_LIBRARY) \ No newline at end of file diff --git a/samples/android/tutorial-4-opencl/jni/Application.mk b/samples/android/tutorial-4-opencl/jni/Application.mk new file mode 100644 index 0000000000..5c7ca0e470 --- /dev/null +++ b/samples/android/tutorial-4-opencl/jni/Application.mk @@ -0,0 +1,4 @@ +#APP_STL := gnustl_shared +#APP_GNUSTL_FORCE_CPP_FEATURES := exceptions rtti +APP_ABI := armeabi-v7a +APP_PLATFORM := android-14 diff --git a/samples/android/tutorial-4-opencl/jni/GLrender.cpp b/samples/android/tutorial-4-opencl/jni/GLrender.cpp new file mode 100644 index 0000000000..a94f33f73e --- /dev/null +++ b/samples/android/tutorial-4-opencl/jni/GLrender.cpp @@ -0,0 +1,300 @@ +#include +#include + +#include "common.hpp" + +float vertexes[] = { + -1.0f, -1.0f, + -1.0f, 1.0f, + 1.0f, -1.0f, + 1.0f, 1.0f +}; +float texCoordOES[] = { + 0.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 1.0f, + 1.0f, 0.0f +}; +float texCoord2D[] = { + 0.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 0.0f, + 1.0f, 1.0f +}; + +const char vss[] = \ + "attribute vec2 vPosition;\n" \ + "attribute vec2 vTexCoord;\n" \ + "varying vec2 texCoord;\n" \ + "void main() {\n" \ + " texCoord = vTexCoord;\n" \ + " gl_Position = vec4 ( vPosition.x, vPosition.y, 0.0, 1.0 );\n" \ + "}"; + +const char fssOES[] = \ + "#extension GL_OES_EGL_image_external : require\n" \ + "precision mediump float;\n" \ + "uniform samplerExternalOES sTexture;\n" \ + "varying vec2 texCoord;\n" \ + "void main() {\n" \ + " gl_FragColor = texture2D(sTexture,texCoord);\n" \ + "}"; + +const char fss2D[] = \ + "precision mediump float;\n" \ + "uniform sampler2D sTexture;\n" \ + "varying vec2 texCoord;\n" \ + "void main() {\n" \ + " gl_FragColor = texture2D(sTexture,texCoord);\n" \ + "}"; + +int progOES = 0; +int prog2D = 0; + +GLuint FBOtex = 0; +GLuint FBO = 0; + +GLuint texOES = 0; +int texWidth = 0, texHeight = 0; + +static inline void deleteTex(GLuint* tex) +{ + if(tex && *tex) + { + glDeleteTextures(1, tex); + *tex = 0; + } +} + +static void releaseFBO() +{ + if (FBO != 0) + { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &FBO); + FBO = 0; + } + deleteTex(&FBOtex); + glDeleteProgram(prog2D); + prog2D = 0; +} + +static inline void logShaderCompileError(GLuint shader) +{ + GLchar msg[512]; + msg[0] = 0; + GLsizei len; + glGetShaderInfoLog(shader, sizeof(msg) - 1, &len, msg); + LOGE("Could not compile shader: %s", msg); +} + +static int makeShaderProg(const char* vss, const char* fss) +{ + LOGD("makeShaderProg: setup GL_VERTEX_SHADER"); + GLuint vshader = glCreateShader(GL_VERTEX_SHADER); + const GLchar* text = vss; + glShaderSource(vshader, 1, &text, 0); + glCompileShader(vshader); + int compiled; + glGetShaderiv(vshader, GL_COMPILE_STATUS, &compiled); + if (compiled == 0) { + logShaderCompileError(vshader); + glDeleteShader(vshader); + vshader = 0; + } + + LOGD("makeShaderProg: setup GL_FRAGMENT_SHADER"); + GLuint fshader = glCreateShader(GL_FRAGMENT_SHADER); + text = fss; + glShaderSource(fshader, 1, &text, 0); + glCompileShader(fshader); + glGetShaderiv(fshader, GL_COMPILE_STATUS, &compiled); + if (compiled == 0) { + logShaderCompileError(fshader); + glDeleteShader(fshader); + fshader = 0; + } + + LOGD("makeShaderProg: glCreateProgram"); + GLuint program = glCreateProgram(); + glAttachShader(program, vshader); + glAttachShader(program, fshader); + glLinkProgram(program); + + if(vshader) glDeleteShader(vshader); + if(fshader) glDeleteShader(fshader); + + return program; +} + +static void initFBO(int width, int height) +{ + releaseFBO(); + + glGenTextures(1, &FBOtex); + glBindTexture(GL_TEXTURE_2D, FBOtex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + //int hFBO; + glGenFramebuffers(1, &FBO); + glBindFramebuffer(GL_FRAMEBUFFER, FBO); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, FBOtex, 0); + LOGD("initFBO status: %d", glGetError()); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + LOGE("initFBO failed: %d", glCheckFramebufferStatus(GL_FRAMEBUFFER)); + + prog2D = makeShaderProg(vss, fss2D); +} + +void drawTex(int tex, GLenum texType, GLuint fbo) +{ + int64_t t = getTimeMs(); + //draw texture to FBO or to screen + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glViewport(0, 0, texWidth, texHeight); + + glClear(GL_COLOR_BUFFER_BIT); + + int prog = texType == GL_TEXTURE_EXTERNAL_OES ? progOES : prog2D; + glUseProgram(prog); + int vPos = glGetAttribLocation(prog, "vPosition"); + int vTC = glGetAttribLocation(prog, "vTexCoord"); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(texType, tex); + glUniform1i(glGetUniformLocation(prog, "sTexture"), 0); + + glVertexAttribPointer(vPos, 2, GL_FLOAT, false, 4*2, vertexes); + glVertexAttribPointer(vTC, 2, GL_FLOAT, false, 4*2, texType == GL_TEXTURE_EXTERNAL_OES ? texCoordOES : texCoord2D); + glEnableVertexAttribArray(vPos); + glEnableVertexAttribArray(vTC); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glFlush(); + LOGD("drawTex(%u) costs %d ms", tex, getTimeInterval(t)); +} + +void drawFrameOrig() +{ + drawTex(texOES, GL_TEXTURE_EXTERNAL_OES, 0); +} + +void procCPU(char* buff, int w, int h) +{ + int64_t t = getTimeMs(); + for(int i=0; i BUFF_SIZE) + { + LOGE("Internal temp buffer is too small, can't make CPU frame processing"); + return; + } + + // read + t = getTimeMs(); + glReadPixels(0, 0, texWidth, texHeight, GL_RGBA, GL_UNSIGNED_BYTE, tmpBuff); + LOGD("glReadPixels() costs %d ms", getTimeInterval(t)); + + // modify + procCPU(tmpBuff, texWidth, texHeight); + + // write back + t = getTimeMs(); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texWidth, texHeight, GL_RGBA, GL_UNSIGNED_BYTE, tmpBuff); + LOGD("glTexSubImage2D() costs %d ms", getTimeInterval(t)); + + // render to screen + drawTex(FBOtex, GL_TEXTURE_2D, 0); +} + +void procOCL(int tex, int w, int h) +{ + //TODO: not yet implemented +} + +void drawFrameProcOCL() +{ + drawTex(texOES, GL_TEXTURE_EXTERNAL_OES, FBO); + + // modify pixels in FBO texture using OpenCL and CL-GL interop + procOCL(FBOtex, texWidth, texHeight); + + // render to screen + drawTex(FBOtex, GL_TEXTURE_2D, 0); +} + + +extern "C" void drawFrame() +{ + LOGD("*** drawFrame() ***"); + int64_t t = getTimeMs(); + //drawFrameOrig(); + drawFrameProcCPU(); + glFinish(); + LOGD("*** drawFrame() costs %d ms ***", getTimeInterval(t)); +} + +extern "C" void closeGL() +{ + LOGD("closeGL"); + deleteTex(&texOES); + + glUseProgram(0); + glDeleteProgram(progOES); + progOES = 0; + + releaseFBO(); +} + +extern "C" int initGL() +{ + LOGD("initGL"); + + closeGL(); + + const char* vs = (const char*)glGetString(GL_VERSION); + LOGD("GL_VERSION = %s", vs); + + progOES = makeShaderProg(vss, fssOES); + + glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + + texOES = 0; + glGenTextures(1, &texOES); + glBindTexture(GL_TEXTURE_EXTERNAL_OES, texOES); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + return texOES; +} + +extern "C" void changeSize(int width, int height) +{ + const int MAX_W=1<<11, MAX_H=1<<11; + LOGD("changeSize: %dx%d", width, height); + texWidth = width <= MAX_W ? width : MAX_W; + texHeight = height <= MAX_H ? height : MAX_H; + initFBO(texWidth, texHeight); +} diff --git a/samples/android/tutorial-4-opencl/jni/common.hpp b/samples/android/tutorial-4-opencl/jni/common.hpp new file mode 100644 index 0000000000..20b882a9f1 --- /dev/null +++ b/samples/android/tutorial-4-opencl/jni/common.hpp @@ -0,0 +1,19 @@ +#include +#define LOG_TAG "JNIRenderer" +//#define LOGD(...) +#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) +#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) + +#include // clock_gettime + +static inline int64_t getTimeMs() +{ + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + return (int64_t) now.tv_sec*1000 + now.tv_nsec/1000000; +} + +static inline int getTimeInterval(int64_t startTime) +{ + return int(getTimeMs() - startTime); +} diff --git a/samples/android/tutorial-4-opencl/jni/jni.c b/samples/android/tutorial-4-opencl/jni/jni.c new file mode 100644 index 0000000000..6bc5fe3515 --- /dev/null +++ b/samples/android/tutorial-4-opencl/jni/jni.c @@ -0,0 +1,26 @@ +#include + +int initGL(); +void closeGL(); +void changeSize(int width, int height); +void drawFrame(); + +JNIEXPORT jint JNICALL Java_org_opencv_samples_tutorial4_NativeGLRenderer_initGL(JNIEnv * env, jclass cls) +{ + return initGL(); +} + +JNIEXPORT void JNICALL Java_org_opencv_samples_tutorial4_NativeGLRenderer_closeGL(JNIEnv * env, jclass cls) +{ + closeGL(); +} + +JNIEXPORT void JNICALL Java_org_opencv_samples_tutorial4_NativeGLRenderer_changeSize(JNIEnv * env, jclass cls, jint width, jint height) +{ + changeSize(width, height); +} + +JNIEXPORT void JNICALL Java_org_opencv_samples_tutorial4_NativeGLRenderer_drawFrame(JNIEnv * env, jclass cls) +{ + drawFrame(); +} diff --git a/samples/android/tutorial-4-opencl/lint.xml b/samples/android/tutorial-4-opencl/lint.xml new file mode 100644 index 0000000000..ee0eead5bb --- /dev/null +++ b/samples/android/tutorial-4-opencl/lint.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/samples/android/tutorial-4-opencl/res/drawable/icon.png b/samples/android/tutorial-4-opencl/res/drawable/icon.png new file mode 100644 index 0000000000..630454927b Binary files /dev/null and b/samples/android/tutorial-4-opencl/res/drawable/icon.png differ diff --git a/samples/android/tutorial-4-opencl/res/layout/activity.xml b/samples/android/tutorial-4-opencl/res/layout/activity.xml new file mode 100644 index 0000000000..17eaf23425 --- /dev/null +++ b/samples/android/tutorial-4-opencl/res/layout/activity.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/samples/android/tutorial-4-opencl/res/values/strings.xml b/samples/android/tutorial-4-opencl/res/values/strings.xml new file mode 100644 index 0000000000..b7e6bed91a --- /dev/null +++ b/samples/android/tutorial-4-opencl/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + OpenCV Tutorial 4 - Use OpenCL + + diff --git a/samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/Camera2Renderer.java b/samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/Camera2Renderer.java new file mode 100644 index 0000000000..a0040ad3b4 --- /dev/null +++ b/samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/Camera2Renderer.java @@ -0,0 +1,282 @@ +package org.opencv.samples.tutorial4; + +import java.util.Arrays; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Point; +import android.graphics.SurfaceTexture; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.os.Handler; +import android.os.HandlerThread; +import android.util.Log; +import android.util.Size; +import android.view.Surface; + +@SuppressLint("NewApi") public class Camera2Renderer extends MyGLRendererBase { + + protected final String LOGTAG = "Camera2Renderer"; + private CameraDevice mCameraDevice; + private CameraCaptureSession mCaptureSession; + private CaptureRequest.Builder mPreviewRequestBuilder; + private String mCameraID; + private Size mPreviewSize = new Size(1280, 720); + + private HandlerThread mBackgroundThread; + private Handler mBackgroundHandler; + private Semaphore mCameraOpenCloseLock = new Semaphore(1); + + Camera2Renderer(MyGLSurfaceView view) { + super(view); + } + + public void onResume() { + stopBackgroundThread(); + super.onResume(); + startBackgroundThread(); + } + + public void onPause() { + super.onPause(); + stopBackgroundThread(); + } + + boolean cacPreviewSize(final int width, final int height) { + Log.i(LOGTAG, "cacPreviewSize: "+width+"x"+height); + if(mCameraID == null) + return false; + CameraManager manager = (CameraManager) mView.getContext() + .getSystemService(Context.CAMERA_SERVICE); + try { + CameraCharacteristics characteristics = manager + .getCameraCharacteristics(mCameraID); + StreamConfigurationMap map = characteristics + .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + int bestWidth = 0, bestHeight = 0; + float aspect = (float)width / height; + for (Size psize : map.getOutputSizes(SurfaceTexture.class)) { + int w = psize.getWidth(), h = psize.getHeight(); + Log.d(LOGTAG, "trying size: "+w+"x"+h); + if ( width >= w && height >= h && + bestWidth <= w && bestHeight <= h && + Math.abs(aspect - (float)w/h) < 0.2 ) { + bestWidth = w; + bestHeight = h; + //mPreviewSize = psize; + } + } + Log.i(LOGTAG, "best size: "+bestWidth+"x"+bestHeight); + if( mPreviewSize.getWidth() == bestWidth && + mPreviewSize.getHeight() == bestHeight ) + return false; + else { + mPreviewSize = new Size(bestWidth, bestHeight); + return true; + } + } catch (CameraAccessException e) { + Log.e(LOGTAG, "cacPreviewSize - Camera Access Exception"); + } catch (IllegalArgumentException e) { + Log.e(LOGTAG, "cacPreviewSize - Illegal Argument Exception"); + } catch (SecurityException e) { + Log.e(LOGTAG, "cacPreviewSize - Security Exception"); + } + return false; + } + + protected void openCamera() { + Log.i(LOGTAG, "openCamera"); + //closeCamera(); + CameraManager manager = (CameraManager) mView.getContext() + .getSystemService(Context.CAMERA_SERVICE); + try { + for (String cameraID : manager.getCameraIdList()) { + CameraCharacteristics characteristics = manager + .getCameraCharacteristics(cameraID); + if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) + continue; + + mCameraID = cameraID; + break; + } + if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) { + throw new RuntimeException( + "Time out waiting to lock camera opening."); + } + manager.openCamera(mCameraID, mStateCallback, mBackgroundHandler); + } catch (CameraAccessException e) { + Log.e(LOGTAG, "OpenCamera - Camera Access Exception"); + } catch (IllegalArgumentException e) { + Log.e(LOGTAG, "OpenCamera - Illegal Argument Exception"); + } catch (SecurityException e) { + Log.e(LOGTAG, "OpenCamera - Security Exception"); + } catch (InterruptedException e) { + Log.e(LOGTAG, "OpenCamera - Interrupted Exception"); + } + } + + protected void closeCamera() { + Log.i(LOGTAG, "closeCamera"); + try { + mCameraOpenCloseLock.acquire(); + if (null != mCaptureSession) { + mCaptureSession.close(); + mCaptureSession = null; + } + if (null != mCameraDevice) { + mCameraDevice.close(); + mCameraDevice = null; + } + } catch (InterruptedException e) { + throw new RuntimeException( + "Interrupted while trying to lock camera closing.", e); + } finally { + mCameraOpenCloseLock.release(); + } + } + + private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { + + @Override + public void onOpened(CameraDevice cameraDevice) { + mCameraDevice = cameraDevice; + mCameraOpenCloseLock.release(); + createCameraPreviewSession(); + } + + @Override + public void onDisconnected(CameraDevice cameraDevice) { + //mCameraOpenCloseLock.release(); + cameraDevice.close(); + mCameraDevice = null; + } + + @Override + public void onError(CameraDevice cameraDevice, int error) { + cameraDevice.close(); + mCameraDevice = null; + mCameraOpenCloseLock.release(); + } + + }; + + private void createCameraPreviewSession() { + Log.i(LOGTAG, "createCameraPreviewSession"); + try { + mCameraOpenCloseLock.acquire(); + if (null == mCameraDevice) { + mCameraOpenCloseLock.release(); + Log.e(LOGTAG, "createCameraPreviewSession: camera isn't opened"); + return; + } + if (null != mCaptureSession) { + mCameraOpenCloseLock.release(); + Log.e(LOGTAG, "createCameraPreviewSession: mCaptureSession is already started"); + return; + } + if(null == mSTex) { + Log.e(LOGTAG, "createCameraPreviewSession: preview SurfaceTexture is null"); + return; + } + Log.d(LOGTAG, "starting preview "+mPreviewSize.getWidth()+"x"+mPreviewSize.getHeight()); + mSTex.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); + + Surface surface = new Surface(mSTex); + Log.d(LOGTAG, "createCameraPreviewSession: surface = " + surface); + + mPreviewRequestBuilder = mCameraDevice + .createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); + mPreviewRequestBuilder.addTarget(surface); + + mCameraDevice.createCaptureSession(Arrays.asList(surface), + new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured( + CameraCaptureSession cameraCaptureSession) { + mCaptureSession = cameraCaptureSession; + try { + mPreviewRequestBuilder + .set(CaptureRequest.CONTROL_AF_MODE, + CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); + mPreviewRequestBuilder + .set(CaptureRequest.CONTROL_AE_MODE, + CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); + + mCaptureSession.setRepeatingRequest( + mPreviewRequestBuilder.build(), null, + mBackgroundHandler); + } catch (CameraAccessException e) { + Log.e(LOGTAG, "createCaptureSession failed"); + } + mCameraOpenCloseLock.release(); + } + + @Override + public void onConfigureFailed( + CameraCaptureSession cameraCaptureSession) { + Log.e(LOGTAG, "createCameraPreviewSession failed"); + mCameraOpenCloseLock.release(); + } + }, null); + } catch (CameraAccessException e) { + Log.e(LOGTAG, "createCameraPreviewSession"); + } catch (InterruptedException e) { + throw new RuntimeException( + "Interrupted while createCameraPreviewSession", e); + } + finally { + mCameraOpenCloseLock.release(); + } + } + + private void startBackgroundThread() { + Log.i(LOGTAG, "startBackgroundThread"); + mBackgroundThread = new HandlerThread("CameraBackground"); + mBackgroundThread.start(); + mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); + } + + private void stopBackgroundThread() { + Log.i(LOGTAG, "stopBackgroundThread"); + if(mBackgroundThread == null) + return; + mBackgroundThread.quitSafely(); + try { + mBackgroundThread.join(); + mBackgroundThread = null; + mBackgroundHandler = null; + } catch (InterruptedException e) { + Log.e(LOGTAG, "stopBackgroundThread"); + } + } + + @Override + protected void setCameraPreviewSize(int width, int height) { + //mPreviewSize = new Size(width, height); + if( !cacPreviewSize(width, height) ) + return; + try { + mCameraOpenCloseLock.acquire(); + if (null != mCaptureSession) { + mCaptureSession.close(); + mCaptureSession = null; + } + mCameraOpenCloseLock.release(); + createCameraPreviewSession(); + } catch (InterruptedException e) { + mCameraOpenCloseLock.release(); + throw new RuntimeException( + "Interrupted while setCameraPreviewSize.", e); + } + } +} diff --git a/samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/CameraRenderer.java b/samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/CameraRenderer.java new file mode 100644 index 0000000000..692ab9884e --- /dev/null +++ b/samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/CameraRenderer.java @@ -0,0 +1,75 @@ +package org.opencv.samples.tutorial4; + +import java.io.IOException; +import java.util.List; + +import android.hardware.Camera; +import android.hardware.Camera.Size; +import android.util.Log; + +@SuppressWarnings("deprecation") +public class CameraRenderer extends MyGLRendererBase { + + protected final String LOGTAG = "CameraRenderer"; + private Camera mCamera; + boolean mPreviewStarted = false; + + CameraRenderer(MyGLSurfaceView view) { + super(view); + } + + protected void closeCamera() { + Log.i(LOGTAG, "closeCamera"); + if(mCamera != null) { + mCamera.stopPreview(); + mPreviewStarted = false; + mCamera.release(); + mCamera = null; + } + } + + protected void openCamera() { + Log.i(LOGTAG, "openCamera"); + closeCamera(); + mCamera = Camera.open(); + try { + mCamera.setPreviewTexture(mSTex); + } catch (IOException ioe) { + Log.e(LOGTAG, "setPreviewTexture() failed: " + ioe.getMessage()); + } + } + + public void setCameraPreviewSize(int width, int height) { + Log.i(LOGTAG, "setCameraPreviewSize: "+width+"x"+height); + if(mCamera == null) + return; + if(mPreviewStarted) { + mCamera.stopPreview(); + mPreviewStarted = false; + } + Camera.Parameters param = mCamera.getParameters(); + List psize = param.getSupportedPreviewSizes(); + int bestWidth = 0, bestHeight = 0; + if (psize.size() > 0) { + float aspect = (float)width / height; + for (Size size : psize) { + int w = size.width, h = size.height; + Log.d("Renderer", "checking camera preview size: "+w+"x"+h); + if ( w <= width && h <= height && + w >= bestWidth && h >= bestHeight && + Math.abs(aspect - (float)w/h) < 0.2 ) { + bestWidth = w; + bestHeight = h; + } + } + if(bestWidth > 0 && bestHeight > 0) { + param.setPreviewSize(bestWidth, bestHeight); + Log.i(LOGTAG, "size: "+bestWidth+" x "+bestHeight); + } + } + param.set("orientation", "landscape"); + mCamera.setParameters(param); + mCamera.startPreview(); + mPreviewStarted = true; + } +} diff --git a/samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/MyGLRendererBase.java b/samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/MyGLRendererBase.java new file mode 100644 index 0000000000..f23d7e4969 --- /dev/null +++ b/samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/MyGLRendererBase.java @@ -0,0 +1,100 @@ +package org.opencv.samples.tutorial4; + +import javax.microedition.khronos.egl.EGLConfig; +import javax.microedition.khronos.opengles.GL10; + +import android.graphics.SurfaceTexture; +import android.opengl.GLES20; +import android.opengl.GLSurfaceView; +import android.util.Log; + +public abstract class MyGLRendererBase implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener { + protected final String LOGTAG = "MyGLRendererBase"; + protected int frameCounter; + protected long lastNanoTime; + + protected SurfaceTexture mSTex; + protected MyGLSurfaceView mView; + + protected boolean mGLInit = false; + protected boolean mTexUpdate = false; + + MyGLRendererBase(MyGLSurfaceView view) { + mView = view; + } + + protected abstract void openCamera(); + protected abstract void closeCamera(); + protected abstract void setCameraPreviewSize(int width, int height); + + public void onResume() { + Log.i(LOGTAG, "onResume"); + frameCounter = 0; + lastNanoTime = System.nanoTime(); + } + + public void onPause() { + Log.i(LOGTAG, "onPause"); + mGLInit = false; + mTexUpdate = false; + closeCamera(); + if(mSTex != null) { + mSTex.release(); + mSTex = null; + NativeGLRenderer.closeGL(); + } + } + + @Override + public synchronized void onFrameAvailable(SurfaceTexture surfaceTexture) { + //Log.i(LOGTAG, "onFrameAvailable"); + mTexUpdate = true; + mView.requestRender(); + } + + @Override + public void onDrawFrame(GL10 gl) { + //Log.i(LOGTAG, "onDrawFrame"); + if (!mGLInit) + return; + + synchronized (this) { + if (mTexUpdate) { + mSTex.updateTexImage(); + mTexUpdate = false; + } + } + NativeGLRenderer.drawFrame(); + + // log FPS + frameCounter++; + if(frameCounter >= 10) + { + int fps = (int) (frameCounter * 1e9 / (System.nanoTime() - lastNanoTime)); + Log.i(LOGTAG, "drawFrame() FPS: "+fps); + frameCounter = 0; + lastNanoTime = System.nanoTime(); + } + } + + @Override + public void onSurfaceChanged(GL10 gl, int surfaceWidth, int surfaceHeight) { + Log.i(LOGTAG, "onSurfaceChanged("+surfaceWidth+"x"+surfaceHeight+")"); + NativeGLRenderer.changeSize(surfaceWidth, surfaceHeight); + setCameraPreviewSize(surfaceWidth, surfaceHeight); + } + + @Override + public void onSurfaceCreated(GL10 gl, EGLConfig config) { + Log.i(LOGTAG, "onSurfaceCreated"); + String strGLVersion = GLES20.glGetString(GLES20.GL_VERSION); + if (strGLVersion != null) + Log.i(LOGTAG, "OpenGL ES version: " + strGLVersion); + + int hTex = NativeGLRenderer.initGL(); + mSTex = new SurfaceTexture(hTex); + mSTex.setOnFrameAvailableListener(this); + openCamera(); + mGLInit = true; + } +} diff --git a/samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/MyGLSurfaceView.java b/samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/MyGLSurfaceView.java new file mode 100644 index 0000000000..2f9a4572ff --- /dev/null +++ b/samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/MyGLSurfaceView.java @@ -0,0 +1,50 @@ +package org.opencv.samples.tutorial4; + +import android.content.Context; +import android.opengl.GLSurfaceView; +import android.view.SurfaceHolder; + +class MyGLSurfaceView extends GLSurfaceView { + + MyGLRendererBase mRenderer; + + MyGLSurfaceView(Context context) { + super(context); + + if(android.os.Build.VERSION.SDK_INT >= 21) + mRenderer = new Camera2Renderer(this); + else + mRenderer = new CameraRenderer(this); + + setEGLContextClientVersion(2); + setRenderer(mRenderer); + setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + super.surfaceCreated(holder); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + super.surfaceDestroyed(holder); + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + super.surfaceChanged(holder, format, w, h); + } + + @Override + public void onResume() { + super.onResume(); + mRenderer.onResume(); + } + + @Override + public void onPause() { + mRenderer.onPause(); + super.onPause(); + } +} diff --git a/samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/NativeGLRenderer.java b/samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/NativeGLRenderer.java new file mode 100644 index 0000000000..19a1337b7c --- /dev/null +++ b/samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/NativeGLRenderer.java @@ -0,0 +1,12 @@ +package org.opencv.samples.tutorial4; + +public class NativeGLRenderer { + static + { + System.loadLibrary("JNIrender"); + } + public static native int initGL(); + public static native void closeGL(); + public static native void drawFrame(); + public static native void changeSize(int width, int height); +} diff --git a/samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/Tutorial4Activity.java b/samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/Tutorial4Activity.java new file mode 100644 index 0000000000..556bf6f2e4 --- /dev/null +++ b/samples/android/tutorial-4-opencl/src/org/opencv/samples/tutorial4/Tutorial4Activity.java @@ -0,0 +1,38 @@ +package org.opencv.samples.tutorial4; + +import android.app.Activity; +import android.content.pm.ActivityInfo; +import android.os.Bundle; +import android.view.Window; +import android.view.WindowManager; + +public class Tutorial4Activity extends Activity { + + private MyGLSurfaceView mView; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, + WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + + mView = new MyGLSurfaceView(this); + setContentView(mView); + } + + @Override + protected void onPause() { + mView.onPause(); + super.onPause(); + } + + @Override + protected void onResume() { + super.onResume(); + mView.onResume(); + } +} \ No newline at end of file