/* * Copyright (c) 2011-2016, Peter Abeles. All Rights Reserved. * * This file is part of BoofCV (http://boofcv.org). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package boofcv.alg.fiducial.square; import boofcv.alg.distort.LensDistortionNarrowFOV; import boofcv.alg.distort.radtan.LensDistortionRadialTangential; import boofcv.alg.geo.PerspectiveOps; import boofcv.alg.geo.WorldToCameraToPixel; import boofcv.struct.calib.CameraPinholeRadial; import boofcv.struct.distort.Point2Transform2_F64; import georegression.geometry.ConvertRotation3D_F64; import georegression.struct.EulerType; import georegression.struct.point.Point2D_F64; import georegression.struct.point.Point3D_F64; import georegression.struct.se.Se3_F64; import georegression.struct.shapes.Quadrilateral_F64; import georegression.transform.se.SePointOps_F64; import org.ejml.ops.MatrixFeatures; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** * @author Peter Abeles */ public class TestQuadPoseEstimator { private static LensDistortionNarrowFOV createDistortion() { CameraPinholeRadial intrinsic = new CameraPinholeRadial(500,550,0,400,300,800,600).fsetRadial(0.15,0.05); return new LensDistortionRadialTangential(intrinsic); } @Test public void basicTest() { LensDistortionNarrowFOV distortion = createDistortion(); Se3_F64 expectedW2C = new Se3_F64(); expectedW2C.T.set(0.1,-0.05,4); ConvertRotation3D_F64.eulerToMatrix(EulerType.XYZ,0.03,0,0,expectedW2C.R); Quadrilateral_F64 quadPlane = new Quadrilateral_F64(-0.5,0.5,0.5,0.5,0.5,-0.5,-0.5,-0.5); Quadrilateral_F64 quadViewed = new Quadrilateral_F64(); project(expectedW2C, quadPlane.a, quadViewed.a); project(expectedW2C, quadPlane.b, quadViewed.b); project(expectedW2C, quadPlane.c, quadViewed.c); project(expectedW2C, quadPlane.d, quadViewed.d); QuadPoseEstimator alg = new QuadPoseEstimator(1e-8,200); alg.setFiducial(-0.5,0.5,0.5,0.5,0.5,-0.5,-0.5,-0.5); alg.setLensDistoriton(distortion); assertTrue(alg.process(quadViewed)); Se3_F64 found = alg.getWorldToCamera(); assertTrue(found.T.distance(expectedW2C.T)<1e-6); assertTrue(MatrixFeatures.isIdentical(found.R, expectedW2C.R,1e-6)); } private void project( Se3_F64 worldToCamera, Point2D_F64 p , Point2D_F64 v ) { Point3D_F64 a = new Point3D_F64(p.x,p.y,0); Point3D_F64 b = new Point3D_F64(); SePointOps_F64.transform(worldToCamera,a,b); v.x = b.x/b.z; v.y = b.y/b.z; } @Test public void estimateP3P() { LensDistortionNarrowFOV distortion = createDistortion(); Se3_F64 fiducialToCamera = new Se3_F64(); fiducialToCamera.getT().set(0.2,-0.15,2); ConvertRotation3D_F64.eulerToMatrix(EulerType.XYZ,0.05,0.015,0.001,fiducialToCamera.R); QuadPoseEstimator alg = new QuadPoseEstimator(1e-8,200); alg.setLensDistoriton(distortion); double r = 1.5; alg.setFiducial(r,-r, r,r, -r,r, -r,-r); WorldToCameraToPixel worldToPixel = PerspectiveOps.createWorldToPixel(distortion, fiducialToCamera); alg.listObs.add(worldToPixel.transform(alg.points.get(0).location)); alg.listObs.add(worldToPixel.transform(alg.points.get(1).location)); alg.listObs.add(worldToPixel.transform(alg.points.get(2).location)); alg.listObs.add(worldToPixel.transform(alg.points.get(3).location)); Point2Transform2_F64 pixelToNorm = distortion.undistort_F64(true, false); for (int i = 0; i < 4; i++) { Point2D_F64 pixel = alg.listObs.get(i); pixelToNorm.compute(pixel.x,pixel.y,alg.points.get(i).observation); } for (int i = 0; i < 4; i++) { alg.bestError = Double.MAX_VALUE; alg.estimateP3P(i); assertEquals(0,alg.bestError,1e-6); assertTrue(alg.bestPose.T.distance(fiducialToCamera.T)<1e-6); assertTrue(MatrixFeatures.isIdentical(alg.bestPose.R, fiducialToCamera.R,1e-6)); } } @Test public void computeErrors() { LensDistortionNarrowFOV distortion = createDistortion(); Se3_F64 fiducialToCamera = new Se3_F64(); fiducialToCamera.getT().set(0.2,-0.15,2); ConvertRotation3D_F64.eulerToMatrix(EulerType.XYZ,0.05, 0.015, 0.001, fiducialToCamera.R); QuadPoseEstimator alg = new QuadPoseEstimator(1e-8,200); alg.setLensDistoriton(distortion); double r = 1.5; alg.setFiducial(r,-r, r,r, -r,r, -r,-r); WorldToCameraToPixel worldToPixel = PerspectiveOps.createWorldToPixel(distortion, fiducialToCamera); for (int i = 0; i < 4; i++) { Point3D_F64 X = alg.points.get(i).location; alg.listObs.add(worldToPixel.transform(X)); } // perfect assertEquals(0,alg.computeErrors(fiducialToCamera),1e-8); // now with known errors for (int i = 0; i < 4; i++) { alg.listObs.get(i).x += 1.5; assertEquals(1.5,alg.computeErrors(fiducialToCamera),1e-8); alg.listObs.get(i).x -= 1.5; } } }