boofcv.struct.calib.CameraUniversalOmni Maven / Gradle / Ivy
Show all versions of boofcv-geo Show documentation
/*
* Copyright (c) 2023, 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.struct.calib;
import org.ejml.FancyPrint;
import static boofcv.struct.calib.CameraPinholeBrown.toStringArray;
/**
* Camera model for omnidirectional single viewpoint sensors [1]. Designed to work with parabolic,
* hyperbolic, wide-angle, and spherical sensors. The FOV that this model can describe is dependent
* on the mirror parameter ξ. See [1] for details, but for example ξ=0 is a pinhole camera,
* ξ=1 can describe fisheye cameras, but a value larger than 1 is limited to 180 degrees due to
* multiple points on the unit sphere intersecting the same projection line. This is the same model as
* {@link CameraPinholeBrown} except that there is a change in reference frame which allows it to model wider FOV.
*
* Forward Projection
*
* - Given a 3D point X=(x,y,z) in camera (mirror) coordinates
* - Project onto unit sphere Xs=X/||X||
* - Change reference frame X'=(x',y',z') = (xs,ys,zs + ξ)
* - Compute normalized image coordinates (u,v)=(x'/z', y'/z')
* - Apply radial and tangential distortion (see below)
* - Convert into pixels p = K*distort([u;v])
*
*
*
* Camera Projection
* [ fx skew cx ]
* K = [ 0 fy cy ]
* [ 0 0 1 ]
*
*
*
* Radial and Tangential Distortion:
* xd = xn + xn[k1 r2 + ... + kn r2n]
* dxu = [ 2t1 u v + t2(r2 + 2u2)]
* dxv = [ t1(r2 + 2v2) + 2 t2 u v]
*
* r2 = u2 + v2
* where xd is the distorted normalized image coordinates, xn=(u,v) is
* undistorted normalized image coordinates.
*
*
* NOTE: The only difference from [1] is that skew is used instead of fx*alpha.
*
* [1] Christopher Mei, and Patrick Rives. "Single view point omnidirectional camera calibration
* from planar grids." ICRA 2007.
*
*
* @author Peter Abeles
*/
@SuppressWarnings({"NullAway.Init"})
public class CameraUniversalOmni extends CameraPinhole {
/** Mirror offset distance. ξ */
public double mirrorOffset;
/** radial distortion parameters: k1,...,kn */
public double[] radial;
/** tangential distortion parameters */
public double t1, t2;
/**
* Constructor for specifying number of radial distortion
*
* @param numRadial Number of radial distortion parameters
*/
public CameraUniversalOmni( int numRadial ) {
this.radial = new double[numRadial];
}
/**
* Copy constructor
*
* @param original Model which is to be copied
*/
public CameraUniversalOmni( CameraUniversalOmni original ) {
setTo(original);
}
public CameraUniversalOmni fsetMirror( double mirrorOffset ) {
this.mirrorOffset = mirrorOffset;
return this;
}
public CameraUniversalOmni fsetRadial( double... radial ) {
this.radial = radial.clone();
return this;
}
public CameraUniversalOmni fsetTangential( double t1, double t2 ) {
this.t1 = t1;
this.t2 = t2;
return this;
}
/**
* Assigns this model to be identical to the passed in model
*
* @param original Model which is to be copied
*/
public void setTo( CameraUniversalOmni original ) {
super.setTo(original);
this.mirrorOffset = original.mirrorOffset;
if (radial.length != original.radial.length)
radial = new double[original.radial.length];
System.arraycopy(original.radial, 0, radial, 0, radial.length);
this.t1 = original.t1;
this.t2 = original.t2;
}
@Override
public T createLike() {
return (T)new CameraUniversalOmni(radial.length);
}
public double[] getRadial() {
return radial;
}
public void setRadial( double[] radial ) {
this.radial = radial;
}
public double getT1() {
return t1;
}
public void setT1( double t1 ) {
this.t1 = t1;
}
public double getT2() {
return t2;
}
public void setT2( double t2 ) {
this.t2 = t2;
}
public double getMirrorOffset() {
return mirrorOffset;
}
public void setMirrorOffset( double mirrorOffset ) {
this.mirrorOffset = mirrorOffset;
}
@Override public String toString() {
FancyPrint fp = new FancyPrint();
String txt = "CameraUniversalOmni{" +
"width=" + width +
", height=" + height +
", fx=" + fx +
", fy=" + fy +
", skew=" + skew +
", cx=" + cx +
", cy=" + cy +
", mirrorOffset=" + fp.s(mirrorOffset);
txt += toStringArray(fp, "r", radial);
if (t1 != 0 || t2 != 0) {
txt += ", t1=" + fp.s(t1) + " t2=" + fp.s(t2);
}
txt += '}';
return txt;
}
@Override
public void print() {
super.print();
if (radial != null) {
for (int i = 0; i < radial.length; i++) {
System.out.printf("radial[%d] = %6.2e\n", i, radial[i]);
}
} else {
System.out.println("No radial");
}
if (t1 != 0 && t2 != 0)
System.out.printf("tangential = ( %6.2e , %6.2e)\n", t1, t2);
else {
System.out.println("No tangential");
}
System.out.printf("mirror offset = %7.3f", mirrorOffset);
}
}