@ -0,0 +1,30 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||
package="com.theveganrobot.cvcamera" android:versionCode="7" android:versionName="7.0" |
||||
> |
||||
<application android:debuggable="false" android:icon="@drawable/icon"> |
||||
<activity android:name=".CVCamera" android:screenOrientation="landscape" |
||||
android:configChanges="orientation|keyboardHidden|keyboard" |
||||
android:label="@string/app_name" |
||||
> |
||||
<intent-filter> |
||||
<action android:name="android.intent.action.MAIN" /> |
||||
<category android:name="android.intent.category.LAUNCHER" /> |
||||
</intent-filter> |
||||
</activity> |
||||
|
||||
</application> |
||||
|
||||
|
||||
|
||||
<uses-feature android:glEsVersion="0x00020000" android:required="true"/> |
||||
<uses-feature android:name="android.hardware.camera" android:required="true"/> |
||||
|
||||
<uses-permission android:name="android.permission.CAMERA"></uses-permission> |
||||
|
||||
|
||||
|
||||
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="7" /> |
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> |
||||
</manifest> |
@ -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)
|
||||
|
@ -0,0 +1,2 @@ |
||||
see http://code.google.com/p/android-opencv/wiki/CVCamera |
||||
|
@ -0,0 +1 @@ |
||||
make V=0 |
@ -0,0 +1 @@ |
||||
make OPENCV_ROOT=../../opencv V=0 clean |
@ -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 |
@ -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) |
||||
|
@ -0,0 +1,2 @@ |
||||
# The ARMv7 is significanly faster due to the use of the hardware FPU
|
||||
APP_ABI := armeabi armeabi-v7a
|
@ -0,0 +1,300 @@ |
||||
/*
|
||||
* Processor.cpp |
||||
* |
||||
* Created on: Jun 13, 2010 |
||||
* Author: ethan |
||||
*/ |
||||
|
||||
#include "Processor.h" |
||||
|
||||
|
||||
#include <sys/stat.h> |
||||
|
||||
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<KeyPoint>::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<vector<Point3f> >& objectPoints, const vector<vector< |
||||
Point2f> >& imagePoints, const vector<Mat>& rvecs, |
||||
const vector<Mat>& tvecs, const Mat& cameraMatrix, |
||||
const Mat& distCoeffs, vector<float>& perViewErrors) { |
||||
vector<Point2f> 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<vector<Point2f> > imagePoints, |
||||
Size imageSize, Size boardSize, float squareSize, float aspectRatio, |
||||
int flags, Mat& cameraMatrix, Mat& distCoeffs, vector<Mat>& rvecs, |
||||
vector<Mat>& tvecs, vector<float>& reprojErrs, double& totalAvgErr) { |
||||
cameraMatrix = Mat::eye(3, 3, CV_64F); |
||||
if (flags & CV_CALIB_FIX_ASPECT_RATIO) |
||||
cameraMatrix.at<double> (0, 0) = aspectRatio; |
||||
|
||||
distCoeffs = Mat::zeros(5, 1, CV_64F); |
||||
|
||||
vector<vector<Point3f> > 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<Point2f> 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<Mat>& rvecs, const vector<Mat>& tvecs, |
||||
const vector<float>& reprojErrs, |
||||
const vector<vector<Point2f> >& 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<Mat> rvecs, tvecs; |
||||
vector<float> 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<Mat> (), writeExtrinsics ? tvecs |
||||
: vector<Mat> (), writeExtrinsics ? reprojErrs |
||||
: vector<float> (), writePoints ? imagepoints : vector< |
||||
vector<Point2f> > (), totalAvgErr); |
||||
} |
||||
|
||||
} |
||||
|
||||
int Processor::getNumberDetectedChessboards() { |
||||
return imagepoints.size(); |
||||
} |
@ -0,0 +1,60 @@ |
||||
/*
|
||||
* Processor.h |
||||
* |
||||
* Created on: Jun 13, 2010 |
||||
* Author: ethan |
||||
*/ |
||||
|
||||
#ifndef PROCESSOR_H_ |
||||
#define PROCESSOR_H_ |
||||
|
||||
#include <opencv2/core/core.hpp> |
||||
#include <opencv2/features2d/features2d.hpp> |
||||
#include <opencv2/highgui/highgui.hpp> |
||||
#include <opencv2/imgproc/imgproc.hpp> |
||||
#include <opencv2/calib3d/calib3d.hpp> |
||||
|
||||
|
||||
|
||||
#include <vector> |
||||
|
||||
#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<cv::KeyPoint> keypoints; |
||||
|
||||
vector<vector<Point2f> > 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_ */ |
@ -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); |
||||
|
||||
}; |
@ -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 <project>/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" |
@ -0,0 +1,2 @@ |
||||
android update project --name CVCamera \ |
||||
--path . |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.8 KiB |
@ -0,0 +1,8 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:orientation="vertical" |
||||
android:layout_width="fill_parent" |
||||
android:layout_height="fill_parent" |
||||
> |
||||
|
||||
</LinearLayout> |
@ -0,0 +1,7 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<resources> |
||||
|
||||
<string name="app_name">CVCamera</string> |
||||
<string name="app_description">app to demo using android camera and passing data to opencv layer.</string> |
||||
<string name="Changes">Release 0.0.1 - first demo of using the OpenCV library with camera data</string> |
||||
</resources> |
@ -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
|
||||
|
@ -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<PoolCallback> defaultcallbackstack = new LinkedList<PoolCallback>(); |
||||
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<NativeProcessor.PoolCallback> cbstack = new LinkedList<PoolCallback>(); |
||||
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"); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,3 @@ |
||||
#!/bin/bash |
||||
echo uninstalling CVCamera from phone |
||||
adb uninstall com.theveganrobot.cvcamera |
@ -0,0 +1,38 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||
package="com.opencv.calibration" android:versionCode="1" |
||||
android:versionName="1.0"> |
||||
|
||||
<application android:debuggable="true" android:icon="@drawable/icon" |
||||
android:label="@string/app_name"> |
||||
|
||||
<activity android:name=".Calibration" android:label="@string/app_name" |
||||
android:screenOrientation="landscape" |
||||
android:configChanges="orientation|keyboardHidden|keyboard"> |
||||
<intent-filter> |
||||
<!-- <action android:name="android.intent.action.MAIN" /> --> |
||||
<action android:name="android.intent.action.MAIN" /> |
||||
<category android:name="android.intent.category.LAUNCHER" /> |
||||
</intent-filter> |
||||
|
||||
</activity> |
||||
|
||||
<activity android:name=".ChessBoardChooser" android:label="@string/app_name" |
||||
android:screenOrientation="landscape" |
||||
android:configChanges="orientation|keyboardHidden|keyboard"> |
||||
|
||||
</activity> |
||||
|
||||
<service android:name=".services.CalibrationService"></service> |
||||
|
||||
</application> |
||||
|
||||
<uses-sdk android:minSdkVersion="7" /> |
||||
|
||||
<uses-feature android:glEsVersion="0x00020000" /> |
||||
|
||||
<uses-permission android:name="android.permission.CAMERA"></uses-permission> |
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> |
||||
|
||||
|
||||
</manifest> |
@ -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 |
@ -0,0 +1,2 @@ |
||||
android update project --name Calibration \ |
||||
--path . |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 161 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 409 B |
@ -0,0 +1,59 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<RelativeLayout |
||||
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:layout_width="fill_parent" |
||||
android:layout_height="fill_parent" |
||||
android:background="@drawable/cameraback"> |
||||
<!--<com.opencv.camera.NativePreviewer--> |
||||
<!-- <SurfaceView --> |
||||
|
||||
<com.opencv.camera.NativePreviewer |
||||
|
||||
android:id="@+id/nativepreviewer" |
||||
|
||||
android:layout_width="400dip" |
||||
android:layout_height="300dip" |
||||
android:layout_alignParentLeft="true" |
||||
android:layout_margin="20dip" |
||||
android:gravity="center_horizontal|center_vertical" |
||||
android:layout_marginRight="20dip" |
||||
/> |
||||
<LinearLayout |
||||
android:id="@+id/glview_layout" |
||||
|
||||
android:layout_width="400dip" |
||||
android:layout_height="300dip" |
||||
android:layout_alignParentLeft="true" |
||||
android:layout_margin="20dip" |
||||
android:gravity="center_horizontal|center_vertical" |
||||
android:layout_marginRight="20dip" |
||||
> |
||||
</LinearLayout> |
||||
<LinearLayout android:layout_width="wrap_content" |
||||
android:layout_height="fill_parent" |
||||
android:orientation="vertical" |
||||
android:layout_margin="20dip" |
||||
android:gravity="center_horizontal|center_vertical" |
||||
android:layout_alignParentRight="true"> |
||||
|
||||
|
||||
<ImageButton android:src="@android:drawable/ic_menu_camera" |
||||
android:id="@+id/capture" android:layout_width="60dip" |
||||
android:layout_height="60dip"></ImageButton> |
||||
<ImageButton android:src="@android:drawable/ic_menu_save" |
||||
android:id="@+id/calibrate" android:layout_width="60dip" |
||||
android:layout_height="60dip"></ImageButton> |
||||
<TextView android:id="@+id/numberpatterns" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:padding="10dip" |
||||
android:background="@android:color/white" |
||||
android:text="0"/> |
||||
|
||||
|
||||
</LinearLayout> |
||||
|
||||
|
||||
|
||||
|
||||
</RelativeLayout> |
@ -0,0 +1,40 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout |
||||
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:layout_width="fill_parent" |
||||
android:layout_height="fill_parent" |
||||
android:orientation="vertical" |
||||
android:gravity="center_vertical|center_horizontal"> |
||||
<TextView android:text="@string/patterntext" android:autoLink="web" android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" android:padding="20dip"/> |
||||
<LinearLayout android:id="@+id/LinearLayout01" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:gravity="center_vertical"> |
||||
<TextView android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:text="Corners in width direction:"/> |
||||
<Spinner android:id="@+id/rows" |
||||
android:layout_width="fill_parent" |
||||
android:layout_height="wrap_content" |
||||
android:saveEnabled="true" |
||||
android:prompt="@string/chesspromptx" |
||||
android:entries="@array/chesssizes"> |
||||
</Spinner> |
||||
</LinearLayout> |
||||
|
||||
<LinearLayout android:id="@+id/LinearLayout01" |
||||
android:layout_width="wrap_content" android:layout_height="wrap_content" |
||||
android:gravity="center_vertical"> |
||||
<TextView android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" android:text="Corners in height direction:"/> |
||||
<Spinner android:id="@+id/cols" |
||||
android:layout_width="fill_parent" |
||||
android:layout_height="wrap_content" |
||||
android:saveEnabled="true" |
||||
android:prompt="@string/chessprompty" |
||||
android:entries="@array/chesssizes"> |
||||
</Spinner> |
||||
</LinearLayout> |
||||
|
||||
</LinearLayout> |
@ -0,0 +1,17 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<menu |
||||
xmlns:android="http://schemas.android.com/apk/res/android"> |
||||
<item android:id="@+id/patternsize" android:titleCondensed="Size" |
||||
android:title="@string/patternsize" |
||||
android:icon="@drawable/patternicon" |
||||
></item> |
||||
<item android:id="@+id/calibrate" |
||||
android:titleCondensed="Calib" |
||||
android:title="Calibrate" |
||||
></item> |
||||
<item android:id="@+id/help" |
||||
android:titleCondensed="Help" |
||||
android:title="Help" |
||||
android:icon="@android:drawable/ic_menu_help" |
||||
></item> |
||||
</menu> |
@ -0,0 +1,20 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<resources> |
||||
<string-array name="chesssizes"> |
||||
<item>3</item> |
||||
<item>4</item> |
||||
<item>5</item> |
||||
<item>6</item> |
||||
<item>7</item> |
||||
<item>8</item> |
||||
<item>9</item> |
||||
<item>10</item> |
||||
<item>11</item> |
||||
<item>12</item> |
||||
<item>13</item> |
||||
</string-array> |
||||
<string name="chesspromptx"> |
||||
Choose the width:</string> |
||||
<string name="chessprompty"> |
||||
Choose the height:</string> |
||||
</resources> |
@ -0,0 +1,5 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<resources> |
||||
<color name="good_color">#00ff00</color> |
||||
<color name="bad_color">#FF0000</color> |
||||
</resources> |
@ -0,0 +1,24 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<resources> |
||||
<string name="app_name">Calibration</string> |
||||
<string name="patternsize">Pattern Size</string> |
||||
<string name="patterntext">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</string> |
||||
|
||||
<string name="patternlink">http://opencv.willowgarage.com/pattern</string> |
||||
|
||||
<string name="calibfile">/opencv/camera.yml</string> |
||||
<string name="sdcarddir">/opencv</string> |
||||
|
||||
<string name="calibration_service_started">Calibration calculations have started...</string> |
||||
<string name="calibration_service_stopped">Calibration calculations has stopped.</string> |
||||
<string name="calibration_service_finished">Calibration finished, you camera is calibrated.</string> |
||||
|
||||
<string name="calibration_service_label">Calibration</string> |
||||
<string name="calibration_not_enough">Please capture atleast 3 - preferably greater than 10 - images of the pattern!</string> |
||||
|
||||
|
||||
<string name="sdcard_error_msg"> 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.</string> |
||||
|
||||
</resources> |
@ -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<NativeProcessor.PoolCallback> callbackstack = new LinkedList<NativeProcessor.PoolCallback>(); |
||||
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); |
||||
|
||||
} |
||||
}); |
||||
|
||||
} |
||||
|
||||
} |
@ -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); |
||||
} |
||||
|
||||
} |
@ -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
|
||||
|
||||
} |
||||
|
||||
} |
@ -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; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,27 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||
package="com.foo.bar" android:versionCode="1" android:versionName="1.0" > |
||||
|
||||
<application android:debuggable="true" android:icon="@drawable/icon"> |
||||
<activity android:name=".FooBar" android:screenOrientation="landscape" |
||||
android:configChanges="orientation|keyboardHidden|keyboard" |
||||
android:label="@string/app_name" |
||||
> |
||||
<intent-filter> |
||||
<action android:name="android.intent.action.MAIN" /> |
||||
<category android:name="android.intent.category.LAUNCHER" /> |
||||
</intent-filter> |
||||
</activity> |
||||
|
||||
</application> |
||||
|
||||
|
||||
|
||||
<uses-feature android:glEsVersion="0x00020000" /> |
||||
|
||||
<uses-permission android:name="android.permission.CAMERA"></uses-permission> |
||||
|
||||
<uses-sdk android:minSdkVersion="7" /> |
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> |
||||
</manifest> |
@ -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=<opencv>)
|
||||
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)
|
@ -0,0 +1,2 @@ |
||||
see http://code.google.com/p/android-opencv/wiki/camera_template |
||||
|
@ -0,0 +1 @@ |
||||
make OPENCV_ROOT=../../opencv V=0 |
@ -0,0 +1 @@ |
||||
make OPENCV_ROOT=../../opencv V=0 clean |
@ -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 |
@ -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) |
||||
|
@ -0,0 +1,2 @@ |
||||
# The ARMv7 is significanly faster due to the use of the hardware FPU
|
||||
APP_ABI := armeabi armeabi-v7a
|
@ -0,0 +1,5 @@ |
||||
#include "TestBar.h" |
||||
|
||||
void BarBar::crazy(){ |
||||
|
||||
} |
@ -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_ */ |
@ -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 <project>/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(); |
||||
}; |
After Width: | Height: | Size: 5.6 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.8 KiB |
@ -0,0 +1,8 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<resources> |
||||
<string name="app_name">FooBar</string> |
||||
<string name="about_str">FooBar is a template app for the android-opencv project!\nVisit http://code.google.com/p/android-opencv for details.</string> |
||||
<string name="about_title">About FooBar</string> |
||||
<string name="about_menu">About</string> |
||||
<string name="ok">O.K.</string> |
||||
</resources> |
@ -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<NativeProcessor.PoolCallback> cbstack = new LinkedList<PoolCallback>(); |
||||
|
||||
// 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(); |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
} |