org.ar.rubik.CameraCalibration.java Source code

Java tutorial

Introduction

Here is the source code for org.ar.rubik.CameraCalibration.java

Source

/**
 * Augmented Reality Rubik Cube Wizard
 * 
 * Author: Steven P. Punte (aka Android Steve : android.steve@cl-sw.com)
 * Date:   April 25th 2015
 * 
 * Project Description:
 *   Android application developed on a commercial Smart Phone which, when run on a pair 
 *   of Smart Glasses, guides a user through the process of solving a Rubik Cube.
 *   
 * File Description:
 *   
 *   The Intrinsic Camera Calibration is express as:
 *   1) The OpenGL Projection Matrix
 *   2) The OpenCL Pose Camera Matrix
 *   
 *   The first valid calibration in the list below is used for The Intrinsic Camera Calibration Source:
 *   1) Hard-coded (opencv data) values found in final member data hardcodedCalData.
 *   2) Use data from OpenCV Android Camera Calibration Service if it is installed and valid.
 *   3) Use data from device manufacturer.
 *   4) Use hard-coded values found in final member data defaultCalData.
 *   =+= above is not yet implemented.
 * 
 * License:
 * 
 *  GPL
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.ar.rubik;

import java.util.Arrays;

import org.opencv.core.CvType;
import org.opencv.core.Mat;

import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.opengl.Matrix;
import android.util.Log;

/**
 * @author android.steve@cl-sw.com
 *
 */
public class CameraCalibration {

    //   private class CalData {
    //
    //       // Field of horizontal axis view in radians
    //       private float fovX;
    //       
    //       // Field of vertical axis view in radians
    //       private float fovY;
    //      
    //   }
    //   private CalData hardcodedCalData;
    //   private CalData defaultCalDada;

    // Field of horizontal axis view in radians
    private float fovX;

    // Field of vertical axis view in radians
    private float fovY;

    /**
     * Camera Parameters Constructor
     */
    public CameraCalibration() {

        Camera camera = Camera.open();
        Parameters parameters = camera.getParameters();
        camera.release();

        //        Log.v(Constants.TAG_CAL, "Calibration Camera Parameters: width=" + size.width + " height=" + size.height);

        // Field of View in Radians
        fovY = parameters.getVerticalViewAngle() * (float) (Math.PI / 180.0);
        fovX = parameters.getHorizontalViewAngle() * (float) (Math.PI / 180.0);

        //      Log.e(Constants.TAG, "Width = " + size.width + " Height = " + size.height);  // 1920 by 1080 reported.
        //      Log.e(Constants.TAG, "dPOV=" + diagnoalFOV + " dPx=" + diagonalPixels);
        //      Log.e(Constants.TAG, "Camera Focal Length in Pixels Calibration: " + focalLengthPixels);
    }

    /**
     * Get OpenCV Camera Matrix
     * 
     * This matrix represents the intrinsic properties of the camera.
     * Matrix is basically:
     * 
     *    |   Fx    0    Cx  |
     *    |    0   Fy    Cy  |
     *    |    0    0     1  |
     *    
     *    Fx := X Focal Length
     *    Fy := Y Focal Length
     *    Cx := X Optical Center
     *    Cy := Y Optical Center
     * 
     * 
     * @return
     */
    public Mat getOpenCVCameraMatrix(int width, int height) {

        Log.v(Constants.TAG_CAL, "CameraCalibration.getOpenCVMatrix(): width=" + width + " height=" + height);

        double focalLengthXPixels = width / (2.0 * Math.tan(0.5 * fovX));
        double focalLengthYPixels = height / (2.0 * Math.tan(0.5 * fovY));

        Mat cameraMatrix = new Mat(3, 3, CvType.CV_64FC1);
        cameraMatrix.put(0, 0, focalLengthXPixels); // should be X focal length in pixels.
        cameraMatrix.put(0, 1, 0.0);
        cameraMatrix.put(0, 2, width / 2.0);
        cameraMatrix.put(1, 0, 0.0);
        cameraMatrix.put(1, 1, focalLengthYPixels); // should be Y focal length in pixels.
        cameraMatrix.put(1, 2, height / 2.0);
        cameraMatrix.put(2, 0, 0.0);
        cameraMatrix.put(2, 1, 0.0);
        cameraMatrix.put(2, 2, 1.0);

        Log.v(Constants.TAG_CAL, "Android Camera Calibration Matrix: ");
        Log.v(Constants.TAG_CAL, cameraMatrix.dump());

        //       =+= From Android Camera Calibration App at resolution 1920 x 1080
        //       cameraMatrix.put(0, 0, 1686.1);
        //       cameraMatrix.put(0, 1, 0.0);
        //       cameraMatrix.put(0, 2, 959.5);
        //       cameraMatrix.put(1, 0, 0.0);
        //       cameraMatrix.put(1, 1, 1686.1);
        //       cameraMatrix.put(1, 2, 539.5);
        //       cameraMatrix.put(2, 0, 0.0);
        //       cameraMatrix.put(2, 1, 0.0);
        //       cameraMatrix.put(2, 2, 1.0);
        //
        //       Log.v(Constants.TAG_CAL, "Camera Calibration App Matrix: ");
        //       Log.v(Constants.TAG_CAL, cameraMatrix.dump());

        return cameraMatrix;
    }

    /**
     * Return Camera Calibration Coefficients: specifically k1, k1 [, p3, p4 [, k3]] as defined by OpenCV
     * 
     * @return
     */
    public double[] getDistortionCoefficients() {

        //       =+= From Android Camera Calibration App at resolution 1920 x 1080
        //      double [] distCoeff =  { 
        //            0.0940951391875556,
        //            0.856988256473992,
        //            0,
        //            0,
        //            -4.559694183079539};
        //
        //      return distCoeff;

        // No distortion coefficients used.
        return null;

    }

    /**
     * Calculate OpenGL Projection Matrix
     * 
     * @param width
     * @param height
     * @return
     */
    public float[] calculateOpenGLProjectionMatrix(int width, int height) {

        float[] mProjectionMatrix = new float[16];

        //      /*
        //       * Method 1
        //       * 
        //       * Use aspect ratio of reported openGL screen width and height.
        //       * This produces a nice looking cube, but size is off by 1/3.
        //       */
        //      float ratio = (float) width / height;
        //      Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 2, 100);
        //      Log.v(Constants.TAG_CAL, "Method 1 Projection Matrix: " + Arrays.toString(mProjectionMatrix));
        //      
        //      
        //      /*
        //       * Method 2
        //       * 
        //       * This uses camera reported Fields of View, and produces a roughly
        //       * correct size cube, but a bit off and looking a bit funny.
        //       * 
        //       * =+= I believe this method is the closer to the correct solution.
        //       */
        //      float near = 1.0f;
        //      float far  = 20.f;
        //      float right = (float) (Math.tan((double) fovX/2) * near);
        //      float top = (float) (Math.tan((double) fovY/2) * near);
        //      Matrix.frustumM(mProjectionMatrix, 0, -right, right, -top, right, near, far);
        //      Log.v(Constants.TAG_CAL, "Method 2 Projection Matrix: " + Arrays.toString(mProjectionMatrix));

        /*
         * Method 3
         * 
         * Use camera reported Field of View and screen aspect ratio with the android.opengl.Matrix
         * provided member function perspectiveM().  This produces excellent results at 
         * screen resolution 1920 x 1080. 
         */
        float ratio = (float) width / height;
        float near = 1.0f;
        float far = 20.f;
        Matrix.perspectiveM(mProjectionMatrix, 0, (float) (fovY * 180.0 / Math.PI), ratio, near, far);
        Log.v(Constants.TAG_CAL, "Method 3 Projection Matrix: " + Arrays.toString(mProjectionMatrix));

        return mProjectionMatrix;
    }
}