docs: boolean projectiveToMetric() {
  
  		// homography from projective to metric
  		DMatrixRMaj H = new DMatrixRMaj(4,4);
  		listPinhole.clear();
  
  		if( manualFocalLength <= 0 ) {
  			// Estimate calibration parameters
  			SelfCalibrationLinearDualQuadratic selfcalib = new SelfCalibrationLinearDualQuadratic(1.0);
  			selfcalib.addCameraMatrix(P1);
  			selfcalib.addCameraMatrix(P2);
  			selfcalib.addCameraMatrix(P3);
  
  			GeometricResult result = selfcalib.solve();
  			if (GeometricResult.SOLVE_FAILED != result && selfcalib.getSolutions().size() == 3) {
  				for (int i = 0; i < 3; i++) {
  					SelfCalibrationLinearDualQuadratic.Intrinsic c = selfcalib.getSolutions().get(i);
  					CameraPinhole p = new CameraPinhole(c.fx, c.fy, 0, 0, 0, width, height);
  					listPinhole.add(p);
  				}
  			} else {
  				// TODO Handle this better
  				System.out.println("Self calibration failed!");
  				for (int i = 0; i < 3; i++) {
  					CameraPinhole p = new CameraPinhole(width / 2, width / 2, 0, 0, 0, width, height);
  					listPinhole.add(p);
  				}
  			}
  			// convert camera matrix from projective to metric
  			if( !MultiViewOps.absoluteQuadraticToH(selfcalib.getQ(),H) ) {
  				if( verbose != null ) {
  					verbose.println("Projective to metric failed");
  				}
  				return false;
  			}
  		} else {
  			// Assume all cameras have a fixed known focal length
  			EstimatePlaneAtInfinityGivenK estimateV = new EstimatePlaneAtInfinityGivenK();
  			estimateV.setCamera1(manualFocalLength,manualFocalLength,0,0,0);
  			estimateV.setCamera2(manualFocalLength,manualFocalLength,0,0,0);
  
  			Vector3D_F64 v = new Vector3D_F64(); // plane at infinity
  			if( !estimateV.estimatePlaneAtInfinity(P2,v))
  				throw new RuntimeException("Failed!");
  
  			DMatrixRMaj K = PerspectiveOps.pinholeToMatrix(manualFocalLength,manualFocalLength,0,0,0);
  			MultiViewOps.createProjectiveToMetric(K,v.x,v.y,v.z,1,H);
  
  			for (int i = 0; i < 3; i++) {
  				CameraPinhole p = new CameraPinhole(manualFocalLength,manualFocalLength, 0, 0, 0, width, height);
  				listPinhole.add(p);
  			}
  		}
  
  		if( verbose != null ) {
  			for (int i = 0; i < 3; i++) {
  				CameraPinhole r = listPinhole.get(i);
  				verbose.println("fx=" + r.fx + " fy=" + r.fy + " skew=" + r.skew);
  			}
  			verbose.println("Projective to metric");
  		}
  
  		DMatrixRMaj K = new DMatrixRMaj(3,3);
  
  		// ignore K since we already have that
  		MultiViewOps.projectiveToMetric(P1,H,worldToView.get(0),K);
  		MultiViewOps.projectiveToMetric(P2,H,worldToView.get(1),K);
  		MultiViewOps.projectiveToMetric(P3,H,worldToView.get(2),K);
  
  		// scale is arbitrary. Set max translation to 1
  		double maxT = 0;
  		for( Se3_F64 p : worldToView ) {
  			maxT = Math.max(maxT,p.T.norm());
  		}
  		for( Se3_F64 p : worldToView ) {
  			p.T.scale(1.0/maxT);
  			if( verbose != null ) {
  				verbose.println(p);
  			}
  		}
  
  		return true;
  	}