mirror of
https://github.com/opencv/opencv.git
synced 2024-11-28 05:06:29 +08:00
Added camera calibration sample for android
This commit is contained in:
parent
be8b3687f4
commit
23c802b4cd
9
samples/android/camera-calibration/.classpath
Normal file
9
samples/android/camera-calibration/.classpath
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
33
samples/android/camera-calibration/.project
Normal file
33
samples/android/camera-calibration/.project
Normal file
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>OpenCV Sample - camera-calibration</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
@ -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
|
38
samples/android/camera-calibration/AndroidManifest.xml
Normal file
38
samples/android/camera-calibration/AndroidManifest.xml
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.opencv.samples.cameracalibration"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0" >
|
||||
|
||||
<application
|
||||
android:label="@string/app_name"
|
||||
android:icon="@drawable/icon"
|
||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
|
||||
|
||||
<activity android:name="CameraCalibrationActivity"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="landscape"
|
||||
android:configChanges="keyboardHidden|orientation" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<supports-screens android:resizeable="true"
|
||||
android:smallScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:largeScreens="true"
|
||||
android:anyDensity="true" />
|
||||
|
||||
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="11" />
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
|
||||
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.camera.front" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.camera.front.autofocus" android:required="false"/>
|
||||
|
||||
</manifest>
|
6
samples/android/camera-calibration/CMakeLists.txt
Normal file
6
samples/android/camera-calibration/CMakeLists.txt
Normal file
@ -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()
|
BIN
samples/android/camera-calibration/res/drawable/icon.png
Normal file
BIN
samples/android/camera-calibration/res/drawable/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
@ -0,0 +1,12 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:opencv="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<org.opencv.android.JavaCameraView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:id="@+id/camera_calibration_java_surface_view" />
|
||||
|
||||
</LinearLayout>
|
22
samples/android/camera-calibration/res/menu/calibration.xml
Normal file
22
samples/android/camera-calibration/res/menu/calibration.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<group android:checkableBehavior="single">
|
||||
<item android:id="@+id/calibrate"
|
||||
android:title="@string/action_calibrate"
|
||||
android:showAsAction="ifRoom|withText" />
|
||||
<item android:id="@+id/preview_mode"
|
||||
android:title="@string/preview_mode">
|
||||
<menu>
|
||||
<group android:checkableBehavior="single">
|
||||
<item android:id="@+id/calibration"
|
||||
android:title="@string/calibration"
|
||||
android:checked="true" />
|
||||
<item android:id="@+id/undistortion"
|
||||
android:title="@string/undistortion" />
|
||||
<item android:id="@+id/comparison"
|
||||
android:title="@string/comparison" />
|
||||
</group>
|
||||
</menu>
|
||||
</item>
|
||||
</group>
|
||||
</menu>
|
18
samples/android/camera-calibration/res/values/strings.xml
Normal file
18
samples/android/camera-calibration/res/values/strings.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="app_name">OCV Camera Calibration</string>
|
||||
<string name="action_calibrate">Calibrate</string>
|
||||
<string name="calibration">Calibration</string>
|
||||
<string name="undistortion">Undistortion</string>
|
||||
<string name="comparison">Comparison</string>
|
||||
<string name="preview_mode">Preview mode</string>
|
||||
<string name="calibration_successful">Successfully calibrated!\nAvg. re-projection error:</string>
|
||||
<string name="calibration_unsuccessful">Unsuccessful calibration.\nTry again</string>
|
||||
<string name="more_samples">Please, capture more samples</string>
|
||||
<string name="calibrating">Calibrating...</string>
|
||||
<string name="please_wait">Please, wait</string>
|
||||
<string name="original">Original</string>
|
||||
<string name="undistorted">Undistorted</string>
|
||||
|
||||
</resources>
|
@ -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;
|
||||
}
|
||||
}
|
@ -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<Void, Void, Void>() {
|
||||
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;
|
||||
}
|
||||
}
|
@ -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<Mat> mCornersBuffer = new ArrayList<Mat>();
|
||||
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<Mat> rvecs = new ArrayList<Mat>();
|
||||
ArrayList<Mat> tvecs = new ArrayList<Mat>();
|
||||
Mat reprojectionErrors = new Mat();
|
||||
ArrayList<Mat> objectPoints = new ArrayList<Mat>();
|
||||
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<Mat> objectPoints,
|
||||
List<Mat> rvecs, List<Mat> 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;
|
||||
}
|
||||
}
|
@ -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<MatOfPoint> border = new ArrayList<MatOfPoint>();
|
||||
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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user