Refactoring the image_pool for android, and adding some common utils for camera configuration. Also experimenting with optimization - grayscale preview is way faster than color right now.
parent
077dd77757
commit
3a932b0f6c
25 changed files with 1655 additions and 972 deletions
@ -1,2 +1,2 @@ |
||||
APP_ABI := armeabi armeabi-v7a
|
||||
APP_ABI := $(ARM_TARGETS)
|
||||
APP_MODULES := android-opencv
|
||||
|
@ -0,0 +1,11 @@ |
||||
<?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:scrollbars="vertical" android:id="@+id/calibtext" android:text="" android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" android:padding="20dip"/> |
||||
|
||||
</LinearLayout> |
@ -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/settings_text" 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="@string/image_size_prompt"/> |
||||
<Spinner android:id="@+id/image_size" |
||||
android:layout_width="fill_parent" |
||||
android:layout_height="wrap_content" |
||||
android:saveEnabled="true" |
||||
android:prompt="@string/image_size_prompt" |
||||
android:entries="@array/image_sizes"> |
||||
</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="@string/camera_mode_prompt"/> |
||||
<Spinner android:id="@+id/camera_mode" |
||||
android:layout_width="fill_parent" |
||||
android:layout_height="wrap_content" |
||||
android:saveEnabled="true" |
||||
android:prompt="@string/camera_mode_prompt" |
||||
android:entries="@array/camera_mode"> |
||||
</Spinner> |
||||
</LinearLayout> |
||||
|
||||
</LinearLayout> |
@ -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,11 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<resources> |
||||
|
||||
<declare-styleable name="CameraParams"> |
||||
|
||||
<attr name="preview_width" format="integer"/> |
||||
<attr name="preview_height" format="integer"/> |
||||
|
||||
</declare-styleable> |
||||
|
||||
</resources> |
@ -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,20 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<resources> |
||||
<string-array name="image_sizes"> |
||||
<item>320x240</item> |
||||
<item>400x300</item> |
||||
<item>600x400</item> |
||||
<item>800x600</item> |
||||
<item>1000x800</item> |
||||
</string-array> |
||||
<string-array name="camera_mode"> |
||||
<item>color</item> |
||||
<item>BW</item> |
||||
</string-array> |
||||
<string name="image_size_prompt"> |
||||
Image Size:\n(may not be exact) |
||||
</string> |
||||
<string name="camera_mode_prompt"> |
||||
Camera Mode: |
||||
</string> |
||||
</resources> |
@ -0,0 +1,19 @@ |
||||
<?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="camera_settings_label">Camera Settings</string> |
||||
<string name="settings_text">Change the camera settings</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 10 images of the pattern!</string> |
||||
|
||||
</resources> |
@ -0,0 +1,47 @@ |
||||
package com.opencv.calibration; |
||||
|
||||
import java.io.BufferedReader; |
||||
import java.io.FileNotFoundException; |
||||
import java.io.FileReader; |
||||
import java.io.IOException; |
||||
|
||||
import android.app.Activity; |
||||
import android.os.Bundle; |
||||
import android.text.method.ScrollingMovementMethod; |
||||
import android.util.Log; |
||||
import android.widget.TextView; |
||||
|
||||
import com.opencv.R; |
||||
|
||||
public class CalibrationViewer extends Activity { |
||||
|
||||
@Override |
||||
protected void onCreate(Bundle savedInstanceState) { |
||||
// TODO Auto-generated method stub
|
||||
super.onCreate(savedInstanceState); |
||||
setContentView(R.layout.calibrationviewer); |
||||
|
||||
Bundle extras = getIntent().getExtras(); |
||||
String filename = extras.getString("calibfile"); |
||||
if (filename != null) { |
||||
TextView text = (TextView) findViewById(R.id.calibtext); |
||||
text.setMovementMethod(new ScrollingMovementMethod()); |
||||
try { |
||||
BufferedReader reader = new BufferedReader(new FileReader( |
||||
filename)); |
||||
while (reader.ready()) { |
||||
text.append(reader.readLine() +"\n"); |
||||
} |
||||
|
||||
} catch (FileNotFoundException e) { |
||||
Log.e("opencv", "could not open calibration file at:" |
||||
+ filename); |
||||
} catch (IOException e) { |
||||
Log.e("opencv", "error reading file: " |
||||
+ filename); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,75 @@ |
||||
package com.opencv.calibration; |
||||
|
||||
import com.opencv.R; |
||||
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,166 @@ |
||||
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.R; |
||||
import com.opencv.calibration.CalibrationViewer; |
||||
import com.opencv.calibration.Calibrator; |
||||
import com.opencv.calibration.Calibrator.CalibrationCallback; |
||||
|
||||
|
||||
public class CalibrationService extends Service implements CalibrationCallback { |
||||
|
||||
Class<?> activity; |
||||
int icon; |
||||
File calibration_file; |
||||
public void startCalibrating(Class<?> activitycaller,int icon_id, Calibrator calibrator, File calibration_file) |
||||
throws IOException { |
||||
activity = activitycaller; |
||||
icon = icon_id; |
||||
// Display a notification about us starting. We put an icon in the
|
||||
// status bar.
|
||||
showNotification(); |
||||
this.calibration_file = calibration_file; |
||||
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); |
||||
|
||||
|
||||
} |
||||
|
||||
@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(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, activity), 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(icon, text, |
||||
System.currentTimeMillis()); |
||||
|
||||
Intent intent = new Intent(this,CalibrationViewer.class); |
||||
intent.putExtra("calibfile", calibration_file.getAbsolutePath()); |
||||
// The PendingIntent to launch our activity if the user selects this
|
||||
// notification
|
||||
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, |
||||
intent, 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,166 @@ |
||||
package com.opencv.camera; |
||||
|
||||
import com.opencv.R; |
||||
|
||||
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 CameraConfig extends Activity { |
||||
public static final String CAMERA_SETTINGS = "CAMERA_SETTINGS"; |
||||
public static final String CAMERA_MODE = "camera_mode"; |
||||
public static final String IMAGE_WIDTH = "IMAGE_WIDTH"; |
||||
public static final String IMAGE_HEIGHT = "IMAGE_HEIGHT"; |
||||
public static final int CAMERA_MODE_BW = 0; |
||||
public static final int CAMERA_MODE_COLOR = 1; |
||||
|
||||
public static int readCameraMode(Context ctx) { |
||||
// Restore preferences
|
||||
SharedPreferences settings = ctx.getSharedPreferences(CAMERA_SETTINGS, |
||||
0); |
||||
int mode = settings.getInt(CAMERA_MODE, CAMERA_MODE_BW); |
||||
return mode; |
||||
} |
||||
|
||||
static public void setCameraMode(Context context, String mode) { |
||||
int m = 0; |
||||
if (mode.equals("BW")) { |
||||
m = CAMERA_MODE_BW; |
||||
} else if (mode.equals("color")) |
||||
m = CAMERA_MODE_COLOR; |
||||
setCameraMode(context, m); |
||||
} |
||||
|
||||
private static String sizeToString(int[] size) { |
||||
return size[0] + "x" + size[1]; |
||||
} |
||||
|
||||
private static void parseStrToSize(String ssize, int[] size) { |
||||
String sz[] = ssize.split("x"); |
||||
size[0] = Integer.valueOf(sz[0]); |
||||
size[1] = Integer.valueOf(sz[1]); |
||||
} |
||||
|
||||
public static void readImageSize(Context ctx, int[] size) { |
||||
// Restore preferences
|
||||
SharedPreferences settings = ctx.getSharedPreferences(CAMERA_SETTINGS, |
||||
0); |
||||
size[0] = settings.getInt(IMAGE_WIDTH, 600); |
||||
size[1] = settings.getInt(IMAGE_HEIGHT, 600); |
||||
|
||||
} |
||||
|
||||
public static void setCameraMode(Context ctx, int mode) { |
||||
// Restore preferences
|
||||
SharedPreferences settings = ctx.getSharedPreferences(CAMERA_SETTINGS, |
||||
0); |
||||
Editor editor = settings.edit(); |
||||
editor.putInt(CAMERA_MODE, mode); |
||||
editor.commit(); |
||||
} |
||||
|
||||
public static void setImageSize(Context ctx, String strsize) { |
||||
int size[] = { 0, 0 }; |
||||
parseStrToSize(strsize, size); |
||||
setImageSize(ctx, size[0], size[1]); |
||||
} |
||||
|
||||
public static void setImageSize(Context ctx, int width, int height) { |
||||
// Restore preferences
|
||||
SharedPreferences settings = ctx.getSharedPreferences(CAMERA_SETTINGS, |
||||
0); |
||||
Editor editor = settings.edit(); |
||||
editor.putInt(IMAGE_WIDTH, width); |
||||
editor.putInt(IMAGE_HEIGHT, height); |
||||
editor.commit(); |
||||
} |
||||
|
||||
@Override |
||||
protected void onCreate(Bundle savedInstanceState) { |
||||
// TODO Auto-generated method stub
|
||||
super.onCreate(savedInstanceState); |
||||
setContentView(R.layout.camerasettings); |
||||
int mode = readCameraMode(this); |
||||
int size[] = { 0, 0 }; |
||||
readImageSize(this, size); |
||||
|
||||
final Spinner size_spinner; |
||||
final Spinner mode_spinner; |
||||
size_spinner = (Spinner) findViewById(R.id.image_size); |
||||
mode_spinner = (Spinner) findViewById(R.id.camera_mode); |
||||
|
||||
String strsize = sizeToString(size); |
||||
String strmode = modeToString(mode); |
||||
|
||||
String sizes[] = getResources().getStringArray(R.array.image_sizes); |
||||
|
||||
int i = 1; |
||||
for (String x : sizes) { |
||||
if (x.equals(strsize)) |
||||
break; |
||||
i++; |
||||
} |
||||
if(i <= sizes.length) |
||||
size_spinner.setSelection(i-1); |
||||
|
||||
i = 1; |
||||
String modes[] = getResources().getStringArray(R.array.camera_mode); |
||||
for (String x :modes) { |
||||
if (x.equals(strmode)) |
||||
break; |
||||
i++; |
||||
} |
||||
if(i <= modes.length) |
||||
mode_spinner.setSelection(i-1); |
||||
|
||||
size_spinner.setOnItemSelectedListener(new OnItemSelectedListener() { |
||||
|
||||
@Override |
||||
public void onItemSelected(AdapterView<?> arg0, View spinner, |
||||
int position, long arg3) { |
||||
Object o = size_spinner.getItemAtPosition(position); |
||||
if (o != null) |
||||
setImageSize(spinner.getContext(), (String) o); |
||||
} |
||||
|
||||
@Override |
||||
public void onNothingSelected(AdapterView<?> arg0) { |
||||
|
||||
} |
||||
}); |
||||
mode_spinner.setOnItemSelectedListener(new OnItemSelectedListener() { |
||||
|
||||
@Override |
||||
public void onItemSelected(AdapterView<?> arg0, View spinner, |
||||
int position, long arg3) { |
||||
Object o = mode_spinner.getItemAtPosition(position); |
||||
if (o != null) |
||||
setCameraMode(spinner.getContext(), (String) o); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public void onNothingSelected(AdapterView<?> arg0) { |
||||
|
||||
} |
||||
}); |
||||
|
||||
} |
||||
|
||||
private String modeToString(int mode) { |
||||
switch (mode) { |
||||
case CAMERA_MODE_BW: |
||||
return "BW"; |
||||
case CAMERA_MODE_COLOR: |
||||
return "color"; |
||||
default: |
||||
return ""; |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue