Difference between revisions of "Example Calibrate Planar Mono"

From BoofCV
Jump to navigationJump to search
(Created page)
 
m
 
(29 intermediate revisions by the same user not shown)
Line 1: Line 1:
= Monocular Camera Calibration with Planar Targets =
<center>
<center>
<gallery caption="Example input and output Images" heights=300 widths=610 >
<gallery heights=300 widths=610 >
Image:Calib_mono.jpg|Calibrated square grid target.  Left uncalibrated.  Right calibrated with lens distortion removed.  Numbers are super imposed on the image to show calibration points.
Image:Calib_mono.jpg|Left uncalibrated.  Right calibrated with lens distortion removed.  Numbers are super imposed on the image to show calibration points.
</gallery>
</gallery>
</center>
</center>


Calibrating a monocular (single) camera is the process of learning its intrinsic camera parameters and removing lens distortion.  This example demonstrates how to use multiple pictures of a planar calibration target.  Both the square grid and chessboard patterns are supported by this example.  For a full description of the calibration process and instruction on how to do it yourself see the tutorial linked to below.
This example demonstrates how to use a high level calibration class that automatically detects calibration targets as viewed from a single (monocular) camera in a set of images.  After processing the images the intrinsic camera parameters and lens distortion are saved to an XML file.  Both the square grid and chessboard patterns are supported by this example.  For a full description of the calibration process and instruction on how to do it yourself see the tutorial linked to below.


Example File: [https://github.com/lessthanoptimal/BoofCV/blob/master/examples/src/boofcv/examples/ExampleBinaryImage.java ExampleBinaryImage.java]
Example File: [https://github.com/lessthanoptimal/BoofCV/blob/v1.1.0/examples/src/main/java/boofcv/examples/calibration/ExampleCalibrateMonocular.java ExampleCalibrateMonocular.java]


Calibration Tutorial: [[Tutorial_Camera_Calibration|Tutorial Here]]
Calibration Tutorial: [[Tutorial_Camera_Calibration|Wikipage]]


Concepts:
Concepts:
Line 17: Line 15:
* Lens distortion
* Lens distortion
* Intrinsic parameters
* Intrinsic parameters
Relevant Applets:
* [[Applet_Binary_Operations| Binary Operations]]
* [[Applet_Binary_Segmentation| Binary Segmentation]]


Related Examples:
Related Examples:
Line 29: Line 23:
<syntaxhighlight lang="java">
<syntaxhighlight lang="java">
/**
/**
* <p>
  * Example of how to calibrate a single (monocular) camera using a high level interface. Depending on the calibration
  * Example of how to calibrate a single (monocular) camera using a planar calibration grid. Two types of calibration
  * target detector and target type, the entire target might need to be visible in the image. All camera images
  * targets can be processed by BoofCV, square grids and chessboard. Square grid is composed of a set of square
  * should be in focus and that target evenly spread through out the images. In particular the edges of the image
  * grids and chessboard is a classic chessboard pattern. In general better quality results have been found using
  * should be covered.
  * the chessboard pattern, but parameter tuning is required to achieve optimal performance.
* </p>
  *
  *
  * <p>
  * After processing both intrinsic camera parameters and lens distortion are estimated. Square grid and chessboard
* All the image processing and calibration is taken care of inside of {@link CalibrateMonoPlanar}. The code below
  * targets are demonstrated by this example. See calibration tutorial for a discussion of different target types
  * images of calibration targets are loaded and pass in as inputs and the found calibration is saved to an XML file.
  * and how to collect good calibration images.
  * See in code comments for tuning and implementation issues.
* </p>
  *
  *
  * @see ExampleCalibrateStereoPlanar
  * All the image processing and calibration is taken care of inside of {@link CalibrateMonoPlanar}. The code below
* loads calibration images as inputs, calibrates, and saves results to an XML file. See in code comments for tuning
* and implementation issues.
  *
  *
  * @author Peter Abeles
  * @author Peter Abeles
* @see CalibrateMonoPlanar
  */
  */
public class ExampleCalibrateMonocularPlanar {
public class ExampleCalibrateMonocular {
public static void main( String[] args ) {
DetectSingleFiducialCalibration detector;
List<String> images;


// Detects the target and calibration point inside the target
// Regular Circle Example
PlanarCalibrationDetector detector;
// detector = FactoryFiducialCalibration.circleRegularGrid(null, new ConfigGridDimen(/*numRows*/ 8, /*numCols*/ 10, 1.5, 2.5));
// images = UtilIO.listByPrefix(UtilIO.pathExample("calibration/mono/Sony_DSC-HX5V_CircleRegular"),"image", null);


// Description of the target's physical dimension
// Hexagonal Circle Example
PlanarCalibrationTarget target;
// detector = FactoryFiducialCalibration.circleHexagonalGrid(null, new ConfigGridDimen(/*numRows*/ 24, /*numCols*/ 28, 1, 1.2));
// images = UtilIO.listByPrefix(UtilIO.pathExample("calibration/mono/Sony_DSC-HX5V_CircleHexagonal"),"image", null);


// List of calibration images
// Square Grid example
List<String> images;
// detector = FactoryFiducialCalibration.squareGrid(null, new ConfigGridDimen(/*numRows*/ 4, /*numCols*/ 3, 30, 30));
// images = UtilIO.listByPrefix(UtilIO.pathExample("calibration/stereo/Bumblebee2_Square"),"left", null);


// Most computer images are in a left handed coordinate system. This can cause problems when algorithms
// ECoCheck Example
// that assume a right handed coordinate system are used later on. To address this issue the image coordinate
// detector = new MultiToSingleFiducialCalibration(FactoryFiducialCalibration.
// system is changed to a right handed one if true is passed in for the second parameter.
// ecocheck(null, ConfigECoCheckMarkers.
boolean isLeftHanded;
// singleShape(/*numRows*/ 9, /*numCols*/ 7, /*num markers*/ 1, /* square size */ 30)));
// images = UtilIO.listByPrefix(UtilIO.pathExample("calibration/stereo/Zed_ecocheck"), "left", null);


/**
// Chessboard Example
* Images from Zhang's website.  Square grid pattern.
detector = FactoryFiducialCalibration.chessboardX(null,
*/
new ConfigGridDimen(/*numRows*/ 7,/*numCols*/ 5,/*shapeSize*/ 30));
private void setupZhang99() {
images = UtilIO.listByPrefix(UtilIO.pathExample("calibration/stereo/Bumblebee2_Chess"), "left", null);
// Use the wrapper below for square grid targets.
detector = new WrapPlanarGridTarget(8,8);


// physical description
// Declare and setup the calibration algorithm
target = FactoryPlanarCalibrationTarget.gridSquare(8, 8, 0.5, 7.0 / 18.0);
var calibrator = new CalibrateMonoPlanar();
 
// load image list
String directory = "../data/evaluation/calibration/mono/PULNiX_CCD_6mm_Zhang";
images = BoofMiscOps.directoryList(directory,"CalibIm");
 
// standard image format
isLeftHanded = true;
}
 
/**
* Images collected from a Bumblee Bee stereo camera.  Large amounts of radial distortion. Chessboard pattern.
*/
private void setupBumbleBee() {
// Use the wrapper below for chessboard targets.  The last parameter adjusts the size of the corner detection
// region.  TUNE THIS PARAMETER FOR OPTIMAL ACCURACY!
detector = new WrapPlanarChessTarget(3,4,6);
 
// physical description
target = FactoryPlanarCalibrationTarget.gridChess(3, 4, 30);


// load image list
// tell it type type of target and which intrinsic parameters to estimate
String directory = "../data/evaluation/calibration/stereo/Bumblebee2_Chess";
calibrator.configurePinhole(
images = BoofMiscOps.directoryList(directory,"left");
/*assumeZeroSkew*/ true,
/*numRadialParam*/ 2,
/*includeTangential*/ false);


// standard image format
var usedImages = new ArrayList<String>();
isLeftHanded = true;
for (String n : images) {
}
BufferedImage input = UtilImageIO.loadImageNotNull(n);
GrayF32 image = ConvertBufferedImage.convertFrom(input, (GrayF32)null);
if (detector.process(image)) {
// Need to tell it the image shape and the layout once
if (usedImages.isEmpty())
calibrator.initialize(image.getWidth(), image.getHeight(), List.of(detector.getLayout()));


/**
calibrator.addImage(detector.getDetectedPoints().copy());
* Process calibration images, compute intrinsic parameters, save to a file
usedImages.add(n);
*/
} else {
public void process() {
System.err.println("Failed to detect target in " + n);
 
// Declare and setup the calibration algorithm
CalibrateMonoPlanar calibrationAlg = new CalibrateMonoPlanar(detector,isLeftHanded);
 
// tell it type type of target and which parameters to estimate
calibrationAlg.configure(target, true, 2);
 
for( String n : images ) {
BufferedImage input = UtilImageIO.loadImage(n);
if( n != null ) {
ImageFloat32 image = ConvertBufferedImage.convertFrom(input,(ImageFloat32)null);
calibrationAlg.addImage(image);
}
}
}
}
// process and compute intrinsic parameters
// process and compute intrinsic parameters
IntrinsicParameters intrinsic = calibrationAlg.process();
CameraPinholeBrown intrinsic = calibrator.process();


// save results to a file and print out
// save results to a file and print out
BoofMiscOps.saveXML(intrinsic, "intrinsic.xml");
CalibrationIO.save(intrinsic, "intrinsic.yaml");


calibrationAlg.printStatistics();
System.out.println(calibrator.computeQualityText(usedImages));
System.out.println();
System.out.println();
System.out.println("--- Intrinsic Parameters ---");
System.out.println("--- Intrinsic Parameters ---");
System.out.println();
System.out.println();
intrinsic.print();
intrinsic.print();
}
public static void main( String args[] ) {
ExampleCalibrateMonocularPlanar alg = new ExampleCalibrateMonocularPlanar();
// which target should it process
// alg.setupZhang99();
alg.setupBumbleBee();
// compute and save results
alg.process();
}
}
}
}
</syntaxhighlight>
</syntaxhighlight>

Latest revision as of 18:05, 9 September 2023

This example demonstrates how to use a high level calibration class that automatically detects calibration targets as viewed from a single (monocular) camera in a set of images. After processing the images the intrinsic camera parameters and lens distortion are saved to an XML file. Both the square grid and chessboard patterns are supported by this example. For a full description of the calibration process and instruction on how to do it yourself see the tutorial linked to below.

Example File: ExampleCalibrateMonocular.java

Calibration Tutorial: Wikipage

Concepts:

  • Camera calibration
  • Lens distortion
  • Intrinsic parameters

Related Examples:

Example Code

/**
 * Example of how to calibrate a single (monocular) camera using a high level interface. Depending on the calibration
 * target detector and target type, the entire target might need to be visible in the image. All camera images
 * should be in focus and that target evenly spread through out the images. In particular the edges of the image
 * should be covered.
 *
 * After processing both intrinsic camera parameters and lens distortion are estimated. Square grid and chessboard
 * targets are demonstrated by this example. See calibration tutorial for a discussion of different target types
 * and how to collect good calibration images.
 *
 * All the image processing and calibration is taken care of inside of {@link CalibrateMonoPlanar}. The code below
 * loads calibration images as inputs, calibrates, and saves results to an XML file. See in code comments for tuning
 * and implementation issues.
 *
 * @author Peter Abeles
 * @see CalibrateMonoPlanar
 */
public class ExampleCalibrateMonocular {
	public static void main( String[] args ) {
		DetectSingleFiducialCalibration detector;
		List<String> images;

		// Regular Circle Example
//		detector = FactoryFiducialCalibration.circleRegularGrid(null, new ConfigGridDimen(/*numRows*/ 8, /*numCols*/ 10, 1.5, 2.5));
//		images = UtilIO.listByPrefix(UtilIO.pathExample("calibration/mono/Sony_DSC-HX5V_CircleRegular"),"image", null);

		// Hexagonal Circle Example
//		detector = FactoryFiducialCalibration.circleHexagonalGrid(null, new ConfigGridDimen(/*numRows*/ 24, /*numCols*/ 28, 1, 1.2));
//		images = UtilIO.listByPrefix(UtilIO.pathExample("calibration/mono/Sony_DSC-HX5V_CircleHexagonal"),"image", null);

		// Square Grid example
//		detector = FactoryFiducialCalibration.squareGrid(null, new ConfigGridDimen(/*numRows*/ 4, /*numCols*/ 3, 30, 30));
//		images = UtilIO.listByPrefix(UtilIO.pathExample("calibration/stereo/Bumblebee2_Square"),"left", null);

		// ECoCheck Example
//		detector = new MultiToSingleFiducialCalibration(FactoryFiducialCalibration.
//				ecocheck(null, ConfigECoCheckMarkers.
//						singleShape(/*numRows*/ 9, /*numCols*/ 7, /*num markers*/ 1, /* square size */ 30)));
//		images = UtilIO.listByPrefix(UtilIO.pathExample("calibration/stereo/Zed_ecocheck"), "left", null);

		// Chessboard Example
		detector = FactoryFiducialCalibration.chessboardX(null,
				new ConfigGridDimen(/*numRows*/ 7,/*numCols*/ 5,/*shapeSize*/ 30));
		images = UtilIO.listByPrefix(UtilIO.pathExample("calibration/stereo/Bumblebee2_Chess"), "left", null);

		// Declare and setup the calibration algorithm
		var calibrator = new CalibrateMonoPlanar();

		// tell it type type of target and which intrinsic parameters to estimate
		calibrator.configurePinhole(
				/*assumeZeroSkew*/ true,
				/*numRadialParam*/ 2,
				/*includeTangential*/ false);

		var usedImages = new ArrayList<String>();
		for (String n : images) {
			BufferedImage input = UtilImageIO.loadImageNotNull(n);
			GrayF32 image = ConvertBufferedImage.convertFrom(input, (GrayF32)null);
			if (detector.process(image)) {
				// Need to tell it the image shape and the layout once
				if (usedImages.isEmpty())
					calibrator.initialize(image.getWidth(), image.getHeight(), List.of(detector.getLayout()));

				calibrator.addImage(detector.getDetectedPoints().copy());
				usedImages.add(n);
			} else {
				System.err.println("Failed to detect target in " + n);
			}
		}
		// process and compute intrinsic parameters
		CameraPinholeBrown intrinsic = calibrator.process();

		// save results to a file and print out
		CalibrationIO.save(intrinsic, "intrinsic.yaml");

		System.out.println(calibrator.computeQualityText(usedImages));
		System.out.println();
		System.out.println("--- Intrinsic Parameters ---");
		System.out.println();
		intrinsic.print();
	}
}