Example Fiducial Random Dots

From BoofCV

Uchiya Markers are composed of random dots and work by computing a hashing function based on the relative orientation of neighboring dots. One advantage to these markers is that they are resistant to conclusion. Many of the dots can be covered and the ID and pose of the marker can still be recovered! Square based markers become unrecognizable if the black square is blocked or damaged. BoofCV also provides tools for generating random marker PDF documents that can be printed.

Example File: ExampleFiducialRandomDots.java

Concepts:

  • Fiducial Markers
  • Dots
  • Pose Estimation

Relevant Videos:

Related Tutorials/Example Code:


Example Code

/**
 * Random dot markers are exactly what their name implies. Each marker is a set of random dots that the tracker
 * learns to identify using hash codes computed from geometric invariants which describe the relationships between
 * the dots. These markers are based off of Uchiya Markers.
 *
 * @author Peter Abeles
 */
public class ExampleFiducialRandomDots {
	public static void main(String[] args) {
		// The definitions file specifies where dots are on each marker and other bits of meta data
		RandomDotDefinition defs = FiducialIO.loadRandomDotYaml(
				UtilIO.fileExample("fiducial/random_dots/descriptions.yaml"));

		// The only parameter that you have to set is markerLength. It's used to compute bounding
		// boxes and similar. If you don't know what the width is just set it to 1.0
		ConfigUchiyaMarker config = new ConfigUchiyaMarker();
		config.markerLength = defs.markerWidth;

		Uchiya_to_FiducialDetector<GrayU8> detector = FactoryFiducial.randomDots(config, GrayU8.class);

		// Load / learn all the markers. This can take a few seconds if there are a lot of markers
		for( List<Point2D_F64> marker : defs.markers ) {
			detector.addMarker(marker);
		}

		// If you want 3D pose information then the camera need sto be calibrated. If you don't provide
		// this information then just things like the bounding box will be returned
		CameraPinholeBrown intrinsic = CalibrationIO.load(
				UtilIO.fileExample("fiducial/random_dots/intrinsic.yaml"));
		detector.setLensDistortion(LensDistortionFactory.narrow(intrinsic),intrinsic.width,intrinsic.height);

		// It's now ready to start processing images. Let's load an image
		BufferedImage image = UtilImageIO.loadImage(UtilIO.pathExample("fiducial/random_dots/image02.jpg"));
		GrayU8 gray = ConvertBufferedImage.convertFrom(image,false, ImageType.SB_U8);

		detector.detect(gray);

		// Time to visualize the results
		Graphics2D g2 = image.createGraphics();
		Se3_F64 targetToSensor = new Se3_F64();
		Polygon2D_F64 bounds = new Polygon2D_F64();
		Point2D_F64 center = new Point2D_F64();
		for (int i = 0; i < detector.totalFound(); i++) {
			detector.getBounds(i, bounds);
			detector.getCenter(i, center);

			g2.setColor(new Color(50,50,255));
			g2.setStroke(new BasicStroke(10));
			VisualizeShapes.drawPolygon(bounds,true,1.0,g2);
			VisualizeFiducial.drawLabel(center, "" + detector.getId(i), g2);

			System.out.println("Target ID = "+detector.getId(i));

			if( detector.is3D() ) {
				detector.getFiducialToCamera(i, targetToSensor);
				VisualizeFiducial.drawCube(targetToSensor, intrinsic, detector.getWidth(i), 3, g2);
			}
		}
		ShowImages.showWindow(image,"Random Dot Markers",true);
	}
}