diff --git a/android/apps/CVCamera/AndroidManifest.xml b/android/apps/CVCamera/AndroidManifest.xml new file mode 100644 index 000000000..0acd4e9ec --- /dev/null +++ b/android/apps/CVCamera/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/apps/CVCamera/Makefile b/android/apps/CVCamera/Makefile new file mode 100644 index 000000000..89d847d27 --- /dev/null +++ b/android/apps/CVCamera/Makefile @@ -0,0 +1,83 @@ +# The path to the NDK, requires crystax version r-4 for now, due to support +# for the standard library + +# load environment from local make file +LOCAL_ENV_MK=local.env.mk +ifneq "$(wildcard $(LOCAL_ENV_MK))" "" +include $(LOCAL_ENV_MK) +else +$(shell cp sample.$(LOCAL_ENV_MK) $(LOCAL_ENV_MK)) +$(info ERROR local environement not setup! try:) +$(info gedit $(LOCAL_ENV_MK)) +$(error Please setup the $(LOCAL_ENV_MK) - the default was just created') +endif + +ANDROID_NDK_BASE = $(ANDROID_NDK_ROOT) + +$(info OPENCV_CONFIG = $(OPENCV_CONFIG)) + +ifndef PROJECT_PATH +$(info PROJECT_PATH defaulting to this directory) +PROJECT_PATH=. +endif + + +# The name of the native library +LIBNAME = libcvcamera.so + + +# Find all the C++ sources in the native folder +SOURCES = $(wildcard jni/*.cpp) +HEADERS = $(wildcard jni/*.h) + +ANDROID_MKS = $(wildcard jni/*.mk) + +SWIG_IS = $(wildcard jni/*.i) + +SWIG_MAIN = jni/cvcamera.i + +SWIG_JAVA_DIR = src/com/theveganrobot/cvcamera/jni +SWIG_JAVA_OUT = $(wildcard $(SWIG_JAVA_DIR)/*.java) + + +SWIG_C_DIR = jni/gen +SWIG_C_OUT = $(SWIG_C_DIR)/cvcamera_swig.cpp + +BUILD_DEFS=OPENCV_CONFIG=$(OPENCV_CONFIG) \ + PROJECT_PATH=$(PROJECT_PATH) \ + V=$(V) \ + $(NDK_FLAGS) + +# The real native library stripped of symbols +LIB = libs/armeabi-v7a/$(LIBNAME) libs/armeabi/$(LIBNAME) + + +all: $(LIB) + + +#calls the ndk-build script, passing it OPENCV_ROOT and OPENCV_LIBS_DIR +$(LIB): $(SWIG_C_OUT) $(SOURCES) $(HEADERS) $(ANDROID_MKS) + $(ANDROID_NDK_BASE)/ndk-build $(BUILD_DEFS) + + +#this creates the swig wrappers +$(SWIG_C_OUT): $(SWIG_IS) + make clean-swig &&\ + mkdir -p $(SWIG_C_DIR) &&\ + mkdir -p $(SWIG_JAVA_DIR) &&\ + swig -java -c++ -I../../android-jni/jni -package "com.theveganrobot.cvcamera.jni" \ + -outdir $(SWIG_JAVA_DIR) \ + -o $(SWIG_C_OUT) $(SWIG_MAIN) + + +#clean targets +.PHONY: clean clean-swig cleanall + +#this deletes the generated swig java and the generated c wrapper +clean-swig: + rm -f $(SWIG_JAVA_OUT) $(SWIG_C_OUT) + +#does clean-swig and then uses the ndk-build clean +clean: clean-swig + $(ANDROID_NDK_BASE)/ndk-build clean $(BUILD_DEFS) + diff --git a/android/apps/CVCamera/README.txt b/android/apps/CVCamera/README.txt new file mode 100644 index 000000000..8bc5b3a90 --- /dev/null +++ b/android/apps/CVCamera/README.txt @@ -0,0 +1,2 @@ +see http://code.google.com/p/android-opencv/wiki/CVCamera + diff --git a/android/apps/CVCamera/build.sh b/android/apps/CVCamera/build.sh new file mode 100644 index 000000000..1497a39ad --- /dev/null +++ b/android/apps/CVCamera/build.sh @@ -0,0 +1 @@ +make V=0 diff --git a/android/apps/CVCamera/clean.sh b/android/apps/CVCamera/clean.sh new file mode 100644 index 000000000..121e391e4 --- /dev/null +++ b/android/apps/CVCamera/clean.sh @@ -0,0 +1 @@ +make OPENCV_ROOT=../../opencv V=0 clean diff --git a/android/apps/CVCamera/default.properties b/android/apps/CVCamera/default.properties new file mode 100644 index 000000000..66148fe91 --- /dev/null +++ b/android/apps/CVCamera/default.properties @@ -0,0 +1,12 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +android.library.reference.1=../../android-jni +# Project target. +target=android-7 diff --git a/android/apps/CVCamera/jni/Android.mk b/android/apps/CVCamera/jni/Android.mk new file mode 100644 index 000000000..f5aa1b12b --- /dev/null +++ b/android/apps/CVCamera/jni/Android.mk @@ -0,0 +1,21 @@ +# date: Summer, 2010 +# author: Ethan Rublee +# contact: ethan.rublee@gmail.com +# +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +#define OPENCV_INCLUDES and OPENCV_LIBS +include $(OPENCV_CONFIG) + +LOCAL_LDLIBS += $(OPENCV_LIBS) $(ANDROID_OPENCV_LIBS) -llog -lGLESv2 + +LOCAL_C_INCLUDES += $(OPENCV_INCLUDES) $(ANDROID_OPENCV_INCLUDES) + +LOCAL_MODULE := cvcamera + +LOCAL_SRC_FILES := Processor.cpp gen/cvcamera_swig.cpp + +include $(BUILD_SHARED_LIBRARY) + diff --git a/android/apps/CVCamera/jni/Application.mk b/android/apps/CVCamera/jni/Application.mk new file mode 100644 index 000000000..a2c96a8fe --- /dev/null +++ b/android/apps/CVCamera/jni/Application.mk @@ -0,0 +1,2 @@ +# The ARMv7 is significanly faster due to the use of the hardware FPU +APP_ABI := armeabi armeabi-v7a \ No newline at end of file diff --git a/android/apps/CVCamera/jni/Processor.cpp b/android/apps/CVCamera/jni/Processor.cpp new file mode 100644 index 000000000..3d5a6b15d --- /dev/null +++ b/android/apps/CVCamera/jni/Processor.cpp @@ -0,0 +1,300 @@ +/* + * Processor.cpp + * + * Created on: Jun 13, 2010 + * Author: ethan + */ + +#include "Processor.h" + + +#include + +using namespace cv; + +Processor::Processor() : + stard(20/*max_size*/, 8/*response_threshold*/, + 15/*line_threshold_projected*/, + 8/*line_threshold_binarized*/, 5/*suppress_nonmax_size*/), + fastd(20/*threshold*/, true/*nonmax_suppression*/), + surfd(100./*hessian_threshold*/, 1/*octaves*/, 2/*octave_layers*/) + +{ + +} + +Processor::~Processor() { + // TODO Auto-generated destructor stub +} + +void Processor::detectAndDrawFeatures(int input_idx, image_pool* pool,int feature_type) { + FeatureDetector* fd = 0; + + switch (feature_type) { + case DETECT_SURF: + fd = &surfd; + break; + case DETECT_FAST: + fd = &fastd; + break; + case DETECT_STAR: + fd = &stard; + break; + } + + Mat greyimage; + pool->getGrey(input_idx, greyimage); + //Mat* grayimage = pool->getYUV(input_idx); + + Mat* img = pool->getImage(input_idx); + + if (!img || greyimage.empty() || fd == 0) + return; //no image at input_idx! + + + keypoints.clear(); + + //if(grayimage->step1() > sizeof(uchar)) return; + //cvtColor(*img,*grayimage,CV_RGB2GRAY); + + + fd->detect(greyimage, keypoints); + + for (vector::const_iterator it = keypoints.begin(); it + != keypoints.end(); ++it) { + circle(*img, it->pt, 3, cvScalar(255, 0, 255, 0)); + } + + //pool->addImage(output_idx,outimage); + +} +static double computeReprojectionErrors( + const vector >& objectPoints, const vector >& imagePoints, const vector& rvecs, + const vector& tvecs, const Mat& cameraMatrix, + const Mat& distCoeffs, vector& perViewErrors) { + vector imagePoints2; + int i, totalPoints = 0; + double totalErr = 0, err; + perViewErrors.resize(objectPoints.size()); + + for (i = 0; i < (int) objectPoints.size(); i++) { + projectPoints(Mat(objectPoints[i]), rvecs[i], tvecs[i], cameraMatrix, + distCoeffs, imagePoints2); + err = norm(Mat(imagePoints[i]), Mat(imagePoints2), CV_L1 ); + int n = (int) objectPoints[i].size(); + perViewErrors[i] = err / n; + totalErr += err; + totalPoints += n; + } + + return totalErr / totalPoints; +} + +static void calcChessboardCorners(Size boardSize, float squareSize, vector< + Point3f>& corners) { + corners.resize(0); + + for (int i = 0; i < boardSize.height; i++) + for (int j = 0; j < boardSize.width; j++) + corners.push_back(Point3f(float(j * squareSize), float(i + * squareSize), 0)); +} + +/**from opencv/samples/cpp/calibration.cpp + * + */ +static bool runCalibration(vector > imagePoints, + Size imageSize, Size boardSize, float squareSize, float aspectRatio, + int flags, Mat& cameraMatrix, Mat& distCoeffs, vector& rvecs, + vector& tvecs, vector& reprojErrs, double& totalAvgErr) { + cameraMatrix = Mat::eye(3, 3, CV_64F); + if (flags & CV_CALIB_FIX_ASPECT_RATIO) + cameraMatrix.at (0, 0) = aspectRatio; + + distCoeffs = Mat::zeros(5, 1, CV_64F); + + vector > objectPoints(1); + calcChessboardCorners(boardSize, squareSize, objectPoints[0]); + for (size_t i = 1; i < imagePoints.size(); i++) + objectPoints.push_back(objectPoints[0]); + + calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix, + distCoeffs, rvecs, tvecs, flags); + + bool ok = checkRange(cameraMatrix, CV_CHECK_QUIET ) && checkRange( + distCoeffs, CV_CHECK_QUIET ); + + totalAvgErr = computeReprojectionErrors(objectPoints, imagePoints, rvecs, + tvecs, cameraMatrix, distCoeffs, reprojErrs); + + return ok; +} + +bool Processor::detectAndDrawChessboard(int idx,image_pool* pool) { + + Mat grey; + pool->getGrey(idx, grey); + if (grey.empty()) + return false; + vector corners; + + IplImage iplgrey = grey; + if (!cvCheckChessboard(&iplgrey, Size(6, 8))) + return false; + bool patternfound = findChessboardCorners(grey, Size(6, 8), corners); + + Mat * img = pool->getImage(idx); + + if (corners.size() < 1) + return false; + + cornerSubPix(grey, corners, Size(11, 11), Size(-1, -1), TermCriteria( + CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1)); + + if(patternfound) + imagepoints.push_back(corners); + + drawChessboardCorners(*img, Size(6, 8), Mat(corners), patternfound); + + imgsize = grey.size(); + + return patternfound; + +} + +void Processor::drawText(int i, image_pool* pool, const char* ctext){ + // Use "y" to show that the baseLine is about + string text = ctext; + int fontFace = FONT_HERSHEY_COMPLEX_SMALL; + double fontScale = .8; + int thickness = .5; + + Mat img = *pool->getImage(i); + + int baseline=0; + Size textSize = getTextSize(text, fontFace, + fontScale, thickness, &baseline); + baseline += thickness; + + // center the text + Point textOrg((img.cols - textSize.width)/2, + (img.rows - textSize.height *2)); + + // draw the box + rectangle(img, textOrg + Point(0, baseline), + textOrg + Point(textSize.width, -textSize.height), + Scalar(0,0,255),CV_FILLED); + // ... and the baseline first + line(img, textOrg + Point(0, thickness), + textOrg + Point(textSize.width, thickness), + Scalar(0, 0, 255)); + + // then put the text itself + putText(img, text, textOrg, fontFace, fontScale, + Scalar::all(255), thickness, 8); +} +void saveCameraParams(const string& filename, Size imageSize, Size boardSize, + float squareSize, float aspectRatio, int flags, + const Mat& cameraMatrix, const Mat& distCoeffs, + const vector& rvecs, const vector& tvecs, + const vector& reprojErrs, + const vector >& imagePoints, double totalAvgErr) { + FileStorage fs(filename, FileStorage::WRITE); + + time_t t; + time(&t); + struct tm *t2 = localtime(&t); + char buf[1024]; + strftime(buf, sizeof(buf) - 1, "%c", t2); + + fs << "calibration_time" << buf; + + if (!rvecs.empty() || !reprojErrs.empty()) + fs << "nframes" << (int) std::max(rvecs.size(), reprojErrs.size()); + fs << "image_width" << imageSize.width; + fs << "image_height" << imageSize.height; + fs << "board_width" << boardSize.width; + fs << "board_height" << boardSize.height; + fs << "squareSize" << squareSize; + + if (flags & CV_CALIB_FIX_ASPECT_RATIO) + fs << "aspectRatio" << aspectRatio; + + if (flags != 0) { + sprintf(buf, "flags: %s%s%s%s", + flags & CV_CALIB_USE_INTRINSIC_GUESS ? "+use_intrinsic_guess" + : "", + flags & CV_CALIB_FIX_ASPECT_RATIO ? "+fix_aspectRatio" : "", + flags & CV_CALIB_FIX_PRINCIPAL_POINT ? "+fix_principal_point" + : "", + flags & CV_CALIB_ZERO_TANGENT_DIST ? "+zero_tangent_dist" : ""); + cvWriteComment(*fs, buf, 0); + } + + fs << "flags" << flags; + + fs << "camera_matrix" << cameraMatrix; + fs << "distortion_coefficients" << distCoeffs; + + fs << "avg_reprojection_error" << totalAvgErr; + if (!reprojErrs.empty()) + fs << "per_view_reprojection_errors" << Mat(reprojErrs); + + if (!rvecs.empty() && !tvecs.empty()) { + Mat bigmat(rvecs.size(), 6, CV_32F); + for (size_t i = 0; i < rvecs.size(); i++) { + Mat r = bigmat(Range(i, i + 1), Range(0, 3)); + Mat t = bigmat(Range(i, i + 1), Range(3, 6)); + rvecs[i].copyTo(r); + tvecs[i].copyTo(t); + } + cvWriteComment( + *fs, + "a set of 6-tuples (rotation vector + translation vector) for each view", + 0); + fs << "extrinsic_parameters" << bigmat; + } + + if (!imagePoints.empty()) { + Mat imagePtMat(imagePoints.size(), imagePoints[0].size(), CV_32FC2); + for (size_t i = 0; i < imagePoints.size(); i++) { + Mat r = imagePtMat.row(i).reshape(2, imagePtMat.cols); + Mat(imagePoints[i]).copyTo(r); + } + fs << "image_points" << imagePtMat; + } +} +void Processor::resetChess() { + + imagepoints.clear(); +} + +void Processor::calibrate(const char* filename) { + + vector rvecs, tvecs; + vector reprojErrs; + double totalAvgErr = 0; + int flags = 0; + bool writeExtrinsics = true; + bool writePoints = true; + + bool ok = runCalibration(imagepoints, imgsize, Size(6, 8), 1.f, 1.f, + flags, K, distortion, rvecs, tvecs, reprojErrs, totalAvgErr); + + + if (ok){ + + saveCameraParams(filename, imgsize, Size(6, 8), 1.f, + 1.f, flags, K, distortion, writeExtrinsics ? rvecs + : vector (), writeExtrinsics ? tvecs + : vector (), writeExtrinsics ? reprojErrs + : vector (), writePoints ? imagepoints : vector< + vector > (), totalAvgErr); + } + +} + +int Processor::getNumberDetectedChessboards() { + return imagepoints.size(); +} diff --git a/android/apps/CVCamera/jni/Processor.h b/android/apps/CVCamera/jni/Processor.h new file mode 100644 index 000000000..49bee55a4 --- /dev/null +++ b/android/apps/CVCamera/jni/Processor.h @@ -0,0 +1,60 @@ +/* + * Processor.h + * + * Created on: Jun 13, 2010 + * Author: ethan + */ + +#ifndef PROCESSOR_H_ +#define PROCESSOR_H_ + +#include +#include +#include +#include +#include + + + +#include + +#include "image_pool.h" + +#define DETECT_FAST 0 +#define DETECT_STAR 1 +#define DETECT_SURF 2 + +class Processor { + + cv::StarFeatureDetector stard; + cv::FastFeatureDetector fastd; + cv::SurfFeatureDetector surfd; + std::vector keypoints; + + vector > imagepoints; + + cv::Mat K; + cv::Mat distortion; + cv::Size imgsize; + //image_pool pool; +public: + + Processor(); + virtual ~Processor(); + + void detectAndDrawFeatures(int idx, image_pool* pool, int feature_type); + + bool detectAndDrawChessboard(int idx,image_pool* pool); + + void resetChess(); + + int getNumberDetectedChessboards(); + + void calibrate(const char* filename); + + void drawText(int idx, image_pool* pool, const char* text); + + +}; + +#endif /* PROCESSOR_H_ */ diff --git a/android/apps/CVCamera/jni/Processor.i b/android/apps/CVCamera/jni/Processor.i new file mode 100644 index 000000000..ff66d5307 --- /dev/null +++ b/android/apps/CVCamera/jni/Processor.i @@ -0,0 +1,51 @@ +/* + * include the headers required by the generated cpp code + */ +%{ +#include "Processor.h" +#include "image_pool.h" +using namespace cv; +%} + + +/** + * some constants, see Processor.h + */ +#define DETECT_FAST 0 +#define DETECT_STAR 1 +#define DETECT_SURF 2 + +//import the android-cv.i file so that swig is aware of all that has been previous defined +//notice that it is not an include.... +%import "android-cv.i" + +//make sure to import the image_pool as it is +//referenced by the Processor java generated +//class +%typemap(javaimports) Processor " +import com.opencv.jni.image_pool;// import the image_pool interface for playing nice with + // android-opencv + +/** Processor - for processing images that are stored in an image pool +*/" + +class Processor { +public: + Processor(); + virtual ~Processor(); + + + + void detectAndDrawFeatures(int idx, image_pool* pool, int feature_type); + + bool detectAndDrawChessboard(int idx,image_pool* pool); + + void resetChess(); + + int getNumberDetectedChessboards(); + + void calibrate(const char* filename); + + void drawText(int idx, image_pool* pool, const char* text); + +}; diff --git a/android/apps/CVCamera/jni/cvcamera.i b/android/apps/CVCamera/jni/cvcamera.i new file mode 100644 index 000000000..ccca8d91a --- /dev/null +++ b/android/apps/CVCamera/jni/cvcamera.i @@ -0,0 +1,36 @@ +/* File : android-cv.i */ + +%module cvcamera + + +/* + * the java import code muse be included for the opencv jni wrappers + * this means that the android project must reference opencv/android as a project + * see the default.properties for how this is done + */ +%pragma(java) jniclassimports=%{ +import com.opencv.jni.*; //import the android-opencv jni wrappers +%} + +%pragma(java) jniclasscode=%{ + static { + try { + //load the cvcamera library, make sure that libcvcamera.so is in your /libs/armeabi directory + //so that android sdk automatically installs it along with the app. + + //the android-opencv lib must be loaded first inorder for the cvcamera + //lib to be found + //check the apk generated, by opening it in an archive manager, to verify that + //both these libraries are present + System.loadLibrary("android-opencv"); + System.loadLibrary("cvcamera"); + } catch (UnsatisfiedLinkError e) { + //badness + throw e; + } + } + +%} + +//include the Processor class swig interface file +%include "Processor.i" \ No newline at end of file diff --git a/android/apps/CVCamera/project_create.sh b/android/apps/CVCamera/project_create.sh new file mode 100755 index 000000000..ec7672872 --- /dev/null +++ b/android/apps/CVCamera/project_create.sh @@ -0,0 +1,2 @@ +android update project --name CVCamera \ +--path . diff --git a/android/apps/CVCamera/res/drawable-hdpi/icon.png b/android/apps/CVCamera/res/drawable-hdpi/icon.png new file mode 100644 index 000000000..4e828bafd Binary files /dev/null and b/android/apps/CVCamera/res/drawable-hdpi/icon.png differ diff --git a/android/apps/CVCamera/res/drawable-ldpi/icon.png b/android/apps/CVCamera/res/drawable-ldpi/icon.png new file mode 100644 index 000000000..5e11406cb Binary files /dev/null and b/android/apps/CVCamera/res/drawable-ldpi/icon.png differ diff --git a/android/apps/CVCamera/res/drawable-mdpi/icon.png b/android/apps/CVCamera/res/drawable-mdpi/icon.png new file mode 100644 index 000000000..a591eb892 Binary files /dev/null and b/android/apps/CVCamera/res/drawable-mdpi/icon.png differ diff --git a/android/apps/CVCamera/res/layout/main.xml b/android/apps/CVCamera/res/layout/main.xml new file mode 100644 index 000000000..ae6cdd14d --- /dev/null +++ b/android/apps/CVCamera/res/layout/main.xml @@ -0,0 +1,8 @@ + + + + diff --git a/android/apps/CVCamera/res/values/strings.xml b/android/apps/CVCamera/res/values/strings.xml new file mode 100644 index 000000000..5fe379d69 --- /dev/null +++ b/android/apps/CVCamera/res/values/strings.xml @@ -0,0 +1,7 @@ + + + + CVCamera +app to demo using android camera and passing data to opencv layer. +Release 0.0.1 - first demo of using the OpenCV library with camera data + diff --git a/android/apps/CVCamera/sample.local.env.mk b/android/apps/CVCamera/sample.local.env.mk new file mode 100644 index 000000000..203c7301e --- /dev/null +++ b/android/apps/CVCamera/sample.local.env.mk @@ -0,0 +1,4 @@ +#location of android-opencv port of OpenCV to android +OPENCV_CONFIG=../../build/android-opencv.mk +ANDROID_NDK_ROOT=$(HOME)/android-ndk-r4-crystax + diff --git a/android/apps/CVCamera/src/com/theveganrobot/cvcamera/CVCamera.java b/android/apps/CVCamera/src/com/theveganrobot/cvcamera/CVCamera.java new file mode 100644 index 000000000..2cbcbb31c --- /dev/null +++ b/android/apps/CVCamera/src/com/theveganrobot/cvcamera/CVCamera.java @@ -0,0 +1,498 @@ +package com.theveganrobot.cvcamera; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.LinkedList; +import java.util.Scanner; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.content.pm.ActivityInfo; +import android.os.Bundle; +import android.os.Environment; +import android.util.Log; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.view.ViewGroup.LayoutParams; +import android.widget.AnalogClock; +import android.widget.Button; +import android.widget.FrameLayout; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.Toast; + +import com.opencv.camera.NativePreviewer; +import com.opencv.camera.NativeProcessor; +import com.opencv.camera.NativeProcessor.PoolCallback; +import com.opencv.jni.image_pool; +import com.opencv.opengl.GL2CameraViewer; +import com.theveganrobot.cvcamera.jni.Processor; +import com.theveganrobot.cvcamera.jni.cvcamera; + +public class CVCamera extends Activity { + + static final int DIALOG_CALIBRATING = 0; + static final int DIALOG_CALIBRATION_FILE = 1; + private static final int DIALOG_OPENING_TUTORIAL = 2; + private static final int DIALOG_TUTORIAL_FAST = 3; + private static final int DIALOG_TUTORIAL_SURF = 4; + private static final int DIALOG_TUTORIAL_STAR = 5; + private static final int DIALOG_TUTORIAL_CHESS = 6; + private boolean captureChess; + + ProgressDialog makeCalibDialog() { + ProgressDialog progressDialog; + progressDialog = new ProgressDialog(this); + progressDialog.setMessage("Callibrating. Please wait..."); + progressDialog.setCancelable(false); + + return progressDialog; + } + + void toasts(int id) { + switch (id) { + case DIALOG_OPENING_TUTORIAL: + Toast.makeText(this, "Try clicking the menu for CV options.", + Toast.LENGTH_LONG).show(); + break; + case DIALOG_TUTORIAL_FAST: + Toast.makeText(this, "Detecting and Displaying FAST features", + Toast.LENGTH_LONG).show(); + break; + case DIALOG_TUTORIAL_SURF: + Toast.makeText(this, "Detecting and Displaying SURF features", + Toast.LENGTH_LONG).show(); + break; + case DIALOG_TUTORIAL_STAR: + Toast.makeText(this, "Detecting and Displaying STAR features", + Toast.LENGTH_LONG).show(); + break; + case DIALOG_TUTORIAL_CHESS: + Toast + .makeText( + this, + "Calibration Mode, Point at a chessboard pattern and press the camera button, space," + + "or the DPAD to capture.", + Toast.LENGTH_LONG).show(); + break; + + default: + break; + } + + } + + @Override + protected Dialog onCreateDialog(int id) { + Dialog dialog; + switch (id) { + case DIALOG_CALIBRATING: + dialog = makeCalibDialog(); + break; + + case DIALOG_CALIBRATION_FILE: + dialog = makeCalibFileAlert(); + break; + default: + dialog = null; + } + return dialog; + } + + private Dialog makeCalibFileAlert() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(calib_text).setTitle( + "camera.yml at " + calib_file_loc).setCancelable(false) + .setPositiveButton("Ok", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + + } + }); + AlertDialog alert = builder.create(); + return alert; + } + + /* + * (non-Javadoc) + * + * @see android.app.Activity#onKeyUp(int, android.view.KeyEvent) + */ + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + + switch (keyCode) { + case KeyEvent.KEYCODE_CAMERA: + case KeyEvent.KEYCODE_SPACE: + case KeyEvent.KEYCODE_DPAD_CENTER: + captureChess = true; + return true; + + default: + return super.onKeyUp(keyCode, event); + } + + } + + /* + * (non-Javadoc) + * + * @see android.app.Activity#onKeyLongPress(int, android.view.KeyEvent) + */ + @Override + public boolean onKeyLongPress(int keyCode, KeyEvent event) { + + return super.onKeyLongPress(keyCode, event); + } + + /** + * Avoid that the screen get's turned off by the system. + */ + public void disableScreenTurnOff() { + getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, + WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + + /** + * Set's the orientation to landscape, as this is needed by AndAR. + */ + public void setOrientation() { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } + + /** + * Maximize the application. + */ + public void setFullscreen() { + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + + public void setNoTitle() { + requestWindowFeature(Window.FEATURE_NO_TITLE); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add("FAST"); + menu.add("STAR"); + menu.add("SURF"); + menu.add("Chess"); + return true; + } + + private NativePreviewer mPreview; + private GL2CameraViewer glview; + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + LinkedList defaultcallbackstack = new LinkedList(); + defaultcallbackstack.addFirst(glview.getDrawCallback()); + if (item.getTitle().equals("FAST")) { + + defaultcallbackstack.addFirst(new FastProcessor()); + toasts(DIALOG_TUTORIAL_FAST); + } + + else if (item.getTitle().equals("Chess")) { + + defaultcallbackstack.addFirst(new CalibrationProcessor()); + toasts(DIALOG_TUTORIAL_CHESS); + + } + + else if (item.getTitle().equals("STAR")) { + + defaultcallbackstack.addFirst(new STARProcessor()); + toasts(DIALOG_TUTORIAL_STAR); + + } + + else if (item.getTitle().equals("SURF")) { + + defaultcallbackstack.addFirst(new SURFProcessor()); + toasts(DIALOG_TUTORIAL_SURF); + + } + + mPreview.addCallbackStack(defaultcallbackstack); + return true; + } + + @Override + public void onOptionsMenuClosed(Menu menu) { + // TODO Auto-generated method stub + super.onOptionsMenuClosed(menu); + } + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setFullscreen(); + disableScreenTurnOff(); + + FrameLayout frame = new FrameLayout(this); + + // Create our Preview view and set it as the content of our activity. + mPreview = new NativePreviewer(getApplication(), 640, 480); + + LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + params.height = getWindowManager().getDefaultDisplay().getHeight(); + params.width = (int) (params.height * 4.0 / 2.88); + + LinearLayout vidlay = new LinearLayout(getApplication()); + + vidlay.setGravity(Gravity.CENTER); + vidlay.addView(mPreview, params); + frame.addView(vidlay); + + // make the glview overlay ontop of video preview + mPreview.setZOrderMediaOverlay(false); + + glview = new GL2CameraViewer(getApplication(), false, 0, 0); + glview.setZOrderMediaOverlay(true); + glview.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, + LayoutParams.FILL_PARENT)); + + ImageButton capture_button = new ImageButton(getApplicationContext()); + capture_button.setImageDrawable(getResources().getDrawable(android.R.drawable.ic_menu_camera)); + capture_button.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT)); + capture_button.setOnClickListener(new View.OnClickListener() { + + + @Override + public void onClick(View v) { + captureChess = true; + + } + }); + + LinearLayout buttons = new LinearLayout(getApplicationContext()); + buttons.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT)); + + buttons.addView(capture_button); + + Button focus_button = new Button(getApplicationContext()); + focus_button.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT)); + focus_button.setText("Focus"); + focus_button.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + mPreview.postautofocus(100); + } + }); + buttons.addView(focus_button); + + frame.addView(glview); + + frame.addView(buttons); + setContentView(frame); + toasts(DIALOG_OPENING_TUTORIAL); + + } + + @Override + public boolean onTrackballEvent(MotionEvent event) { + if(event.getAction() == MotionEvent.ACTION_UP){ + captureChess = true; + return true; + } + return super.onTrackballEvent(event); + } + + @Override + protected void onPause() { + super.onPause(); + + + //clears the callback stack + mPreview.onPause(); + + glview.onPause(); + + } + + @Override + protected void onResume() { + super.onResume(); + glview.onResume(); + + //add an initiall callback stack to the preview on resume... + //this one will just draw the frames to opengl + LinkedList cbstack = new LinkedList(); + cbstack.add(glview.getDrawCallback()); + mPreview.addCallbackStack(cbstack); + mPreview.onResume(); + + } + + // final processor so taht these processor callbacks can access it + final Processor processor = new Processor(); + + class FastProcessor implements NativeProcessor.PoolCallback { + + @Override + public void process(int idx, image_pool pool, long timestamp, + NativeProcessor nativeProcessor) { + processor.detectAndDrawFeatures(idx, pool, cvcamera.DETECT_FAST); + + } + + } + + class STARProcessor implements NativeProcessor.PoolCallback { + + @Override + public void process(int idx, image_pool pool, long timestamp, + NativeProcessor nativeProcessor) { + processor.detectAndDrawFeatures(idx, pool, cvcamera.DETECT_STAR); + + + } + + } + + class SURFProcessor implements NativeProcessor.PoolCallback { + + @Override + public void process(int idx, image_pool pool, long timestamp, + NativeProcessor nativeProcessor) { + processor.detectAndDrawFeatures(idx, pool, cvcamera.DETECT_SURF); + + } + + } + + String calib_text = null; + String calib_file_loc = null; + + class CalibrationProcessor implements NativeProcessor.PoolCallback { + + boolean calibrated = false; + + @Override + public void process(int idx, image_pool pool, long timestamp, + NativeProcessor nativeProcessor) { + + if (calibrated) { + processor.drawText(idx, pool, "Calibrated successfully"); + return; + } + if (processor.getNumberDetectedChessboards() == 10) { + + File opencvdir = new File(Environment + .getExternalStorageDirectory(), "opencv"); + if (!opencvdir.exists()) { + opencvdir.mkdir(); + } + File calibfile = new File(opencvdir, "camera.yml"); + + calib_file_loc = calibfile.getAbsolutePath(); + processor.calibrate(calibfile.getAbsolutePath()); + Log.i("chessboard", "calibrated"); + calibrated = true; + processor.resetChess(); + runOnUiThread(new Runnable() { + + @Override + public void run() { + removeDialog(DIALOG_CALIBRATING); + + } + }); + + try { + + StringBuilder text = new StringBuilder(); + String NL = System.getProperty("line.separator"); + Scanner scanner = new Scanner(calibfile); + + try { + while (scanner.hasNextLine()) { + text.append(scanner.nextLine() + NL); + } + } finally { + scanner.close(); + } + + calib_text = text.toString(); + + runOnUiThread(new Runnable() { + + @Override + public void run() { + showDialog(DIALOG_CALIBRATION_FILE); + + } + }); + + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } else if (captureChess + && processor.detectAndDrawChessboard(idx, pool)) { + + runOnUiThread(new Runnable() { + + String numchess = String.valueOf(processor + .getNumberDetectedChessboards()); + + @Override + public void run() { + Toast.makeText(CVCamera.this, + "Detected " + numchess + " of 10 chessboards", + Toast.LENGTH_SHORT).show(); + + } + }); + Log.i("cvcamera", + "detected a chessboard, n chess boards found: " + + String.valueOf(processor + .getNumberDetectedChessboards())); + + } + + captureChess = false; + + if (processor.getNumberDetectedChessboards() == 10) { + runOnUiThread(new Runnable() { + + @Override + public void run() { + showDialog(DIALOG_CALIBRATING); + + } + }); + + processor.drawText(idx, pool, "Calibrating, please wait."); + } + if (processor.getNumberDetectedChessboards() < 10) { + + processor.drawText(idx, pool, "found " + + processor.getNumberDetectedChessboards() + + "/10 chessboards"); + } + + } + + } + +} diff --git a/android/apps/CVCamera/uninstall.phone.sh b/android/apps/CVCamera/uninstall.phone.sh new file mode 100755 index 000000000..06b78cabe --- /dev/null +++ b/android/apps/CVCamera/uninstall.phone.sh @@ -0,0 +1,3 @@ +#!/bin/bash +echo uninstalling CVCamera from phone +adb uninstall com.theveganrobot.cvcamera diff --git a/android/apps/Calibration/AndroidManifest.xml b/android/apps/Calibration/AndroidManifest.xml new file mode 100644 index 000000000..2933e8766 --- /dev/null +++ b/android/apps/Calibration/AndroidManifest.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/apps/Calibration/artwork/icon.xcf b/android/apps/Calibration/artwork/icon.xcf new file mode 100644 index 000000000..aa7bb992b Binary files /dev/null and b/android/apps/Calibration/artwork/icon.xcf differ diff --git a/android/apps/Calibration/default.properties b/android/apps/Calibration/default.properties new file mode 100644 index 000000000..66148fe91 --- /dev/null +++ b/android/apps/Calibration/default.properties @@ -0,0 +1,12 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +android.library.reference.1=../../android-jni +# Project target. +target=android-7 diff --git a/android/apps/Calibration/project_create.sh b/android/apps/Calibration/project_create.sh new file mode 100755 index 000000000..f8c9c51ff --- /dev/null +++ b/android/apps/Calibration/project_create.sh @@ -0,0 +1,2 @@ +android update project --name Calibration \ +--path . diff --git a/android/apps/Calibration/res/drawable-hdpi/icon.png b/android/apps/Calibration/res/drawable-hdpi/icon.png new file mode 100644 index 000000000..ab9f93288 Binary files /dev/null and b/android/apps/Calibration/res/drawable-hdpi/icon.png differ diff --git a/android/apps/Calibration/res/drawable-ldpi/icon.png b/android/apps/Calibration/res/drawable-ldpi/icon.png new file mode 100644 index 000000000..ab9f93288 Binary files /dev/null and b/android/apps/Calibration/res/drawable-ldpi/icon.png differ diff --git a/android/apps/Calibration/res/drawable-mdpi/cameraback.jpg b/android/apps/Calibration/res/drawable-mdpi/cameraback.jpg new file mode 100644 index 000000000..b53ffd489 Binary files /dev/null and b/android/apps/Calibration/res/drawable-mdpi/cameraback.jpg differ diff --git a/android/apps/Calibration/res/drawable-mdpi/icon.png b/android/apps/Calibration/res/drawable-mdpi/icon.png new file mode 100644 index 000000000..ab9f93288 Binary files /dev/null and b/android/apps/Calibration/res/drawable-mdpi/icon.png differ diff --git a/android/apps/Calibration/res/drawable-mdpi/patternicon.png b/android/apps/Calibration/res/drawable-mdpi/patternicon.png new file mode 100644 index 000000000..879044ad0 Binary files /dev/null and b/android/apps/Calibration/res/drawable-mdpi/patternicon.png differ diff --git a/android/apps/Calibration/res/layout/camera.xml b/android/apps/Calibration/res/layout/camera.xml new file mode 100644 index 000000000..8e721d8df --- /dev/null +++ b/android/apps/Calibration/res/layout/camera.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/apps/Calibration/res/layout/chesssizer.xml b/android/apps/Calibration/res/layout/chesssizer.xml new file mode 100644 index 000000000..b93bc0bde --- /dev/null +++ b/android/apps/Calibration/res/layout/chesssizer.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + diff --git a/android/apps/Calibration/res/menu/calibrationmenu.xml b/android/apps/Calibration/res/menu/calibrationmenu.xml new file mode 100644 index 000000000..8dd345778 --- /dev/null +++ b/android/apps/Calibration/res/menu/calibrationmenu.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/android/apps/Calibration/res/values/chessnumbers.xml b/android/apps/Calibration/res/values/chessnumbers.xml new file mode 100644 index 000000000..f0db5693e --- /dev/null +++ b/android/apps/Calibration/res/values/chessnumbers.xml @@ -0,0 +1,20 @@ + + + +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 + + +Choose the width: + +Choose the height: + diff --git a/android/apps/Calibration/res/values/color.xml b/android/apps/Calibration/res/values/color.xml new file mode 100644 index 000000000..e33983712 --- /dev/null +++ b/android/apps/Calibration/res/values/color.xml @@ -0,0 +1,5 @@ + + +#00ff00 +#FF0000 + diff --git a/android/apps/Calibration/res/values/strings.xml b/android/apps/Calibration/res/values/strings.xml new file mode 100644 index 000000000..a33cf0b13 --- /dev/null +++ b/android/apps/Calibration/res/values/strings.xml @@ -0,0 +1,24 @@ + + + Calibration + Pattern Size + Please choose the width and height (number of inside corners) of the checker + board pattern you will be using for calibration. Default is 6 by 8 corners. You may find a checkerboard pattern at + http://opencv.willowgarage.com/pattern + + http://opencv.willowgarage.com/pattern + + /opencv/camera.yml + /opencv + + Calibration calculations have started... + Calibration calculations has stopped. + Calibration finished, you camera is calibrated. + + Calibration + Please capture atleast 3 - preferably greater than 10 - images of the pattern! + + + Please make sure that you\'re sdcard is not mounted to you\'re computer, and that you have an sdcard that is writable on your device. + + diff --git a/android/apps/Calibration/src/com/opencv/calibration/Calibration.java b/android/apps/Calibration/src/com/opencv/calibration/Calibration.java new file mode 100644 index 000000000..5c7abf3b0 --- /dev/null +++ b/android/apps/Calibration/src/com/opencv/calibration/Calibration.java @@ -0,0 +1,311 @@ +package com.opencv.calibration; + +import java.io.File; +import java.io.IOException; +import java.util.LinkedList; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.ActivityInfo; +import android.os.Bundle; +import android.os.IBinder; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.opencv.calibration.Calibrator.CalibrationCallback; +import com.opencv.calibration.services.CalibrationService; +import com.opencv.camera.NativePreviewer; +import com.opencv.camera.NativeProcessor; +import com.opencv.misc.SDCardChecker; +import com.opencv.opengl.GL2CameraViewer; + +public class Calibration extends Activity implements CalibrationCallback { + private NativePreviewer mPreview; + + private GL2CameraViewer glview; + private Calibrator calibrator; + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + + switch (keyCode) { + case KeyEvent.KEYCODE_CAMERA: + case KeyEvent.KEYCODE_SPACE: + case KeyEvent.KEYCODE_DPAD_CENTER: + calibrator.queueChessCapture(); + return true; + default: + return super.onKeyUp(keyCode, event); + } + + } + + @Override + public boolean onTrackballEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_UP) { + calibrator.queueChessCapture(); + return true; + } + return super.onTrackballEvent(event); + } + + /** + * Avoid that the screen get's turned off by the system. + */ + public void disableScreenTurnOff() { + getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, + WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + + /** + * Set's the orientation to landscape, as this is needed by AndAR. + */ + public void setOrientation() { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } + + /** + * Maximize the application. + */ + public void setFullscreen() { + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + + public void setNoTitle() { + requestWindowFeature(Window.FEATURE_NO_TITLE); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.calibrationmenu, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.patternsize: { + Intent sizer = new Intent(getApplicationContext(), + ChessBoardChooser.class); + startActivity(sizer); + } + break; + case R.id.help: + help(); + break; + case R.id.calibrate: + calibrate(); + break; + } + + return true; + + } + + private void help() { + // TODO Auto-generated method stub + + } + + @Override + public void onOptionsMenuClosed(Menu menu) { + // TODO Auto-generated method stub + super.onOptionsMenuClosed(menu); + } + + private ServiceConnection mConnection = new ServiceConnection() { + + @Override + public void onServiceDisconnected(ComponentName name) { + + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + + CalibrationService calibservice = ((CalibrationService.CalibrationServiceBinder) service) + .getService(); + if (!SDCardChecker.CheckStorage(Calibration.this)) + return; + SDCardChecker.MakeDataDir(Calibration.this); + + File calibfile = SDCardChecker.getFile(calibservice, + R.string.calibfile); + try { + + Calibrator tcalib = calibrator; + calibrator = new Calibrator(Calibration.this); + setCallbackStack(); + calibservice.startCalibrating(tcalib, calibfile); + } catch (IOException e) { + e.printStackTrace(); + } + + // Tell the user about this for our demo. + Toast.makeText(Calibration.this, + "Starting calibration in the background.", + Toast.LENGTH_SHORT).show(); + unbindService(this); + } + + }; + + public static File getCalibrationFile(Context ctx) { + return SDCardChecker.getFile(ctx, R.string.calibfile); + } + + void doBindCalibService() { + // Establish a connection with the service. We use an explicit + // class name because we want a specific service implementation that + // we know will be running in our own process (and thus won't be + // supporting component replacement by other applications). + bindService(new Intent(Calibration.this, CalibrationService.class), + mConnection, Context.BIND_AUTO_CREATE); + } + + void calibrate() { + if (calibrator.getNumberPatternsDetected() < 3) { + Toast.makeText(this, getText(R.string.calibration_not_enough), + Toast.LENGTH_LONG).show(); + return; + } + Intent calibservice = new Intent(Calibration.this, + CalibrationService.class); + startService(calibservice); + doBindCalibService(); + + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setFullscreen(); + disableScreenTurnOff(); + setContentView(R.layout.camera); + mPreview = (NativePreviewer) findViewById(R.id.nativepreviewer); + LinearLayout glview_layout = (LinearLayout) findViewById(R.id.glview_layout); + glview = new GL2CameraViewer(getApplication(), false, 0, 0); + glview_layout.addView(glview); + calibrator = new Calibrator(this); + + ImageButton capturebutton = (ImageButton) findViewById(R.id.capture); + capturebutton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + calibrator.queueChessCapture(); + + } + }); + ImageButton calibbutton = (ImageButton) findViewById(R.id.calibrate); + calibbutton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + calibrate(); + } + }); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + } + + @Override + protected void onPause() { + super.onPause(); + + mPreview.onPause(); + + glview.onPause(); + + } + + protected void setCallbackStack() { + calibrator.setPatternSize(ChessBoardChooser.getPatternSize(this)); + + LinkedList callbackstack = new LinkedList(); + callbackstack.add(calibrator); + callbackstack.add(glview.getDrawCallback()); + mPreview.addCallbackStack(callbackstack); + updateNumber(calibrator); + } + + @Override + protected void onResume() { + super.onResume(); + glview.onResume(); + mPreview.onResume(); + setCallbackStack(); + + } + + void updateNumber(Calibrator calibrator) { + TextView numbertext = (TextView) findViewById(R.id.numberpatterns); + int numdetectd = calibrator.getNumberPatternsDetected(); + if (numdetectd > 2) { + numbertext + .setTextColor(getResources().getColor(R.color.good_color)); + + } else + numbertext.setTextColor(getResources().getColor(R.color.bad_color)); + + numbertext.setText(String.valueOf(numdetectd)); + + } + + @Override + public void onFoundChessboard(final Calibrator calibrator) { + runOnUiThread(new Runnable() { + + @Override + public void run() { + Toast.makeText(Calibration.this, + "Captured a calibration pattern!", Toast.LENGTH_SHORT) + .show(); + updateNumber(calibrator); + + } + }); + + } + + @Override + public void onDoneCalibration(Calibrator calibration, File calibfile) { + + } + + @Override + public void onFailedChessboard(final Calibrator calibrator) { + runOnUiThread(new Runnable() { + + @Override + public void run() { + Toast.makeText( + Calibration.this, + "No pattern found. Make sure its the right dimensions, and close enough...", + Toast.LENGTH_LONG).show(); + updateNumber(calibrator); + + } + }); + + } + +} \ No newline at end of file diff --git a/android/apps/Calibration/src/com/opencv/calibration/ChessBoardChooser.java b/android/apps/Calibration/src/com/opencv/calibration/ChessBoardChooser.java new file mode 100644 index 000000000..10f936009 --- /dev/null +++ b/android/apps/Calibration/src/com/opencv/calibration/ChessBoardChooser.java @@ -0,0 +1,74 @@ +package com.opencv.calibration; + +import com.opencv.jni.Size; + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.os.Bundle; +import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.Spinner; + +public class ChessBoardChooser extends Activity { + public static final String CHESS_SIZE = "chess_size"; + public static final int DEFAULT_WIDTH = 6; + public static final int DEFAULT_HEIGHT = 8; + public static final int LOWEST = 3; + + class DimChooser implements OnItemSelectedListener { + private String dim; + + public DimChooser(String dim) { + this.dim = dim; + } + + @Override + public void onItemSelected(AdapterView arg0, View arg1, int pos, + long arg3) { + SharedPreferences settings = getSharedPreferences(CHESS_SIZE, 0); + Editor editor = settings.edit(); + editor.putInt(dim, pos + LOWEST); + editor.commit(); + } + + @Override + public void onNothingSelected(AdapterView arg0) { + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + // TODO Auto-generated method stub + super.onCreate(savedInstanceState); + setContentView(R.layout.chesssizer); + // Restore preferences + SharedPreferences settings = getSharedPreferences(CHESS_SIZE, 0); + int width = settings.getInt("width", 6); + + int height = settings.getInt("height", 8); + + Spinner wspin, hspin; + wspin = (Spinner) findViewById(R.id.rows); + hspin = (Spinner) findViewById(R.id.cols); + + wspin.setSelection(width - LOWEST); + hspin.setSelection(height - LOWEST); + + wspin.setOnItemSelectedListener(new DimChooser("width")); + hspin.setOnItemSelectedListener(new DimChooser("height")); + + } + + public static Size getPatternSize(Context ctx) { + SharedPreferences settings = ctx.getSharedPreferences(CHESS_SIZE, 0); + int width = settings.getInt("width", 6); + + int height = settings.getInt("height", 8); + + return new Size(width, height); + } + +} diff --git a/android/apps/Calibration/src/com/opencv/calibration/services/CalibrationService.java b/android/apps/Calibration/src/com/opencv/calibration/services/CalibrationService.java new file mode 100644 index 000000000..bc9d5cd4c --- /dev/null +++ b/android/apps/Calibration/src/com/opencv/calibration/services/CalibrationService.java @@ -0,0 +1,151 @@ +package com.opencv.calibration.services; + +import java.io.File; +import java.io.IOException; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.util.Log; +import android.widget.Toast; + +import com.opencv.calibration.Calibration; +import com.opencv.calibration.Calibrator; +import com.opencv.calibration.Calibrator.CalibrationCallback; +import com.opencv.calibration.R; + +public class CalibrationService extends Service implements CalibrationCallback { + + public void startCalibrating(Calibrator calibrator, File calibration_file) + throws IOException { + calibrator.setCallback(this); + calibrator.calibrate(calibration_file); + } + + private NotificationManager mNM; + + /** + * Class for clients to access. Because we know this service always runs in + * the same process as its clients, we don't need to deal with IPC. + */ + public class CalibrationServiceBinder extends Binder { + public CalibrationService getService() { + return CalibrationService.this; + } + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.i("LocalService", "Received start id " + startId + ": " + intent); + // We want this service to continue running until it is explicitly + // stopped, so return sticky. + return START_NOT_STICKY; + } + + @Override + public void onCreate() { + mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + + // Display a notification about us starting. We put an icon in the + // status bar. + showNotification(); + } + + @Override + public void onDestroy() { + // Cancel the persistent notification. + // mNM.cancel(R.string.calibration_service_started); + + // Tell the user we stopped. + Toast.makeText(this, R.string.calibration_service_finished, + Toast.LENGTH_SHORT).show(); + } + + private final IBinder mBinder = new CalibrationServiceBinder(); + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + /** + * Show a notification while this service is running. + */ + private void showNotification() { + // In this sample, we'll use the same text for the ticker and the + // expanded notification + CharSequence text = getText(R.string.calibration_service_started); + + // Set the icon, scrolling text and timestamp + Notification notification = new Notification(R.drawable.icon, text, + System.currentTimeMillis()); + + // The PendingIntent to launch our activity if the user selects this + // notification + PendingIntent contentIntent = PendingIntent.getActivity(this, 0, + new Intent(this, Calibration.class), 0); + + // Set the info for the views that show in the notification panel. + notification.setLatestEventInfo(this, + getText(R.string.calibration_service_label), text, + contentIntent); + + notification.defaults |= Notification.DEFAULT_SOUND; + // Send the notification. + // We use a layout id because it is a unique number. We use it later to + // cancel. + mNM.notify(R.string.calibration_service_started, notification); + } + + /** + * Show a notification while this service is running. + */ + private void doneNotification() { + // In this sample, we'll use the same text for the ticker and the + // expanded notification + CharSequence text = getText(R.string.calibration_service_finished); + + // Set the icon, scrolling text and timestamp + Notification notification = new Notification(R.drawable.icon, text, + System.currentTimeMillis()); + + // The PendingIntent to launch our activity if the user selects this + // notification + PendingIntent contentIntent = PendingIntent.getActivity(this, 0, + new Intent(this, Calibration.class), 0); + + // Set the info for the views that show in the notification panel. + notification.setLatestEventInfo(this, + getText(R.string.calibration_service_label), text, + contentIntent); + + notification.defaults |= Notification.DEFAULT_SOUND; + // Send the notification. + // We use a layout id because it is a unique number. We use it later to + // cancel. + mNM.notify(R.string.calibration_service_started, notification); + } + + @Override + public void onFoundChessboard(Calibrator calibrator) { + // TODO Auto-generated method stub + + } + + @Override + public void onDoneCalibration(Calibrator calibration, File calibfile) { + doneNotification(); + stopSelf(); + } + + @Override + public void onFailedChessboard(Calibrator calibrator) { + // TODO Auto-generated method stub + + } + +} diff --git a/android/apps/Calibration/src/com/opencv/misc/SDCardChecker.java b/android/apps/Calibration/src/com/opencv/misc/SDCardChecker.java new file mode 100644 index 000000000..1a7d2b7e2 --- /dev/null +++ b/android/apps/Calibration/src/com/opencv/misc/SDCardChecker.java @@ -0,0 +1,64 @@ +package com.opencv.misc; + +import java.io.File; + +import android.content.Context; +import android.os.Environment; +import android.widget.Toast; + +import com.opencv.calibration.R; + +public class SDCardChecker { + + public static File createThumb(Context ctx, File workingDir) { + return new File(workingDir, "thumb.jpg"); + } + + public static File getDir(Context ctx, String relativename) { + return new File(Environment.getExternalStorageDirectory() + + relativename); + } + + public static File getDir(Context ctx, int id) { + return new File(Environment.getExternalStorageDirectory() + + ctx.getResources().getString(id)); + } + + public static File getFile(Context ctx, int id) { + return new File(Environment.getExternalStorageDirectory() + + ctx.getResources().getString(id)); + } + + public static void MakeDataDir(Context ctx) { + File dir = getDir(ctx, R.string.sdcarddir); + dir.mkdirs(); + } + + public static boolean CheckStorage(Context ctx) { + boolean mExternalStorageAvailable = false; + boolean mExternalStorageWriteable = false; + String state = Environment.getExternalStorageState(); + + if (Environment.MEDIA_MOUNTED.equals(state)) { + // We can read and write the media + mExternalStorageAvailable = mExternalStorageWriteable = true; + } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { + // We can only read the media + mExternalStorageAvailable = true; + mExternalStorageWriteable = false; + } else { + // Something else is wrong. It may be one of many other states, but + // all we need + // to know is we can neither read nor write + mExternalStorageAvailable = mExternalStorageWriteable = false; + } + boolean goodmount = mExternalStorageAvailable + && mExternalStorageWriteable; + if (!goodmount) { + Toast.makeText(ctx, ctx.getString(R.string.sdcard_error_msg), + Toast.LENGTH_LONG).show(); + } + return goodmount; + } + +} diff --git a/android/apps/camera_template/AndroidManifest.xml b/android/apps/camera_template/AndroidManifest.xml new file mode 100644 index 000000000..6af45ffec --- /dev/null +++ b/android/apps/camera_template/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/apps/camera_template/Makefile b/android/apps/camera_template/Makefile new file mode 100644 index 000000000..3d1e8eec6 --- /dev/null +++ b/android/apps/camera_template/Makefile @@ -0,0 +1,98 @@ +#Re-usable Makefile template for +#android-ndk + swig projects +#author: Ethan Rublee +#date: summer 2010 + +# The path to the NDK, requires crystax version r-4 for now, due to support +#for the standard library +ifndef ANDROID_NDK_BASE +ANDROID_NDK_BASE = $(HOME)/android-ndk-r4-crystax +$(info default ndk location set ANDROID_NDK_BASE = $(ANDROID_NDK_BASE)) +endif + +#define OPENCV_ROOT when calling this makefile +#OPENCV_ROOT = $(ANDROID_NDK_BASE)/apps/opencv +ifndef OPENCV_ROOT +$(error Please define OPENCV_ROOT with something like the command \ +make OPENCV_ROOT=) +endif + +ifndef PROJECT_PATH +$(info PROJECT_PATH defaulting to this directory) +PROJECT_PATH=. +endif + +$(info OPENCV_ROOT = $(OPENCV_ROOT)) + +# The name of the native library +LIBNAME = libfoobar.so + +#define the main jni swig interface file +#the desired java package name +#and the desired java directory +#notice that the package and the java dir +#are related... +SWIG_MAIN = jni/foobar.i +SWIG_JAVA_PACKAGE = com.foo.bar.jni +SWIG_JAVA_DIR = src/com/foo/bar/jni + +SWIG_BASE = foobar + +#swig definitions - auto as long as all the +#swig interface files are in jni/*.i +SWIG_JAVA_OUT = $(wildcard $(SWIG_JAVA_DIR)/*.java) +SWIG_IS = $(wildcard jni/*.i) + +#the directory where the jni sources are +C_DIR = jni + +#directory where to put generated files +#relative to the C_DIR +GEN_DIR = gen + +#the c swig is generated and put into the jni/gen folder +SWIG_C_DIR = $(C_DIR)/$(GEN_DIR) + +#this file - jin/gen/foobar_swig.cpp must be included in the Android.mk +#for it to be built! +SWIG_C_OUT = $(SWIG_C_DIR)/$(SWIG_BASE)_swig.cpp + +# The real native library stripped of symbols +LIB = libs/armeabi-v7a/$(LIBNAME) libs/armeabi/$(LIBNAME) + +# Find all the C++ sources in the native folder +SOURCES = $(wildcard jni/*.cpp) +HEADERS = $(wildcard jni/*.h) + +ANDROID_MKS = $(wildcard jni/*.mk) + +#this gets called by the make command +all: $(LIB) + +#calls the ndk-build script, passing it OPENCV_ROOT and OPENCV_LIBS_DIR +$(LIB): $(SWIG_C_OUT) $(SOURCES) $(HEADERS) $(ANDROID_MKS) + $(ANDROID_NDK_BASE)/ndk-build OPENCV_ROOT=$(OPENCV_ROOT) \ + OPENCV_LIBS_DIR=$(OPENCV_LIBS_DIR) PROJECT_PATH=$(PROJECT_PATH) SWIG_C_OUT=$(GEN_DIR)/$(SWIG_BASE)_swig.cpp V=$(V) $(NDK_FLAGS) + + +#this creates the swig wrappers +#-I$(OPENCV_ROOT)/android/jni is an additional swig include path +$(SWIG_C_OUT): $(SWIG_IS) + make clean-swig &&\ + mkdir -p $(SWIG_C_DIR) &&\ + mkdir -p $(SWIG_JAVA_DIR) &&\ + swig -java -c++ -I$(OPENCV_ROOT)/android/jni -package "$(SWIG_JAVA_PACKAGE)" \ + -outdir $(SWIG_JAVA_DIR) \ + -o $(SWIG_C_OUT) $(SWIG_MAIN) + + +#clean targets +.PHONY: clean clean-swig cleanall + +#this deletes the generated swig java and the generated c wrapper +clean-swig: + rm -f $(SWIG_JAVA_OUT) $(SWIG_C_OUT) + +#does clean-swig and then uses the ndk-build clean +clean: clean-swig + $(ANDROID_NDK_BASE)/ndk-build clean V=$(V) $(NDK_FLAGS) diff --git a/android/apps/camera_template/README.txt b/android/apps/camera_template/README.txt new file mode 100644 index 000000000..14d57b56f --- /dev/null +++ b/android/apps/camera_template/README.txt @@ -0,0 +1,2 @@ +see http://code.google.com/p/android-opencv/wiki/camera_template + diff --git a/android/apps/camera_template/build.sh b/android/apps/camera_template/build.sh new file mode 100644 index 000000000..92772c3a7 --- /dev/null +++ b/android/apps/camera_template/build.sh @@ -0,0 +1 @@ +make OPENCV_ROOT=../../opencv V=0 diff --git a/android/apps/camera_template/clean.sh b/android/apps/camera_template/clean.sh new file mode 100644 index 000000000..121e391e4 --- /dev/null +++ b/android/apps/camera_template/clean.sh @@ -0,0 +1 @@ +make OPENCV_ROOT=../../opencv V=0 clean diff --git a/android/apps/camera_template/default.properties b/android/apps/camera_template/default.properties new file mode 100644 index 000000000..887872424 --- /dev/null +++ b/android/apps/camera_template/default.properties @@ -0,0 +1,12 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "build.properties", and override values to adapt the script to your +# project structure. + +android.library.reference.1=../../opencv/android/ +# Project target. +target=android-7 diff --git a/android/apps/camera_template/jni/Android.mk b/android/apps/camera_template/jni/Android.mk new file mode 100644 index 000000000..b66bb7032 --- /dev/null +++ b/android/apps/camera_template/jni/Android.mk @@ -0,0 +1,36 @@ +# date: Summer, 2010 +# author: Ethan Rublee +# contact: ethan.rublee@gmail.com +# +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +#pass in OPENCV_ROOT or define it here +#OPENCV_ROOT := ~/android-opencv/opencv +ifndef OPENCV_ROOT +${error please define OPENCV_ROOT before this point!} +endif + +#define OPENCV_INCLUDES +include $(OPENCV_ROOT)/includes.mk +#define OPENCV_LIBS +include $(OPENCV_ROOT)/libs.mk + +LOCAL_LDLIBS += $(OPENCV_LIBS) $(ANDROID_OPENCV_LIBS) -llog -lGLESv2 + +LOCAL_C_INCLUDES += $(OPENCV_INCLUDES) $(ANDROID_OPENCV_INCLUDES) + +LOCAL_MODULE := foobar + + +ifndef SWIG_C_OUT +${error please define SWIG_C_OUT before this point!} +endif + +#make sure to pass in SWIG_C_OUT=gen/foobar_swig.cpp +#done in the makefile +LOCAL_SRC_FILES := ${SWIG_C_OUT} TestBar.cpp + +include $(BUILD_SHARED_LIBRARY) + diff --git a/android/apps/camera_template/jni/Application.mk b/android/apps/camera_template/jni/Application.mk new file mode 100644 index 000000000..a2c96a8fe --- /dev/null +++ b/android/apps/camera_template/jni/Application.mk @@ -0,0 +1,2 @@ +# The ARMv7 is significanly faster due to the use of the hardware FPU +APP_ABI := armeabi armeabi-v7a \ No newline at end of file diff --git a/android/apps/camera_template/jni/TestBar.cpp b/android/apps/camera_template/jni/TestBar.cpp new file mode 100644 index 000000000..869113a31 --- /dev/null +++ b/android/apps/camera_template/jni/TestBar.cpp @@ -0,0 +1,5 @@ +#include "TestBar.h" + +void BarBar::crazy(){ + +} diff --git a/android/apps/camera_template/jni/TestBar.h b/android/apps/camera_template/jni/TestBar.h new file mode 100644 index 000000000..e919f8f32 --- /dev/null +++ b/android/apps/camera_template/jni/TestBar.h @@ -0,0 +1,26 @@ +/* + * TestBar.h + * + * Created on: Jul 17, 2010 + * Author: ethan + */ + +#ifndef TESTBAR_H_ +#define TESTBAR_H_ + +#include "image_pool.h" + +struct FooBarStruct { + + int pool_image_count(image_pool* pool){ + return pool->getCount(); + } + +}; + +class BarBar{ +public: + void crazy(); +}; + +#endif /* TESTBAR_H_ */ diff --git a/android/apps/camera_template/jni/foobar.i b/android/apps/camera_template/jni/foobar.i new file mode 100644 index 000000000..a66a05932 --- /dev/null +++ b/android/apps/camera_template/jni/foobar.i @@ -0,0 +1,68 @@ +/* File : foobar.i */ +%module foobar + +/* + * the java import code muse be included for the opencv jni wrappers + * this means that the android project must reference opencv/android as a project + * see the default.properties for how this is done + */ +%pragma(java) jniclassimports=%{ +import com.opencv.jni.*; //import the android-opencv jni wrappers +%} + +%pragma(java) jniclasscode=%{ + static { + try { + //load the cvcamera library, make sure that libcvcamera.so is in your /libs/armeabi directory + //so that android sdk automatically installs it along with the app. + + //the android-opencv lib must be loaded first inorder for the cvcamera + //lib to be found + //check the apk generated, by opening it in an archive manager, to verify that + //both these libraries are present + System.loadLibrary("android-opencv"); + System.loadLibrary("foobar"); + } catch (UnsatisfiedLinkError e) { + //badness + throw e; + } + } + +%} + +%{ +#include "image_pool.h" +#include "TestBar.h" +using namespace cv; +%} + + +//import the android-cv.i file so that swig is aware of all that has been previous defined +//notice that it is not an include.... +%import "android-cv.i" + + +//make sure to import the image_pool as it is +//referenced by the Processor java generated +//class +%typemap(javaimports) FooBarStruct " +import com.opencv.jni.image_pool;// import the image_pool interface for playing nice with + // android-opencv + +/** FooBar - template +*/" + +//sample inline class that needs to include image_pool +struct FooBarStruct { + + int pool_image_count(image_pool* pool){ + return pool->getCount(); + } + +}; + +//sample jni class +class BarBar{ +public: + void crazy(); +}; diff --git a/android/apps/camera_template/res/drawable-hdpi/icon.png b/android/apps/camera_template/res/drawable-hdpi/icon.png new file mode 100644 index 000000000..4e828bafd Binary files /dev/null and b/android/apps/camera_template/res/drawable-hdpi/icon.png differ diff --git a/android/apps/camera_template/res/drawable-ldpi/icon.png b/android/apps/camera_template/res/drawable-ldpi/icon.png new file mode 100644 index 000000000..5e11406cb Binary files /dev/null and b/android/apps/camera_template/res/drawable-ldpi/icon.png differ diff --git a/android/apps/camera_template/res/drawable-mdpi/icon.png b/android/apps/camera_template/res/drawable-mdpi/icon.png new file mode 100644 index 000000000..a591eb892 Binary files /dev/null and b/android/apps/camera_template/res/drawable-mdpi/icon.png differ diff --git a/android/apps/camera_template/res/values/strings.xml b/android/apps/camera_template/res/values/strings.xml new file mode 100644 index 000000000..5ff41dc25 --- /dev/null +++ b/android/apps/camera_template/res/values/strings.xml @@ -0,0 +1,8 @@ + + +FooBar +FooBar is a template app for the android-opencv project!\nVisit http://code.google.com/p/android-opencv for details. +About FooBar +About +O.K. + diff --git a/android/apps/camera_template/src/com/foo/bar/FooBar.java b/android/apps/camera_template/src/com/foo/bar/FooBar.java new file mode 100644 index 000000000..6bc31be5b --- /dev/null +++ b/android/apps/camera_template/src/com/foo/bar/FooBar.java @@ -0,0 +1,224 @@ +package com.foo.bar; + +import java.util.LinkedList; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.util.Log; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.Window; +import android.view.WindowManager; +import android.view.ViewGroup.LayoutParams; +import android.widget.FrameLayout; +import android.widget.LinearLayout; + +//make sure you have the OpenCV project open, so that the +//android-sdk can find it! +import com.foo.bar.jni.BarBar; +import com.foo.bar.jni.FooBarStruct; +import com.opencv.camera.NativePreviewer; +import com.opencv.camera.NativeProcessor; +import com.opencv.camera.NativeProcessor.PoolCallback; +import com.opencv.jni.image_pool; +import com.opencv.opengl.GL2CameraViewer; + +public class FooBar extends Activity { + + private final int FOOBARABOUT = 0; + + @Override + protected Dialog onCreateDialog(int id) { + Dialog dialog; + switch (id) { + case FOOBARABOUT: + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.about_title); + builder.setMessage(R.string.about_str); + builder.setPositiveButton(R.string.ok, + new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + dismissDialog(FOOBARABOUT); + + } + }); + dialog = builder.create(); + default: + dialog = null; + } + return dialog; + } + + /* + * Handle the capture button as follows... + */ + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + + switch (keyCode) { + case KeyEvent.KEYCODE_CAMERA: + case KeyEvent.KEYCODE_SPACE: + case KeyEvent.KEYCODE_DPAD_CENTER: + // capture button pressed here + return true; + + default: + return super.onKeyUp(keyCode, event); + } + + } + + /* + * Handle the capture button as follows... On some phones there is no + * capture button, only trackball + */ + @Override + public boolean onTrackballEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_UP) { + // capture button pressed + return true; + } + return super.onTrackballEvent(event); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(R.string.about_menu); + return true; + } + + private NativePreviewer mPreview; + private GL2CameraViewer glview; + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // example menu + if (item.getTitle().equals( + getResources().getString(R.string.about_menu))) { + showDialog(FOOBARABOUT); + } + return true; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + + FrameLayout frame = new FrameLayout(this); + + // Create our Preview view and set it as the content of our activity. + mPreview = new NativePreviewer(getApplication(), 300, 300); + + LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + params.height = getWindowManager().getDefaultDisplay().getHeight(); + params.width = (int) (params.height * 4.0 / 2.88); + + LinearLayout vidlay = new LinearLayout(getApplication()); + + vidlay.setGravity(Gravity.CENTER); + vidlay.addView(mPreview, params); + frame.addView(vidlay); + + // make the glview overlay ontop of video preview + mPreview.setZOrderMediaOverlay(false); + + glview = new GL2CameraViewer(getApplication(), false, 0, 0); + glview.setZOrderMediaOverlay(true); + glview.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, + LayoutParams.FILL_PARENT)); + frame.addView(glview); + + setContentView(frame); + + } + + @Override + protected void onPause() { + super.onPause(); + + // IMPORTANT + // must tell the NativePreviewer of a pause + // and the glview - so that they can release resources and start back up + // properly + // failing to do this will cause the application to crash with no + // warning + // on restart + // clears the callback stack + mPreview.onPause(); + + glview.onPause(); + + } + + @Override + protected void onResume() { + super.onResume(); + + // resume the opengl viewer first + glview.onResume(); + + // add an initial callback stack to the preview on resume... + // this one will just draw the frames to opengl + LinkedList cbstack = new LinkedList(); + + // SpamProcessor will be called first + cbstack.add(new SpamProcessor()); + + // then the same idx and pool will be passed to + // the glview callback - + // so operate on the image at idx, and modify, and then + // it will be drawn in the glview + // or remove this, and call glview manually in SpamProcessor + // cbstack.add(glview.getDrawCallback()); + + mPreview.addCallbackStack(cbstack); + mPreview.onResume(); + + } + + class SpamProcessor implements NativeProcessor.PoolCallback { + + FooBarStruct foo = new FooBarStruct(); + BarBar barbar = new BarBar(); + + @Override + public void process(int idx, image_pool pool, long timestamp, + NativeProcessor nativeProcessor) { + + // example of using the jni generated FoobarStruct; + int nImages = foo.pool_image_count(pool); + Log.i("foobar", "Number of images in pool: " + nImages); + + // call a function - this function does absolutely nothing! + barbar.crazy(); + + // sample processor + // this gets called every frame in the order of the list + // first add to the callback stack linked list will be the + // first called + // the idx and pool may be used to get the cv::Mat + // that is the latest frame being passed. + // pool.getClass(idx) + + // these are what the glview.getDrawCallback() calls + glview.drawMatToGL(idx, pool); + glview.requestRender(); + + } + + } + +}