001/******************************************************************************* 002 * This software is provided as a supplement to the authors' textbooks on digital 003 * image processing published by Springer-Verlag in various languages and editions. 004 * Permission to use and distribute this software is granted under the BSD 2-Clause 005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause). 006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved. 007 * Visit https://imagingbook.com for additional details. 008 ******************************************************************************/ 009package imagingbook.common.ransac; 010 011import imagingbook.common.geometry.basic.Pnt2d; 012import imagingbook.common.geometry.fitting.line.LineFit; 013import imagingbook.common.geometry.fitting.line.OrthogonalLineFitEigen; 014import imagingbook.common.geometry.line.AlgebraicLine; 015import imagingbook.common.ij.DialogUtils; 016 017import static imagingbook.common.math.Arithmetic.sqr; 018 019/** 020 * <p> 021 * RANSAC detector for straight lines. See Sec. 12.1.2 - 12.1.3 of [1] for additional details. 022 * </p> 023 * <p> 024 * [1] W. Burger, M.J. Burge, <em>Digital Image Processing – An Algorithmic Introduction</em>, 3rd ed, Springer 025 * (2022). 026 * </p> 027 * 028 * @author WB 029 * @version 2022/11/19 030 * @see AlgebraicLine 031 * @see RansacDetector 032 */ 033public class RansacLineDetector extends RansacDetector<AlgebraicLine>{ 034 035 private final Parameters params; 036 037 /** 038 * Nested class extending {@link RansacDetector.RansacParameters} 039 * to specify additional RANSAC parameters. 040 */ 041 public static class Parameters extends RansacDetector.RansacParameters { 042 043 /** The minimum distance between two random sample points. */ 044 @DialogUtils.DialogLabel("Min. distance between sample points") 045 public int minPairDistance; 046 047 /** 048 * Constructor used to define default parameter values. 049 */ 050 public Parameters() { 051 this.randomPointDraws = 1000; 052 this.maxInlierDistance = 2.0; 053 this.minInlierCount = 100; 054 this.minPairDistance = 25; 055 } 056 } 057 058 // constructors ------------------------------------ 059 060 /** 061 * Constructor using specific parameters. 062 * @param params RANSAC parameters 063 */ 064 public RansacLineDetector(Parameters params) { 065 super(2, params); 066 this.params = params; 067 } 068 069 /** 070 * Constructor using default parameters. 071 */ 072 public RansacLineDetector() { 073 this(new Parameters()); 074 } 075 076 // ---------------------------------------------------------------- 077 078 @Override // override default method to check for min pair distance 079 Pnt2d[] drawRandomPoints(Pnt2d[] points) { 080 final int MaxTries = 20; 081 int i = 0; 082 Pnt2d[] draw = super.drawRandomPoints(points); 083 while (draw[0].distanceSq(draw[1]) < sqr(params.minPairDistance) && i < MaxTries) { 084 draw = super.drawRandomPoints(points); 085 i++; 086 } 087 return (i < MaxTries) ? draw : null; 088 } 089 090 @Override 091 AlgebraicLine fitInitial(Pnt2d[] points) { 092 return AlgebraicLine.from(points[0], points[1]); 093 } 094 095 @Override 096 AlgebraicLine fitFinal(Pnt2d[] inliers) { 097 LineFit fit = new OrthogonalLineFitEigen(inliers); 098 return fit.getLine(); 099 } 100 101}