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

boofcv.alg.shapes.polygon.DetectPolygonBinaryGrayRefine Maven / Gradle / Ivy

Go to download

BoofCV is an open source Java library for real-time computer vision and robotics applications.

The newest version!
/*
 * Copyright (c) 2021, 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.alg.shapes.polygon;

import boofcv.alg.filter.binary.ContourPacked;
import boofcv.alg.shapes.edge.EdgeIntensityPolygon;
import boofcv.misc.MovingAverage;
import boofcv.struct.distort.PixelTransform;
import boofcv.struct.image.GrayU8;
import boofcv.struct.image.ImageGray;
import georegression.struct.point.Point2D_F32;
import georegression.struct.point.Point2D_I32;
import georegression.struct.shapes.Polygon2D_F64;
import org.ddogleg.struct.DogArray;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;

/**
 * Detects polygons using contour of blobs in a binary image. The contours can then have their edges refined as a
 * whole or on an individual basis. Lens distortion can be specified. Lens distortion is handled in a sparse way
 * along the contour of shapes.
 *
 * @author Peter Abeles
 */
@SuppressWarnings({"NullAway.Init"})
public class DetectPolygonBinaryGrayRefine> {

	// Detects the polygons using a contour from a binary image
	DetectPolygonFromContour detector;

	AdjustPolygonForThresholdBias adjustForBias;

	// Refines the edges using the contour alone
	private final @Nullable RefinePolygonToContour refineContour;

	// Refines the edges using the gray scale image
	private final @Nullable RefinePolygonToGray refineGray;

	// Used to remove false positives
	private final EdgeIntensityPolygon edgeIntensity;

	private final Polygon2D_F64 work = new Polygon2D_F64();

	// useful for customization
	AdjustBeforeRefineEdge functionAdjust;

	// threshold for pruning after refinement
	double minimumRefineEdgeIntensity;

	// timing for profiler
	MovingAverage milliAdjustBias = new MovingAverage(0.8);

	/**
	 * Configures the polygon detector
	 *
	 * @param detector Fits a polygon to a contour
	 * @param refineContour Refines the polygon produce a better fit against the contour
	 * @param refineGray Refine the edges to the input gray scale image
	 * @param minimumRefineEdgeIntensity Threshold for pruning shapes. Must have this edge intensity. Try 6
	 * @param adjustForThresholdBias Should it adjust contour polygons for the bias caused by thresholding?
	 */
	public DetectPolygonBinaryGrayRefine( DetectPolygonFromContour detector,
										  @Nullable RefinePolygonToContour refineContour,
										  @Nullable RefinePolygonToGray refineGray,
										  double minimumRefineEdgeIntensity,
										  boolean adjustForThresholdBias ) {
		this.detector = detector;
		this.refineContour = refineContour;
		this.refineGray = refineGray;
		this.minimumRefineEdgeIntensity = minimumRefineEdgeIntensity;
		if (adjustForThresholdBias) {
			this.adjustForBias = new AdjustPolygonForThresholdBias();
		}

		this.edgeIntensity = new EdgeIntensityPolygon<>(1, 1.5, 15,
				detector.getInputType());
	}

	/**
	 * Specify a helper used to inject specialized code into the polygon detector
	 */
	public void setHelper( PolygonHelper helper ) {
		detector.setHelper(helper);
	}

	/**
	 * Turn on and off verbose output to standard out
	 */
	public void setVerbose( boolean verbose ) {
		detector.setVerbose(verbose ? System.out : null, null);
	}

	/**
	 * 

Specifies transforms which can be used to change coordinates from distorted to undistorted and the opposite * coordinates. The undistorted image is never explicitly created.

* * @param width Input image width. Used in sanity check only. * @param height Input image height. Used in sanity check only. * @param distToUndist Transform from distorted to undistorted image. * @param undistToDist Transform from undistorted to distorted image. */ public void setLensDistortion( int width, int height, @Nullable PixelTransform distToUndist, @Nullable PixelTransform undistToDist ) { detector.setLensDistortion(width, height, distToUndist, undistToDist); if (refineGray != null) refineGray.setLensDistortion(width, height, distToUndist, undistToDist); edgeIntensity.setTransform(undistToDist); } /** * Discard previously set lens distortion models */ public void clearLensDistortion() { detector.clearLensDistortion(); if (refineGray != null) refineGray.clearLensDistortion(); edgeIntensity.setTransform(null); } public void resetRuntimeProfiling() { detector.resetRuntimeProfiling(); milliAdjustBias.reset(); } /** * Detects polygons inside the grayscale image and its thresholded version * * @param gray Gray scale image * @param binary Binary version of grayscale image */ public void process( T gray, GrayU8 binary ) { detector.process(gray, binary); if (refineGray != null) refineGray.setImage(gray); edgeIntensity.setImage(gray); long time0 = System.nanoTime(); DogArray detections = detector.getFoundInfo(); if (adjustForBias != null) { int minSides = getMinimumSides(); for (int i = detections.size() - 1; i >= 0; i--) { Polygon2D_F64 p = detections.get(i).polygon; adjustForBias.process(p, detector.isOutputClockwiseUpY()); // When the polygon is adjusted for bias a point might need to be removed because it's // almost parallel. This could cause the shape to have too few corners and needs to be removed. if (p.size() < minSides) detections.remove(i); } } long time1 = System.nanoTime(); double milli = (time1 - time0)*1e-6; milliAdjustBias.update(milli); // System.out.printf(" contour %7.2f shapes %7.2f adjust_bias %7.2f\n", // detector.getMilliShapes(),detector.getMilliShapes(),milliAdjustBias); } /** * Refines the fit to the specified polygon. Only info.polygon is modified * * @param info The polygon and related info * @return true if successful or false if not */ public boolean refine( DetectPolygonFromContour.Info info ) { double before, after; if (edgeIntensity.computeEdge(info.polygon, !detector.isOutputClockwiseUpY())) { before = edgeIntensity.getAverageOutside() - edgeIntensity.getAverageInside(); } else { return false; } boolean success = false; if (refineContour != null) { List contour = detector.getContour(info); refineContour.process(contour, info.splits, work); if (adjustForBias != null) adjustForBias.process(work, detector.isOutputClockwiseUpY()); if (edgeIntensity.computeEdge(work, !detector.isOutputClockwiseUpY())) { after = edgeIntensity.getAverageOutside() - edgeIntensity.getAverageInside(); if (after > before) { info.edgeInside = edgeIntensity.getAverageInside(); info.edgeOutside = edgeIntensity.getAverageOutside(); info.polygon.setTo(work); success = true; before = after; } } } if (functionAdjust != null) { functionAdjust.adjust(info, detector.isOutputClockwiseUpY()); } if (refineGray != null) { work.vertexes.resize(info.polygon.size()); if (refineGray.refine(info.polygon, work)) { if (edgeIntensity.computeEdge(work, !detector.isOutputClockwiseUpY())) { after = edgeIntensity.getAverageOutside() - edgeIntensity.getAverageInside(); // basically, unless it diverged stick with this optimization // a near tie if (after*1.5 > before) { info.edgeInside = edgeIntensity.getAverageInside(); info.edgeOutside = edgeIntensity.getAverageOutside(); info.polygon.setTo(work); success = true; } } } } return success; } /** * Refines all the detected polygons and places them into the provided list. Polygons which fail the refinement * step are not added. */ public void refineAll() { List detections = detector.getFoundInfo().toList(); for (int i = 0; i < detections.size(); i++) { refine(detections.get(i)); } } /** * Returns a list of all polygons with an edge threshold above the minimum * * @param storageInfo Optional storage for info associated with polygons. Pruning is done so the info list * and the returned polygon list are not in synch with each other */ public List getPolygons( @Nullable List storage, @Nullable List storageInfo ) { if (storage == null) storage = new ArrayList<>(); else storage.clear(); if (storageInfo != null) storageInfo.clear(); List detections = detector.getFoundInfo().toList(); for (int i = 0; i < detections.size(); i++) { DetectPolygonFromContour.Info d = detections.get(i); if (d.computeEdgeIntensity() >= minimumRefineEdgeIntensity) { storage.add(d.polygon); if (storageInfo != null) { storageInfo.add(d); } } } return storage; } public List getPolygonInfo() { return detector.getFoundInfo().toList(); } public Class getInputType() { return detector.getInputType(); } public int getMinimumSides() { return detector.getMinimumSides(); } public int getMaximumSides() { return detector.getMaximumSides(); } public boolean isOutputClockwise() { return detector.isOutputClockwiseUpY(); } public DetectPolygonFromContour getDetector() { return detector; } public List getAllContours() { return detector.getAllContours(); } public void setFunctionAdjust( AdjustBeforeRefineEdge functionAdjust ) { this.functionAdjust = functionAdjust; } public double getMilliAdjustBias() { return milliAdjustBias.getAverage(); } public interface AdjustBeforeRefineEdge { void adjust( DetectPolygonFromContour.Info info, boolean clockwise ); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy