All Downloads are FREE. Search and download functionalities are using the official Maven repository.

boofcv.factory.fiducial.FactoryFiducial Maven / Gradle / Ivy

/*
 * 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.factory.fiducial;

import boofcv.abst.fiducial.*;
import boofcv.abst.fiducial.calib.*;
import boofcv.abst.filter.binary.InputToBinary;
import boofcv.alg.feature.describe.llah.LlahHasher;
import boofcv.alg.feature.describe.llah.LlahOperations;
import boofcv.alg.fiducial.aztec.AztecCode;
import boofcv.alg.fiducial.calib.ecocheck.ECoCheckDetector;
import boofcv.alg.fiducial.calib.ecocheck.ECoCheckUtils;
import boofcv.alg.fiducial.dots.UchiyaMarkerImageTracker;
import boofcv.alg.fiducial.dots.UchiyaMarkerTracker;
import boofcv.alg.fiducial.qrcode.QrCodePositionPatternDetector;
import boofcv.alg.fiducial.square.DetectFiducialSquareBinary;
import boofcv.alg.fiducial.square.DetectFiducialSquareHamming;
import boofcv.alg.fiducial.square.DetectFiducialSquareImage;
import boofcv.alg.shapes.ellipse.BinaryEllipseDetectorPixel;
import boofcv.alg.shapes.ellipse.EdgeIntensityEllipse;
import boofcv.alg.shapes.polygon.DetectPolygonBinaryGrayRefine;
import boofcv.factory.filter.binary.ConfigThreshold;
import boofcv.factory.filter.binary.FactoryThresholdBinary;
import boofcv.factory.filter.binary.ThresholdType;
import boofcv.factory.geo.ConfigHomography;
import boofcv.factory.geo.FactoryMultiViewRobust;
import boofcv.factory.shape.FactoryShapeDetector;
import boofcv.struct.geo.AssociatedPair;
import boofcv.struct.image.ImageGray;
import boofcv.struct.image.ImageType;
import georegression.struct.homography.Homography2D_F64;
import org.ddogleg.fitting.modelset.ransac.Ransac;
import org.jetbrains.annotations.Nullable;

/**
 * Factory for creating fiducial detectors which implement {@link FiducialDetector}.
 *
 * @author Peter Abeles
 */
public class FactoryFiducial {

	/**
	 * Detector for square binary based fiducials.
	 *
	 * @param configFiducial Description of the fiducial. Can't be null.
	 * @param configThreshold Threshold for binary image. null for default.
	 * @param imageType Type of image it's processing
	 * @return FiducialDetector
	 * @see DetectFiducialSquareBinary DetectFiducialSquareBinary for a description of this fiducial type.
	 */
	public static >
	SquareBinary_to_FiducialDetector squareBinary( ConfigFiducialBinary configFiducial,
													  @Nullable ConfigThreshold configThreshold,
													  Class imageType ) {

		if (configThreshold == null) {
			configThreshold = ConfigThreshold.local(ThresholdType.LOCAL_MEAN, 21);
		}

		configFiducial.checkValidity();

		final InputToBinary binary = FactoryThresholdBinary.threshold(configThreshold, imageType);
		final DetectPolygonBinaryGrayRefine squareDetector = FactoryShapeDetector.
				polygon(configFiducial.squareDetector, imageType);

		final DetectFiducialSquareBinary alg =
				new DetectFiducialSquareBinary<>(configFiducial.gridWidth,
						configFiducial.borderWidthFraction, configFiducial.minimumBlackBorderFraction,
						binary, squareDetector, imageType);
		alg.setAmbiguityThreshold(configFiducial.ambiguousThreshold);
		return new SquareBinary_to_FiducialDetector<>(alg, configFiducial.targetWidth);
	}

	/**
	 * Detector for square binary based fiducials.
	 *
	 * @param configMarkers Describes the markers it will detect.
	 * @param configDetector (Optional) Describes how it will detect the markers.
	 * @param imageType Type of image it's processing
	 * @return FiducialDetector
	 * @see DetectFiducialSquareBinary DetectFiducialSquareBinary for a description of this fiducial type.
	 */
	public static >
	SquareHamming_to_FiducialDetector squareHamming( ConfigHammingMarker configMarkers,
														@Nullable ConfigFiducialHammingDetector configDetector,
														Class imageType ) {
		if (configDetector == null) {
			configDetector = new ConfigFiducialHammingDetector();
		}
		configMarkers.checkValidity();
		configDetector.checkValidity();

		final InputToBinary binary = FactoryThresholdBinary.threshold(configDetector.configThreshold, imageType);
		final DetectPolygonBinaryGrayRefine squareDetector = FactoryShapeDetector.
				polygon(configDetector.squareDetector, imageType);

		final var alg = new DetectFiducialSquareHamming<>(configMarkers, configDetector.minimumBlackBorderFraction,
				binary, squareDetector, imageType);
		alg.setAmbiguousPenaltyFrac(configDetector.ambiguousPenaltyFrac);
		return new SquareHamming_to_FiducialDetector<>(alg);
	}

	/**
	 * 

Detector for square image based fiducials.

* *

For this fiducial to work images need to be added to it. Which is why {@link SquareImage_to_FiducialDetector} * is returned instead of the more generic {@link FiducialDetector}.

* * @param configFiducial Description of the fiducial. null for default. * @param configThreshold Threshold for binary image. null for default. * @param imageType Type of image it's processing * @return FiducialDetector * @see DetectFiducialSquareImage DetectFiducialSquareImage for a description of this fiducial type. */ public static > SquareImage_to_FiducialDetector squareImage( @Nullable ConfigFiducialImage configFiducial, @Nullable ConfigThreshold configThreshold, Class imageType ) { if (configFiducial == null) { configFiducial = new ConfigFiducialImage(); } if (configThreshold == null) { configThreshold = ConfigThreshold.local(ThresholdType.LOCAL_MEAN, 21); } configFiducial.squareDetector.detector.clockwise = false; InputToBinary binary = FactoryThresholdBinary.threshold(configThreshold, imageType); DetectPolygonBinaryGrayRefine squareDetector = FactoryShapeDetector.polygon(configFiducial.squareDetector, imageType); DetectFiducialSquareImage alg = new DetectFiducialSquareImage<>(binary, squareDetector, configFiducial.borderWidthFraction, configFiducial.minimumBlackBorderFraction, configFiducial.maxErrorFraction, imageType); return new SquareImage_to_FiducialDetector<>(alg); } /** * Chessboard detector based on binary images. Fast but not as robust as the X-Corner method. Not recommended * for fisheye images. * * @param config Description of the chessboard. * @param imageType Type of image it's processing * @return FiducialDetector */ public static > CalibrationFiducialDetector calibChessboardB( @Nullable ConfigChessboardBinary config, ConfigGridDimen dimen, Class imageType ) { return new CalibrationFiducialDetector<>(config, dimen, imageType); } /** * Chessboard detector which searches for x-corners. Very robust but is about 2x to 3x slower on large images * than the binary method. * * @param config Description of the chessboard. * @param imageType Type of image it's processing * @return FiducialDetector */ public static > CalibrationFiducialDetector calibChessboardX( @Nullable ConfigChessboardX config, ConfigGridDimen dimen, Class imageType ) { return new CalibrationFiducialDetector<>(config, dimen, imageType); } /** * Wrapper around square-grid calibration detector. Refine with lines is set to true automatically. This * isn't being used for calibration, and it's better to use the whole line. * * @param config Description of the chessboard. * @param imageType Type of image it's processing * @return FiducialDetector */ public static > CalibrationFiducialDetector calibSquareGrid( @Nullable ConfigSquareGrid config, ConfigGridDimen configDimen, Class imageType ) { return new CalibrationFiducialDetector<>(config, configDimen, imageType); } public static > CalibrationFiducialDetector calibCircleHexagonalGrid( @Nullable ConfigCircleHexagonalGrid config, ConfigGridDimen configDimen, Class imageType ) { return new CalibrationFiducialDetector<>(config, configDimen, imageType); } public static > CalibrationFiducialDetector calibCircleRegularGrid( @Nullable ConfigCircleRegularGrid config, ConfigGridDimen configDimen, Class imageType ) { return new CalibrationFiducialDetector<>(config, configDimen, imageType); } /** * Returns a QR Code detector * * @param config Configuration * @param imageType type of input image * @return the detector */ public static > QrCodePreciseDetector qrcode( @Nullable ConfigQrCode config, Class imageType ) { if (config == null) config = new ConfigQrCode(); config.checkValidity(); InputToBinary inputToBinary = FactoryThresholdBinary.threshold(config.threshold, imageType); DetectPolygonBinaryGrayRefine squareDetector = FactoryShapeDetector.polygon(config.polygon, imageType); var detectPositionPatterns = new QrCodePositionPatternDetector<>(squareDetector); var detector = new QrCodePreciseDetector<>(inputToBinary, detectPositionPatterns, config.forceEncoding, config.defaultEncoding, false, imageType); detector.getGraphPositionPatterns().setMaxVersionQR(config.versionMaximum); detector.getDecoder().considerTransposed = config.considerTransposed; detector.getDecoder().getDecoder().ignorePaddingBytes = config.ignorePaddingBytes; return detector; } /** * Returns an {@link AztecCode Aztec Code} detector * * @param config Configuration * @param imageType type of input image * @return the detector */ public static > AztecCodePreciseDetector aztec( @Nullable ConfigAztecCode config, Class imageType ) { if (config == null) config = new ConfigAztecCode(); config.checkValidity(); InputToBinary inputToBinary = FactoryThresholdBinary.threshold(config.threshold, imageType); DetectPolygonBinaryGrayRefine squareDetector = FactoryShapeDetector.polygon(config.polygon, imageType); var detector = new AztecCodePreciseDetector<>(inputToBinary, squareDetector, imageType); detector.getDecoder().considerTransposed = config.considerTransposed; detector.getDecoder().maxOrientationError = config.maxOrientationError; return detector; } /** * Returns a Micro QR Code detector * * @param config Configuration * @param imageType type of input image * @return the detector */ public static > MicroQrCodePreciseDetector microqr( @Nullable ConfigMicroQrCode config, Class imageType ) { if (config == null) config = new ConfigMicroQrCode(); config.checkValidity(); InputToBinary inputToBinary = FactoryThresholdBinary.threshold(config.threshold, imageType); DetectPolygonBinaryGrayRefine squareDetector = FactoryShapeDetector.polygon(config.polygon, imageType); var detectPositionPatterns = new QrCodePositionPatternDetector<>(squareDetector); var detector = new MicroQrCodePreciseDetector<>(inputToBinary, detectPositionPatterns, config.forceEncoding, config.defaultEncoding, false, imageType); detector.getDecoder().considerTransposed = config.considerTransposed; detector.getDecoder().getDecoder().ignorePaddingBytes = config.ignorePaddingBytes; return detector; } /** * Creates a new {@link ECoCheckDetector}. This will detect chessboard patterns that have been * encoded with a marker ID and coordinates of every corner. * * @param configDetector Configuration for chessboard detector * @param configMarkers Configuration for what markers it should search for * @return New detector */ public static > ECoCheck_to_FiducialDetector ecocheck( @Nullable ConfigECoCheckDetector configDetector, ConfigECoCheckMarkers configMarkers, Class imageType ) { if (configDetector == null) configDetector = new ConfigECoCheckDetector(); configDetector.checkValidity(); configMarkers.checkValidity(); // Set up the utils, which configures how the targets will be encoded var utils = new ECoCheckUtils(); utils.setParametersFromConfig(configMarkers); utils.fixate(); var alg = new ECoCheckDetector<>(utils, configDetector.chessboard, imageType); return new ECoCheck_to_FiducialDetector<>(alg, configMarkers.markerShapes); } /** * QR Code but with the ability to estimate it's 3D pose using PnP. Implements {@link FiducialDetector}. */ public static > QrCodeDetectorPnP qrcode3D( @Nullable ConfigQrCode config, Class imageType ) { return new QrCodeDetectorPnP<>(qrcode(config, imageType)); } public static > MicroQrCodeDetectorPnP microqr3D( @Nullable ConfigMicroQrCode config, Class imageType ) { return new MicroQrCodeDetectorPnP<>(microqr(config, imageType)); } /** * Creates detector for random dot markers using Uchiya Marker approach. * * @param config Specifies the configuration. If null then default is used * @param imageType Type of input gray scale image * @return The fiducial detector * @see UchiyaMarkerImageTracker */ public static > Uchiya_to_FiducialDetector randomDots( ConfigUchiyaMarker config, Class imageType ) { config.checkValidity(); var ellipseDetector = new BinaryEllipseDetectorPixel(config.contourRule); ellipseDetector.setMaxDistanceFromEllipse(config.maxDistanceFromEllipse); ellipseDetector.setMinimumMinorAxis(config.minimumMinorAxis); ellipseDetector.setMaxMajorToMinorRatio(config.maxMajorToMinorRatio); ellipseDetector.setMinimumContour(config.contourMinimumLength); ellipseDetector.setMaximumContour(config.contourMaximumLength); EdgeIntensityEllipse check = new EdgeIntensityEllipse<>( config.checkEdge.checkRadialDistance, config.checkEdge.numSampleContour, config.checkEdge.minimumEdgeIntensity, imageType); InputToBinary inputToBinary = FactoryThresholdBinary.threshold(config.threshold, imageType); ConfigLlah llah = config.llah; LlahHasher hasher = switch (config.llah.hashType) { case AFFINE -> new LlahHasher.Affine(llah.quantizationK, llah.hashTableSize); case CROSS_RATIO -> new LlahHasher.CrossRatio(llah.quantizationK, llah.hashTableSize); }; LlahOperations ops = new LlahOperations(config.llah.numberOfNeighborsN, config.llah.sizeOfCombinationM, hasher); Ransac ransac = FactoryMultiViewRobust.homographyRansac(new ConfigHomography(false), config.ransac); UchiyaMarkerTracker uchiya = new UchiyaMarkerTracker(ops, ransac); UchiyaMarkerImageTracker tracker = new UchiyaMarkerImageTracker<>(inputToBinary, ellipseDetector, check, uchiya); return new Uchiya_to_FiducialDetector(tracker, config.markerWidth, config.markerHeight, ImageType.single(imageType)); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy