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