package com.theveganrobot.cvcamera; import java.io.File; import java.io.FileNotFoundException; import java.util.LinkedList; import java.util.Scanner; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.pm.ActivityInfo; import android.os.Bundle; import android.os.Environment; import android.util.Log; import android.view.Gravity; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.view.ViewGroup.LayoutParams; import android.widget.AnalogClock; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.Toast; import com.opencv.camera.NativePreviewer; import com.opencv.camera.NativeProcessor; import com.opencv.camera.NativeProcessor.PoolCallback; import com.opencv.jni.image_pool; import com.opencv.opengl.GL2CameraViewer; import com.theveganrobot.cvcamera.jni.Processor; import com.theveganrobot.cvcamera.jni.cvcamera; public class CVCamera extends Activity { static final int DIALOG_CALIBRATING = 0; static final int DIALOG_CALIBRATION_FILE = 1; private static final int DIALOG_OPENING_TUTORIAL = 2; private static final int DIALOG_TUTORIAL_FAST = 3; private static final int DIALOG_TUTORIAL_SURF = 4; private static final int DIALOG_TUTORIAL_STAR = 5; private static final int DIALOG_TUTORIAL_CHESS = 6; private boolean captureChess; ProgressDialog makeCalibDialog() { ProgressDialog progressDialog; progressDialog = new ProgressDialog(this); progressDialog.setMessage("Callibrating. Please wait..."); progressDialog.setCancelable(false); return progressDialog; } void toasts(int id) { switch (id) { case DIALOG_OPENING_TUTORIAL: Toast.makeText(this, "Try clicking the menu for CV options.", Toast.LENGTH_LONG).show(); break; case DIALOG_TUTORIAL_FAST: Toast.makeText(this, "Detecting and Displaying FAST features", Toast.LENGTH_LONG).show(); break; case DIALOG_TUTORIAL_SURF: Toast.makeText(this, "Detecting and Displaying SURF features", Toast.LENGTH_LONG).show(); break; case DIALOG_TUTORIAL_STAR: Toast.makeText(this, "Detecting and Displaying STAR features", Toast.LENGTH_LONG).show(); break; case DIALOG_TUTORIAL_CHESS: Toast .makeText( this, "Calibration Mode, Point at a chessboard pattern and press the camera button, space," + "or the DPAD to capture.", Toast.LENGTH_LONG).show(); break; default: break; } } @Override protected Dialog onCreateDialog(int id) { Dialog dialog; switch (id) { case DIALOG_CALIBRATING: dialog = makeCalibDialog(); break; case DIALOG_CALIBRATION_FILE: dialog = makeCalibFileAlert(); break; default: dialog = null; } return dialog; } private Dialog makeCalibFileAlert() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(calib_text).setTitle( "camera.yml at " + calib_file_loc).setCancelable(false) .setPositiveButton("Ok", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { } }); AlertDialog alert = builder.create(); return alert; } /* * (non-Javadoc) * * @see android.app.Activity#onKeyUp(int, android.view.KeyEvent) */ @Override public boolean onKeyUp(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_CAMERA: case KeyEvent.KEYCODE_SPACE: case KeyEvent.KEYCODE_DPAD_CENTER: captureChess = true; return true; default: return super.onKeyUp(keyCode, event); } } /* * (non-Javadoc) * * @see android.app.Activity#onKeyLongPress(int, android.view.KeyEvent) */ @Override public boolean onKeyLongPress(int keyCode, KeyEvent event) { return super.onKeyLongPress(keyCode, event); } /** * Avoid that the screen get's turned off by the system. */ public void disableScreenTurnOff() { getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } /** * Set's the orientation to landscape, as this is needed by AndAR. */ public void setOrientation() { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } /** * Maximize the application. */ public void setFullscreen() { requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); } public void setNoTitle() { requestWindowFeature(Window.FEATURE_NO_TITLE); } @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add("FAST"); menu.add("STAR"); menu.add("SURF"); menu.add("Chess"); return true; } private NativePreviewer mPreview; private GL2CameraViewer glview; @Override public boolean onOptionsItemSelected(MenuItem item) { LinkedList defaultcallbackstack = new LinkedList(); defaultcallbackstack.addFirst(glview.getDrawCallback()); if (item.getTitle().equals("FAST")) { defaultcallbackstack.addFirst(new FastProcessor()); toasts(DIALOG_TUTORIAL_FAST); } else if (item.getTitle().equals("Chess")) { defaultcallbackstack.addFirst(new CalibrationProcessor()); toasts(DIALOG_TUTORIAL_CHESS); } else if (item.getTitle().equals("STAR")) { defaultcallbackstack.addFirst(new STARProcessor()); toasts(DIALOG_TUTORIAL_STAR); } else if (item.getTitle().equals("SURF")) { defaultcallbackstack.addFirst(new SURFProcessor()); toasts(DIALOG_TUTORIAL_SURF); } mPreview.addCallbackStack(defaultcallbackstack); return true; } @Override public void onOptionsMenuClosed(Menu menu) { // TODO Auto-generated method stub super.onOptionsMenuClosed(menu); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setFullscreen(); disableScreenTurnOff(); FrameLayout frame = new FrameLayout(this); // Create our Preview view and set it as the content of our activity. mPreview = new NativePreviewer(getApplication(), 640, 480); LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); params.height = getWindowManager().getDefaultDisplay().getHeight(); params.width = (int) (params.height * 4.0 / 2.88); LinearLayout vidlay = new LinearLayout(getApplication()); vidlay.setGravity(Gravity.CENTER); vidlay.addView(mPreview, params); frame.addView(vidlay); // make the glview overlay ontop of video preview mPreview.setZOrderMediaOverlay(false); glview = new GL2CameraViewer(getApplication(), false, 0, 0); glview.setZOrderMediaOverlay(true); glview.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); ImageButton capture_button = new ImageButton(getApplicationContext()); capture_button.setImageDrawable(getResources().getDrawable(android.R.drawable.ic_menu_camera)); capture_button.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); capture_button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { captureChess = true; } }); LinearLayout buttons = new LinearLayout(getApplicationContext()); buttons.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); buttons.addView(capture_button); Button focus_button = new Button(getApplicationContext()); focus_button.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); focus_button.setText("Focus"); focus_button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mPreview.postautofocus(100); } }); buttons.addView(focus_button); frame.addView(glview); frame.addView(buttons); setContentView(frame); toasts(DIALOG_OPENING_TUTORIAL); } @Override public boolean onTrackballEvent(MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_UP){ captureChess = true; return true; } return super.onTrackballEvent(event); } @Override protected void onPause() { super.onPause(); //clears the callback stack mPreview.onPause(); glview.onPause(); } @Override protected void onResume() { super.onResume(); glview.onResume(); //add an initiall callback stack to the preview on resume... //this one will just draw the frames to opengl LinkedList cbstack = new LinkedList(); cbstack.add(glview.getDrawCallback()); mPreview.addCallbackStack(cbstack); mPreview.onResume(); } // final processor so taht these processor callbacks can access it final Processor processor = new Processor(); class FastProcessor implements NativeProcessor.PoolCallback { @Override public void process(int idx, image_pool pool, long timestamp, NativeProcessor nativeProcessor) { processor.detectAndDrawFeatures(idx, pool, cvcamera.DETECT_FAST); } } class STARProcessor implements NativeProcessor.PoolCallback { @Override public void process(int idx, image_pool pool, long timestamp, NativeProcessor nativeProcessor) { processor.detectAndDrawFeatures(idx, pool, cvcamera.DETECT_STAR); } } class SURFProcessor implements NativeProcessor.PoolCallback { @Override public void process(int idx, image_pool pool, long timestamp, NativeProcessor nativeProcessor) { processor.detectAndDrawFeatures(idx, pool, cvcamera.DETECT_SURF); } } String calib_text = null; String calib_file_loc = null; class CalibrationProcessor implements NativeProcessor.PoolCallback { boolean calibrated = false; @Override public void process(int idx, image_pool pool, long timestamp, NativeProcessor nativeProcessor) { if (calibrated) { processor.drawText(idx, pool, "Calibrated successfully"); return; } if (processor.getNumberDetectedChessboards() == 10) { File opencvdir = new File(Environment .getExternalStorageDirectory(), "opencv"); if (!opencvdir.exists()) { opencvdir.mkdir(); } File calibfile = new File(opencvdir, "camera.yml"); calib_file_loc = calibfile.getAbsolutePath(); processor.calibrate(calibfile.getAbsolutePath()); Log.i("chessboard", "calibrated"); calibrated = true; processor.resetChess(); runOnUiThread(new Runnable() { @Override public void run() { removeDialog(DIALOG_CALIBRATING); } }); try { StringBuilder text = new StringBuilder(); String NL = System.getProperty("line.separator"); Scanner scanner = new Scanner(calibfile); try { while (scanner.hasNextLine()) { text.append(scanner.nextLine() + NL); } } finally { scanner.close(); } calib_text = text.toString(); runOnUiThread(new Runnable() { @Override public void run() { showDialog(DIALOG_CALIBRATION_FILE); } }); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else if (captureChess && processor.detectAndDrawChessboard(idx, pool)) { runOnUiThread(new Runnable() { String numchess = String.valueOf(processor .getNumberDetectedChessboards()); @Override public void run() { Toast.makeText(CVCamera.this, "Detected " + numchess + " of 10 chessboards", Toast.LENGTH_SHORT).show(); } }); Log.i("cvcamera", "detected a chessboard, n chess boards found: " + String.valueOf(processor .getNumberDetectedChessboards())); } captureChess = false; if (processor.getNumberDetectedChessboards() == 10) { runOnUiThread(new Runnable() { @Override public void run() { showDialog(DIALOG_CALIBRATING); } }); processor.drawText(idx, pool, "Calibrating, please wait."); } if (processor.getNumberDetectedChessboards() < 10) { processor.drawText(idx, pool, "found " + processor.getNumberDetectedChessboards() + "/10 chessboards"); } } } }