de.vion.eyetracking.cameracalib.calibration.opencv.CameraCalibrator.java Source code

Java tutorial

Introduction

Here is the source code for de.vion.eyetracking.cameracalib.calibration.opencv.CameraCalibrator.java

Source

package de.vion.eyetracking.cameracalib.calibration.opencv;

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;

/**
 * 
 * The class that performs the camera calibration. Adopted from OpenCV Camera
 * Calibration Application example (for the license, see the OpenCV license
 * in the licenses folder)
 * 
 */
public class CameraCalibrator {
    private static final String TAG = "OCVSample::CameraCalibrator";

    private final Size mPatternSize = new Size(4, 11);
    private final int mCornersSize = (int) (this.mPatternSize.width * this.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) {
        this.mImageSize = new Size(width, height);
        this.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(this.mCameraMatrix);
        this.mCameraMatrix.put(0, 0, 1.0);
        Mat.zeros(5, 1, CvType.CV_64FC1).copyTo(this.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(this.mCornersSize, 1, CvType.CV_32FC3));
        calcBoardCornerPositions(objectPoints.get(0));
        for (int i = 1; i < this.mCornersBuffer.size(); i++) {
            objectPoints.add(objectPoints.get(0));
        }

        Calib3d.calibrateCamera(objectPoints, this.mCornersBuffer, this.mImageSize, this.mCameraMatrix,
                this.mDistortionCoefficients, rvecs, tvecs, this.mFlags);

        this.mIsCalibrated = Core.checkRange(this.mCameraMatrix) && Core.checkRange(this.mDistortionCoefficients);

        this.mRms = computeReprojectionErrors(objectPoints, rvecs, tvecs, reprojectionErrors);
    }

    public void clearCorners() {
        this.mCornersBuffer.clear();
    }

    private void calcBoardCornerPositions(Mat corners) {
        final int cn = 3;
        float positions[] = new float[this.mCornersSize * cn];

        for (int i = 0; i < this.mPatternSize.height; i++) {
            for (int j = 0; j < this.mPatternSize.width * cn; j += cn) {
                positions[(int) (i * this.mPatternSize.width * cn + j + 0)] = (2 * (j / cn) + i % 2)
                        * (float) this.mSquareSize;
                positions[(int) (i * this.mPatternSize.width * cn + j + 1)] = i * (float) this.mSquareSize;
                positions[(int) (i * this.mPatternSize.width * cn + j + 2)] = 0;
            }
        }
        corners.create(this.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(this.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), this.mCameraMatrix, distortionCoefficients,
                    cornersProjected);
            error = Core.norm(this.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) {
        this.mPatternWasFound = Calib3d.findCirclesGridDefault(grayFrame, this.mPatternSize, this.mCorners,
                Calib3d.CALIB_CB_ASYMMETRIC_GRID);
    }

    public void addCorners() {
        if (this.mPatternWasFound) {
            this.mCornersBuffer.add(this.mCorners.clone());
        }
    }

    private void drawPoints(Mat rgbaFrame) {
        Calib3d.drawChessboardCorners(rgbaFrame, this.mPatternSize, this.mCorners, this.mPatternWasFound);
    }

    private void renderFrame(Mat rgbaFrame) {
        drawPoints(rgbaFrame);

        Core.putText(rgbaFrame, "Captured: " + this.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 this.mCameraMatrix;
    }

    public Mat getDistortionCoefficients() {
        return this.mDistortionCoefficients;
    }

    public int getCornersBufferSize() {
        return this.mCornersBuffer.size();
    }

    public double getAvgReprojectionError() {
        return this.mRms;
    }

    public boolean isCalibrated() {
        return this.mIsCalibrated;
    }

    public void setCalibrated() {
        this.mIsCalibrated = true;
    }
}