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.hough;
010
011import imagingbook.common.geometry.basic.Pnt2d;
012import imagingbook.common.geometry.line.AlgebraicLine;
013import imagingbook.common.geometry.line.HessianLine;
014
015import java.util.Locale;
016
017/**
018 * <p>
019 * This class represents a straight line of the form (x - xRef) * cos(angle) + (y - yRef) * sin(angle) = radius. It is
020 * used by the Hough transform (see {@link imagingbook.common.hough.HoughTransformLines}). It inherits from
021 * {@link HessianLine} which is, in turn, a subclass of {@link AlgebraicLine}. It adds an arbitrary reference point
022 * (xRef, yRef) and a counter (count) for pixel votes. Instances are immutable. See Sec. 12.2 of [1] for additional
023 * details.
024 * </p>
025 * <p>
026 * [1] W. Burger, M.J. Burge, <em>Digital Image Processing &ndash; An Algorithmic Introduction</em>, 3rd ed, Springer
027 * (2022).
028 * </p>
029 *
030 * @author WB
031 * @version 2022/08/24
032 */
033public class HoughLine extends HessianLine implements Comparable<HoughLine> {
034        
035        private final double xRef, yRef;        // reference point
036        private final int count;                        // pixel votes for this line
037        
038        // static factory methods -------------------------------
039        
040        public static HoughLine from(Pnt2d p1, Pnt2d p2, Pnt2d pRef, int count) {
041                return new HoughLine(AlgebraicLine.from(p1, p2), pRef.getX(), pRef.getY(), count);
042        }
043        
044        // constructors -----------------------------------------
045
046        /**
047         * Constructor, creates a new {@link HoughLine} instance from the specified {@link HessianLine} parameters (angle,
048         * radius), an arbitrary reference point (xRef, yRef) and count.
049         *
050         * @param angle the line's normal angle (see {@link HessianLine})
051         * @param radius the line's radius (distance to reference point)
052         * @param xRef reference point x-coordinate
053         * @param yRef reference point y-coordinate
054         * @param count pixel votes for this line
055         */
056        public HoughLine(double angle, double radius, double xRef, double yRef, int count) {
057                super(angle, radius);
058                this.xRef = xRef;
059                this.yRef = yRef;
060                this.count = count;
061        }
062
063        /**
064         * Constructor, creates a new {@link HoughLine} instance from a given {@link AlgebraicLine} (or any subclass)
065         * instance. The line parameters are adjusted to the specified reference point (actually only parameter c is
066         * modified, since a change of reference point effects only a shift of the line). The two lines are equivalent,
067         * i.e., contain the same points (x,y). Thus the distance from a given point (x,y) is the same from the original
068         * line and the new line.
069         *
070         * @param line an existing line ({@link AlgebraicLine} or subclass)
071         * @param xRef reference point x-coordinate
072         * @param yRef reference point y-coordinate
073         * @param count pixel votes for this line
074         */
075        public HoughLine(AlgebraicLine line, double xRef, double yRef, int count) {
076                super(new AlgebraicLine(line.A, line.B, line.C + line.A * (xRef - line.getXref()) + line.B * (yRef - line.getYref())));
077                this.xRef = xRef;
078                this.yRef = yRef;
079                this.count = count;
080        }
081
082        /**
083         * Convenience constructor, creates a new {@link HoughLine} instance from a given {@link AlgebraicLine} (or any
084         * subclass) instance with the same reference point as the original line and zero count.
085         *
086         * @param line a {@link AlgebraicLine} instance
087         */
088        public HoughLine(AlgebraicLine line) {
089                this(line, line.getXref(), line.getYref(), 0);
090        }
091        
092        // ------------------------------------------
093        
094        @Override
095        public double getXref() {
096                return xRef;
097        }
098        
099        @Override
100        public double getYref() {
101                return yRef;
102        }
103        
104        public int getCount() {
105                return count;
106        }
107        
108        // other methods ------------------------------------------
109
110        /**
111         * Required by the {@link Comparable} interface, used for sorting lines by their point count (in descending order,
112         * i.e., strong lines come first).
113         *
114         * @param other another {@link HoughLine} instance.
115         */
116        @Override
117        public int compareTo(HoughLine other) {
118                return Integer.compare(other.count, this.count);
119        }
120        
121        @Override
122        public String toString() {
123                return String.format(Locale.US, "%s <angle = %.3f, radius = %.3f, xRef = %.3f, yRef = %.3f, count = %d>",
124                                this.getClass().getSimpleName(), getAngle(), getRadius(), getXref(), getYref(), count);
125        }
126        
127}