From: Daniil Osokin Date: Mon, 1 Jul 2013 20:53:18 +0000 (+0400) Subject: Added camera calibration sample for android X-Git-Tag: accepted/tizen/ivi/20140515.103456~1^2~618^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=23c802b4cdde447f68afdef428443e81f0fa0b2f;p=profile%2Fivi%2Fopencv.git Added camera calibration sample for android --- diff --git a/samples/android/camera-calibration/.classpath b/samples/android/camera-calibration/.classpath new file mode 100644 index 0000000..46c3d46 --- /dev/null +++ b/samples/android/camera-calibration/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/samples/android/camera-calibration/.project b/samples/android/camera-calibration/.project new file mode 100644 index 0000000..eae413e --- /dev/null +++ b/samples/android/camera-calibration/.project @@ -0,0 +1,33 @@ + + + OpenCV Sample - camera-calibration + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/samples/android/camera-calibration/.settings/org.eclipse.jdt.core.prefs b/samples/android/camera-calibration/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..48ab4c6 --- /dev/null +++ b/samples/android/camera-calibration/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/samples/android/camera-calibration/AndroidManifest.xml b/samples/android/camera-calibration/AndroidManifest.xml new file mode 100644 index 0000000..d47576a --- /dev/null +++ b/samples/android/camera-calibration/AndroidManifest.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/android/camera-calibration/CMakeLists.txt b/samples/android/camera-calibration/CMakeLists.txt new file mode 100644 index 0000000..83b11b3 --- /dev/null +++ b/samples/android/camera-calibration/CMakeLists.txt @@ -0,0 +1,6 @@ +set(sample example-camera-calibration) + +add_android_project(${sample} "${CMAKE_CURRENT_SOURCE_DIR}" LIBRARY_DEPS ${OpenCV_BINARY_DIR} SDK_TARGET 11 ${ANDROID_SDK_TARGET}) +if(TARGET ${sample}) + add_dependencies(opencv_android_examples ${sample}) +endif() diff --git a/samples/android/camera-calibration/res/drawable/icon.png b/samples/android/camera-calibration/res/drawable/icon.png new file mode 100644 index 0000000..79ad948 Binary files /dev/null and b/samples/android/camera-calibration/res/drawable/icon.png differ diff --git a/samples/android/camera-calibration/res/layout/camera_calibration_surface_view.xml b/samples/android/camera-calibration/res/layout/camera_calibration_surface_view.xml new file mode 100644 index 0000000..0feccde --- /dev/null +++ b/samples/android/camera-calibration/res/layout/camera_calibration_surface_view.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/samples/android/camera-calibration/res/menu/calibration.xml b/samples/android/camera-calibration/res/menu/calibration.xml new file mode 100644 index 0000000..9c90f12 --- /dev/null +++ b/samples/android/camera-calibration/res/menu/calibration.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + diff --git a/samples/android/camera-calibration/res/values/strings.xml b/samples/android/camera-calibration/res/values/strings.xml new file mode 100644 index 0000000..e1ce932 --- /dev/null +++ b/samples/android/camera-calibration/res/values/strings.xml @@ -0,0 +1,18 @@ + + + + OCV Camera Calibration + Calibrate + Calibration + Undistortion + Comparison + Preview mode + Successfully calibrated!\nAvg. re-projection error: + Unsuccessful calibration.\nTry again + Please, capture more samples + Calibrating... + Please, wait + Original + Undistorted + + diff --git a/samples/android/camera-calibration/src/org/opencv/samples/cameracalibration/CalibrationResult.java b/samples/android/camera-calibration/src/org/opencv/samples/cameracalibration/CalibrationResult.java new file mode 100644 index 0000000..4b03d59 --- /dev/null +++ b/samples/android/camera-calibration/src/org/opencv/samples/cameracalibration/CalibrationResult.java @@ -0,0 +1,69 @@ +package org.opencv.samples.cameracalibration; + +import org.opencv.core.Mat; + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; + +public abstract class CalibrationResult { + private static final String TAG = "OCVSample::CalibrationResult"; + + private static final int CAMERA_MATRIX_ROWS = 3; + private static final int CAMERA_MATRIX_COLS = 3; + private static final int DISTORTION_COEFFICIENTS_SIZE = 5; + + public static void save(Activity activity, Mat cameraMatrix, Mat distortionCoefficients) { + SharedPreferences sharedPref = activity.getPreferences(Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + + double[] cameraMatrixArray = new double[CAMERA_MATRIX_ROWS * CAMERA_MATRIX_COLS]; + cameraMatrix.get(0, 0, cameraMatrixArray); + for (int i = 0; i < CAMERA_MATRIX_ROWS; i++) { + for (int j = 0; j < CAMERA_MATRIX_COLS; j++) { + Integer id = i * CAMERA_MATRIX_ROWS + j; + editor.putFloat(id.toString(), (float)cameraMatrixArray[id]); + } + } + + double[] distortionCoefficientsArray = new double[DISTORTION_COEFFICIENTS_SIZE]; + distortionCoefficients.get(0, 0, distortionCoefficientsArray); + int shift = CAMERA_MATRIX_ROWS * CAMERA_MATRIX_COLS; + for (Integer i = shift; i < DISTORTION_COEFFICIENTS_SIZE + shift; i++) { + editor.putFloat(i.toString(), (float)distortionCoefficientsArray[i-shift]); + } + + editor.commit(); + Log.i(TAG, "Saved camera matrix: " + cameraMatrix.dump()); + Log.i(TAG, "Saved distortion coefficients: " + distortionCoefficients.dump()); + } + + public static boolean tryLoad(Activity activity, Mat cameraMatrix, Mat distortionCoefficients) { + SharedPreferences sharedPref = activity.getPreferences(Context.MODE_PRIVATE); + if (sharedPref.getFloat("0", -1) == -1) { + Log.i(TAG, "No previous calibration results found"); + return false; + } + + double[] cameraMatrixArray = new double[CAMERA_MATRIX_ROWS * CAMERA_MATRIX_COLS]; + for (int i = 0; i < CAMERA_MATRIX_ROWS; i++) { + for (int j = 0; j < CAMERA_MATRIX_COLS; j++) { + Integer id = i * CAMERA_MATRIX_ROWS + j; + cameraMatrixArray[id] = sharedPref.getFloat(id.toString(), -1); + } + } + cameraMatrix.put(0, 0, cameraMatrixArray); + Log.i(TAG, "Loaded camera matrix: " + cameraMatrix.dump()); + + double[] distortionCoefficientsArray = new double[DISTORTION_COEFFICIENTS_SIZE]; + int shift = CAMERA_MATRIX_ROWS * CAMERA_MATRIX_COLS; + for (Integer i = shift; i < DISTORTION_COEFFICIENTS_SIZE + shift; i++) { + distortionCoefficientsArray[i - shift] = sharedPref.getFloat(i.toString(), -1); + } + distortionCoefficients.put(0, 0, distortionCoefficientsArray); + Log.i(TAG, "Loaded distortion coefficients: " + distortionCoefficients.dump()); + + return true; + } +} diff --git a/samples/android/camera-calibration/src/org/opencv/samples/cameracalibration/CameraCalibrationActivity.java b/samples/android/camera-calibration/src/org/opencv/samples/cameracalibration/CameraCalibrationActivity.java new file mode 100644 index 0000000..33c9bbb --- /dev/null +++ b/samples/android/camera-calibration/src/org/opencv/samples/cameracalibration/CameraCalibrationActivity.java @@ -0,0 +1,216 @@ +// This sample is based on "Camera calibration With OpenCV" tutorial: +// http://docs.opencv.org/doc/tutorials/calib3d/camera_calibration/camera_calibration.html +// +// It uses standard OpenCV asymmetric circles grid pattern 11x4: +// https://github.com/Itseez/opencv/blob/2.4/doc/acircles_pattern.png. +// The results are the camera matrix and 5 distortion coefficients. +// +// Tap on highlighted pattern to capture pattern corners for calibration. +// Move pattern along the whole screen and capture data. +// +// When you've captured necessary amount of pattern corners (usually ~20 are enough), +// press "Calibrate" button for performing camera calibration. + +package org.opencv.samples.cameracalibration; + +import org.opencv.android.BaseLoaderCallback; +import org.opencv.android.CameraBridgeViewBase; +import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame; +import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2; +import org.opencv.android.LoaderCallbackInterface; +import org.opencv.android.OpenCVLoader; +import org.opencv.core.Mat; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.res.Resources; +import android.os.AsyncTask; +import android.os.Bundle; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.SurfaceView; +import android.view.View; +import android.view.View.OnTouchListener; +import android.view.WindowManager; +import android.widget.Toast; + +public class CameraCalibrationActivity extends Activity implements CvCameraViewListener2, OnTouchListener { + private static final String TAG = "OCVSample::Activity"; + + private CameraBridgeViewBase mOpenCvCameraView; + private CameraCalibrator mCalibrator; + private OnCameraFrameRender mOnCameraFrameRender; + private int mWidth; + private int mHeight; + + private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) { + @Override + public void onManagerConnected(int status) { + switch (status) { + case LoaderCallbackInterface.SUCCESS: + { + Log.i(TAG, "OpenCV loaded successfully"); + mOpenCvCameraView.enableView(); + mOpenCvCameraView.setOnTouchListener(CameraCalibrationActivity.this); + } break; + default: + { + super.onManagerConnected(status); + } break; + } + } + }; + + public CameraCalibrationActivity() { + Log.i(TAG, "Instantiated new " + this.getClass()); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + Log.i(TAG, "called onCreate"); + super.onCreate(savedInstanceState); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + setContentView(R.layout.camera_calibration_surface_view); + + mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.camera_calibration_java_surface_view); + mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE); + mOpenCvCameraView.setCvCameraViewListener(this); + } + + @Override + public void onPause() + { + super.onPause(); + if (mOpenCvCameraView != null) + mOpenCvCameraView.disableView(); + } + + @Override + public void onResume() + { + super.onResume(); + OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_2, this, mLoaderCallback); + } + + public void onDestroy() { + super.onDestroy(); + if (mOpenCvCameraView != null) + mOpenCvCameraView.disableView(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.calibration, menu); + + return true; + } + + @Override + public boolean onPrepareOptionsMenu (Menu menu) { + super.onPrepareOptionsMenu(menu); + menu.findItem(R.id.preview_mode).setEnabled(true); + if (!mCalibrator.isCalibrated()) + menu.findItem(R.id.preview_mode).setEnabled(false); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.calibration: + mOnCameraFrameRender = + new OnCameraFrameRender(new CalibrationFrameRender(mCalibrator)); + item.setChecked(true); + return true; + case R.id.undistortion: + mOnCameraFrameRender = + new OnCameraFrameRender(new UndistortionFrameRender(mCalibrator)); + item.setChecked(true); + return true; + case R.id.comparison: + mOnCameraFrameRender = + new OnCameraFrameRender(new ComparisonFrameRender(mCalibrator, mWidth, mHeight, getResources())); + item.setChecked(true); + return true; + case R.id.calibrate: + final Resources res = getResources(); + if (mCalibrator.getCornersBufferSize() < 2) { + (Toast.makeText(this, res.getString(R.string.more_samples), Toast.LENGTH_SHORT)).show(); + return true; + } + + mOnCameraFrameRender = new OnCameraFrameRender(new PreviewFrameRender()); + new AsyncTask() { + private ProgressDialog calibrationProgress; + + @Override + protected void onPreExecute() { + calibrationProgress = new ProgressDialog(CameraCalibrationActivity.this); + calibrationProgress.setTitle(res.getString(R.string.calibrating)); + calibrationProgress.setMessage(res.getString(R.string.please_wait)); + calibrationProgress.setCancelable(false); + calibrationProgress.setIndeterminate(true); + calibrationProgress.show(); + } + + @Override + protected Void doInBackground(Void... arg0) { + mCalibrator.calibrate(); + return null; + } + + @Override + protected void onPostExecute(Void result) { + calibrationProgress.dismiss(); + mCalibrator.clearCorners(); + mOnCameraFrameRender = new OnCameraFrameRender(new CalibrationFrameRender(mCalibrator)); + String resultMessage = (mCalibrator.isCalibrated()) ? + res.getString(R.string.calibration_successful) + " " + mCalibrator.getAvgReprojectionError() : + res.getString(R.string.calibration_unsuccessful); + (Toast.makeText(CameraCalibrationActivity.this, resultMessage, Toast.LENGTH_SHORT)).show(); + + if (mCalibrator.isCalibrated()) { + CalibrationResult.save(CameraCalibrationActivity.this, + mCalibrator.getCameraMatrix(), mCalibrator.getDistortionCoefficients()); + } + } + }.execute(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + public void onCameraViewStarted(int width, int height) { + if (mWidth != width || mHeight != height) { + mWidth = width; + mHeight = height; + mCalibrator = new CameraCalibrator(mWidth, mHeight); + if (CalibrationResult.tryLoad(this, mCalibrator.getCameraMatrix(), mCalibrator.getDistortionCoefficients())) { + mCalibrator.setCalibrated(); + } + + mOnCameraFrameRender = new OnCameraFrameRender(new CalibrationFrameRender(mCalibrator)); + } + } + + public void onCameraViewStopped() { + } + + public Mat onCameraFrame(CvCameraViewFrame inputFrame) { + return mOnCameraFrameRender.render(inputFrame); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + Log.d(TAG, "onTouch invoked"); + + mCalibrator.addCorners(); + return false; + } +} diff --git a/samples/android/camera-calibration/src/org/opencv/samples/cameracalibration/CameraCalibrator.java b/samples/android/camera-calibration/src/org/opencv/samples/cameracalibration/CameraCalibrator.java new file mode 100644 index 0000000..f0cd230 --- /dev/null +++ b/samples/android/camera-calibration/src/org/opencv/samples/cameracalibration/CameraCalibrator.java @@ -0,0 +1,169 @@ +package org.opencv.samples.cameracalibration; + +import java.util.ArrayList; +import java.util.List; + +import org.opencv.calib3d.Calib3d; +import org.opencv.core.Core; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.MatOfDouble; +import org.opencv.core.MatOfPoint2f; +import org.opencv.core.MatOfPoint3f; +import org.opencv.core.Point; +import org.opencv.core.Scalar; +import org.opencv.core.Size; + +import android.util.Log; + +public class CameraCalibrator { + private static final String TAG = "OCVSample::CameraCalibrator"; + + private final Size mPatternSize = new Size(4, 11); + private final int mCornersSize = (int)(mPatternSize.width * mPatternSize.height); + private boolean mPatternWasFound = false; + private MatOfPoint2f mCorners = new MatOfPoint2f(); + private List mCornersBuffer = new ArrayList(); + private boolean mIsCalibrated = false; + + private Mat mCameraMatrix = new Mat(); + private Mat mDistortionCoefficients = new Mat(); + private int mFlags; + private double mRms; + private double mSquareSize = 0.0181; + private Size mImageSize; + + public CameraCalibrator(int width, int height) { + mImageSize = new Size(width, height); + mFlags = Calib3d.CALIB_FIX_PRINCIPAL_POINT + + Calib3d.CALIB_ZERO_TANGENT_DIST + + Calib3d.CALIB_FIX_ASPECT_RATIO + + Calib3d.CALIB_FIX_K4 + + Calib3d.CALIB_FIX_K5; + Mat.eye(3, 3, CvType.CV_64FC1).copyTo(mCameraMatrix); + mCameraMatrix.put(0, 0, 1.0); + Mat.zeros(5, 1, CvType.CV_64FC1).copyTo(mDistortionCoefficients); + Log.i(TAG, "Instantiated new " + this.getClass()); + } + + public void processFrame(Mat grayFrame, Mat rgbaFrame) { + findPattern(grayFrame); + renderFrame(rgbaFrame); + } + + public void calibrate() { + ArrayList rvecs = new ArrayList(); + ArrayList tvecs = new ArrayList(); + Mat reprojectionErrors = new Mat(); + ArrayList objectPoints = new ArrayList(); + objectPoints.add(Mat.zeros(mCornersSize, 1, CvType.CV_32FC3)); + calcBoardCornerPositions(objectPoints.get(0)); + for (int i = 1; i < mCornersBuffer.size(); i++) { + objectPoints.add(objectPoints.get(0)); + } + + Calib3d.calibrateCamera(objectPoints, mCornersBuffer, mImageSize, + mCameraMatrix, mDistortionCoefficients, rvecs, tvecs, mFlags); + + mIsCalibrated = Core.checkRange(mCameraMatrix) + && Core.checkRange(mDistortionCoefficients); + + mRms = computeReprojectionErrors(objectPoints, rvecs, tvecs, reprojectionErrors); + Log.i(TAG, String.format("Average re-projection error: %f", mRms)); + Log.i(TAG, "Camera matrix: " + mCameraMatrix.dump()); + Log.i(TAG, "Distortion coefficients: " + mDistortionCoefficients.dump()); + } + + public void clearCorners() { + mCornersBuffer.clear(); + } + + private void calcBoardCornerPositions(Mat corners) { + final int cn = 3; + float positions[] = new float[mCornersSize * cn]; + + for (int i = 0; i < mPatternSize.height; i++) { + for (int j = 0; j < mPatternSize.width * cn; j += cn) { + positions[(int) (i * mPatternSize.width * cn + j + 0)] = + (2 * (j / cn) + i % 2) * (float) mSquareSize; + positions[(int) (i * mPatternSize.width * cn + j + 1)] = + i * (float) mSquareSize; + positions[(int) (i * mPatternSize.width * cn + j + 2)] = 0; + } + } + corners.create(mCornersSize, 1, CvType.CV_32FC3); + corners.put(0, 0, positions); + } + + private double computeReprojectionErrors(List objectPoints, + List rvecs, List tvecs, Mat perViewErrors) { + MatOfPoint2f cornersProjected = new MatOfPoint2f(); + double totalError = 0; + double error; + float viewErrors[] = new float[objectPoints.size()]; + + MatOfDouble distortionCoefficients = new MatOfDouble(mDistortionCoefficients); + int totalPoints = 0; + for (int i = 0; i < objectPoints.size(); i++) { + MatOfPoint3f points = new MatOfPoint3f(objectPoints.get(i)); + Calib3d.projectPoints(points, rvecs.get(i), tvecs.get(i), + mCameraMatrix, distortionCoefficients, cornersProjected); + error = Core.norm(mCornersBuffer.get(i), cornersProjected, Core.NORM_L2); + + int n = objectPoints.get(i).rows(); + viewErrors[i] = (float) Math.sqrt(error * error / n); + totalError += error * error; + totalPoints += n; + } + perViewErrors.create(objectPoints.size(), 1, CvType.CV_32FC1); + perViewErrors.put(0, 0, viewErrors); + + return Math.sqrt(totalError / totalPoints); + } + + private void findPattern(Mat grayFrame) { + mPatternWasFound = Calib3d.findCirclesGridDefault(grayFrame, mPatternSize, + mCorners, Calib3d.CALIB_CB_ASYMMETRIC_GRID); + } + + public void addCorners() { + if (mPatternWasFound) { + mCornersBuffer.add(mCorners.clone()); + } + } + + private void drawPoints(Mat rgbaFrame) { + Calib3d.drawChessboardCorners(rgbaFrame, mPatternSize, mCorners, mPatternWasFound); + } + + private void renderFrame(Mat rgbaFrame) { + drawPoints(rgbaFrame); + + Core.putText(rgbaFrame, "Captured: " + mCornersBuffer.size(), new Point(rgbaFrame.cols() / 3 * 2, rgbaFrame.rows() * 0.1), + Core.FONT_HERSHEY_SIMPLEX, 1.0, new Scalar(255, 255, 0)); + } + + public Mat getCameraMatrix() { + return mCameraMatrix; + } + + public Mat getDistortionCoefficients() { + return mDistortionCoefficients; + } + + public int getCornersBufferSize() { + return mCornersBuffer.size(); + } + + public double getAvgReprojectionError() { + return mRms; + } + + public boolean isCalibrated() { + return mIsCalibrated; + } + + public void setCalibrated() { + mIsCalibrated = true; + } +} diff --git a/samples/android/camera-calibration/src/org/opencv/samples/cameracalibration/OnCameraFrameRender.java b/samples/android/camera-calibration/src/org/opencv/samples/cameracalibration/OnCameraFrameRender.java new file mode 100644 index 0000000..3f155c2 --- /dev/null +++ b/samples/android/camera-calibration/src/org/opencv/samples/cameracalibration/OnCameraFrameRender.java @@ -0,0 +1,102 @@ +package org.opencv.samples.cameracalibration; + +import java.util.ArrayList; +import java.util.List; + +import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame; +import org.opencv.core.Core; +import org.opencv.core.Mat; +import org.opencv.core.MatOfPoint; +import org.opencv.core.Point; +import org.opencv.core.Range; +import org.opencv.core.Scalar; +import org.opencv.imgproc.Imgproc; + +import android.content.res.Resources; + +abstract class FrameRender { + protected CameraCalibrator mCalibrator; + + public abstract Mat render(CvCameraViewFrame inputFrame); +} + +class PreviewFrameRender extends FrameRender { + @Override + public Mat render(CvCameraViewFrame inputFrame) { + return inputFrame.rgba(); + } +} + +class CalibrationFrameRender extends FrameRender { + public CalibrationFrameRender(CameraCalibrator calibrator) { + mCalibrator = calibrator; + } + + @Override + public Mat render(CvCameraViewFrame inputFrame) { + Mat rgbaFrame = inputFrame.rgba(); + Mat grayFrame = inputFrame.gray(); + mCalibrator.processFrame(grayFrame, rgbaFrame); + + return rgbaFrame; + } +} + +class UndistortionFrameRender extends FrameRender { + public UndistortionFrameRender(CameraCalibrator calibrator) { + mCalibrator = calibrator; + } + + @Override + public Mat render(CvCameraViewFrame inputFrame) { + Mat renderedFrame = new Mat(inputFrame.rgba().size(), inputFrame.rgba().type()); + Imgproc.undistort(inputFrame.rgba(), renderedFrame, + mCalibrator.getCameraMatrix(), mCalibrator.getDistortionCoefficients()); + + return renderedFrame; + } +} + +class ComparisonFrameRender extends FrameRender { + private int mWidth; + private int mHeight; + private Resources mResources; + public ComparisonFrameRender(CameraCalibrator calibrator, int width, int height, Resources resources) { + mCalibrator = calibrator; + mWidth = width; + mHeight = height; + mResources = resources; + } + + @Override + public Mat render(CvCameraViewFrame inputFrame) { + Mat undistortedFrame = new Mat(inputFrame.rgba().size(), inputFrame.rgba().type()); + Imgproc.undistort(inputFrame.rgba(), undistortedFrame, + mCalibrator.getCameraMatrix(), mCalibrator.getDistortionCoefficients()); + + Mat comparisonFrame = inputFrame.rgba(); + undistortedFrame.colRange(new Range(0, mWidth / 2)).copyTo(comparisonFrame.colRange(new Range(mWidth / 2, mWidth))); + List border = new ArrayList(); + final int shift = (int)(mWidth * 0.005); + border.add(new MatOfPoint(new Point(mWidth / 2 - shift, 0), new Point(mWidth / 2 + shift, 0), + new Point(mWidth / 2 + shift, mHeight), new Point(mWidth / 2 - shift, mHeight))); + Core.fillPoly(comparisonFrame, border, new Scalar(255, 255, 255)); + + Core.putText(comparisonFrame, mResources.getString(R.string.original), new Point(mWidth * 0.1, mHeight * 0.1), + Core.FONT_HERSHEY_SIMPLEX, 1.0, new Scalar(255, 255, 0)); + Core.putText(comparisonFrame, mResources.getString(R.string.undistorted), new Point(mWidth * 0.6, mHeight * 0.1), + Core.FONT_HERSHEY_SIMPLEX, 1.0, new Scalar(255, 255, 0)); + + return comparisonFrame; + } +} + +class OnCameraFrameRender { + private FrameRender mFrameRender; + public OnCameraFrameRender(FrameRender frameRender) { + mFrameRender = frameRender; + } + public Mat render(CvCameraViewFrame inputFrame) { + return mFrameRender.render(inputFrame); + } +}