/** * Renders a 3D point in the left and right camera views given the stereo parameters. Lens * distortion is taken in account. * * @param param Stereo parameters * @param X Point location in 3D space * @param left location in pixels in left camera * @param right location in pixels in right camera */ public static void renderPointPixel( StereoParameters param, Point3D_F64 X, Point2D_F64 left, Point2D_F64 right) { // compute the location of X in the right camera's reference frame Point3D_F64 rightX = new Point3D_F64(); SePointOps_F64.transform(param.getRightToLeft().invert(null), X, rightX); // location of object in normalized image coordinates Point2D_F64 normLeft = new Point2D_F64(X.x / X.z, X.y / X.z); Point2D_F64 normRight = new Point2D_F64(rightX.x / rightX.z, rightX.y / rightX.z); // convert into pixel coordinates Point2D_F64 pixelLeft = PerspectiveOps.convertNormToPixel(param.left, normLeft.x, normLeft.y, null); Point2D_F64 pixelRight = PerspectiveOps.convertNormToPixel(param.right, normRight.x, normRight.y, null); // take in account lens distortion Point2Transform2_F32 distLeft = LensDistortionOps.transformPoint(param.left).distort_F32(true, true); Point2Transform2_F32 distRight = LensDistortionOps.transformPoint(param.right).distort_F32(true, true); Point2D_F32 lensLeft = new Point2D_F32(); Point2D_F32 lensRight = new Point2D_F32(); distLeft.compute((float) pixelLeft.x, (float) pixelLeft.y, lensLeft); distRight.compute((float) pixelRight.x, (float) pixelRight.y, lensRight); // output solution left.set(lensLeft.x, lensLeft.y); right.set(lensRight.x, lensRight.y); }
/** @author Peter Abeles */ public class TestCalibrateMonoPlanar { CameraPinholeRadial intrinsic = new CameraPinholeRadial(200, 210, 0, 320, 240, 640, 480) .fsetRadial(0.01, -0.02) .fsetTangental(0.03, 0.03); Point2Transform2_F64 normToPixel = LensDistortionOps.transformPoint(intrinsic).distort_F64(false, true); GrayF32 blank = new GrayF32(intrinsic.width, intrinsic.height); List<Se3_F64> targetToCamera = new ArrayList<>(); public TestCalibrateMonoPlanar() { double z = 250; double w = 40; targetToCamera.add( new Se3_F64( ConvertRotation3D_F64.eulerToMatrix(EulerType.XYZ, 0, 0, 0, null), new Vector3D_F64(0, 0, z))); targetToCamera.add( new Se3_F64( ConvertRotation3D_F64.eulerToMatrix(EulerType.XYZ, 0.1, 0, 0, null), new Vector3D_F64(w, 0, z))); targetToCamera.add( new Se3_F64( ConvertRotation3D_F64.eulerToMatrix(EulerType.XYZ, 0, 0.1, 0, null), new Vector3D_F64(w, w, z))); targetToCamera.add( new Se3_F64( ConvertRotation3D_F64.eulerToMatrix(EulerType.XYZ, 0, 0, 0.1, null), new Vector3D_F64(0, -w, z))); targetToCamera.add( new Se3_F64( ConvertRotation3D_F64.eulerToMatrix(EulerType.XYZ, 0.05, 0, 0.1, null), new Vector3D_F64(0, -w, z))); } /** * Give it a fake feature detector and a fairly benign scenario and see if it can correctly * estimate the camera parameters. */ @Test public void fullBasic() { FakeDetector detector = new FakeDetector(); CalibrateMonoPlanar alg = new CalibrateMonoPlanar(detector); alg.configure(true, 2, true); for (int i = 0; i < targetToCamera.size(); i++) { alg.addImage(blank); } CameraPinholeRadial found = alg.process(); assertEquals(intrinsic.fx, found.fx, 1e-3); assertEquals(intrinsic.fy, found.fy, 1e-3); assertEquals(intrinsic.cx, found.cx, 1e-3); assertEquals(intrinsic.cy, found.cy, 1e-3); assertEquals(intrinsic.skew, found.skew, 1e-3); assertEquals(intrinsic.radial[0], found.radial[0], 1e-5); assertEquals(intrinsic.radial[1], found.radial[1], 1e-5); assertEquals(intrinsic.t1, found.t1, 1e-5); assertEquals(intrinsic.t2, found.t2, 1e-5); } private class FakeDetector implements DetectorFiducialCalibration { int count = 0; CalibrationObservation set; List<Point2D_F64> layout = CalibrationDetectorSquareGrid.createLayout(4, 3, 30, 30); @Override public boolean process(GrayF32 input) { Se3_F64 t2c = targetToCamera.get(count++); set = new CalibrationObservation(); for (int i = 0; i < layout.size(); i++) { Point2D_F64 p2 = layout.get(i); // location of calibration point on the target Point3D_F64 p3 = new Point3D_F64(p2.x, p2.y, 0); Point3D_F64 a = SePointOps_F64.transform(t2c, p3, null); Point2D_F64 pixel = new Point2D_F64(); normToPixel.compute(a.x / a.z, a.y / a.z, pixel); if (pixel.x < 0 || pixel.x >= intrinsic.width - 1 || pixel.y < 0 || pixel.y >= input.height - 1) throw new RuntimeException("Adjust test setup, bad observation"); set.add(pixel, i); } return true; } @Override public CalibrationObservation getDetectedPoints() { return set; } @Override public List<Point2D_F64> getLayout() { return layout; } } }