From 0178e61243d3255cc2caed4e917a66db98a6d472 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Wed, 4 Apr 2018 16:23:07 -0500 Subject: [PATCH 01/42] Ignore IntelliJ metadata files --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 10d81e8c6..78a598308 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,7 @@ /.project /.settings/ /target/ + +# IntelliJ +/.idea/ +*.iml From c1952b163f9a7e443a8f7351ecf327559d5c382a Mon Sep 17 00:00:00 2001 From: Yili Zhao Date: Fri, 24 Feb 2017 09:55:19 -0600 Subject: [PATCH 02/42] Add Hough line transform Signed-off-by: Gabe Selzer --- .../algorithm/hough/HoughLineTransform.java | 360 ++++++++++++++++++ .../algorithm/hough/HoughTransform.java | 250 ++++++++++++ .../hough/HoughLineTransformTest.java | 106 ++++++ .../net/imglib2/algorithm/hough/result8.png | Bin 0 -> 895 bytes 4 files changed, 716 insertions(+) create mode 100644 src/main/java/net/imglib2/algorithm/hough/HoughLineTransform.java create mode 100644 src/main/java/net/imglib2/algorithm/hough/HoughTransform.java create mode 100644 src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java create mode 100644 src/test/resources/net/imglib2/algorithm/hough/result8.png diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughLineTransform.java b/src/main/java/net/imglib2/algorithm/hough/HoughLineTransform.java new file mode 100644 index 000000000..6b4e56ed5 --- /dev/null +++ b/src/main/java/net/imglib2/algorithm/hough/HoughLineTransform.java @@ -0,0 +1,360 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package net.imglib2.algorithm.hough; + +import java.util.ArrayList; + +import net.imglib2.Cursor; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.NativeType; +import net.imglib2.type.Type; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.LongType; +import net.imglib2.type.numeric.integer.ShortType; + +/** + * A class that extends {@link HoughTransform} to handle Hough Line voting over + * an edge map. This implementation uses a threshold to determine whether a + * pixel at a certain point is an edge or not. Comparison is + * strictly-greater-than. This implementation is fairly dumb in that it does not + * take gradients into account. The threshold used is the default value returned + * by calling the constructor for the {@link Type} of the input {@link Img}. + *

+ * Vote space here has two dimensions: {@code rho} and {@code theta}. + * {@code theta} is measured in radians {@code [-pi/2 pi/2)}, {@code rho} is + * measured in {@code [-rhoMax, rhoMax)}. + *

+ *

+ * Lines are modeled as + *

+ * + *
+ * l(t) = | x | = rho * |  cos(theta) | + t * | sin(theta) |
+ *        | y |         | -sin(theta) |       | cos(theta) |
+ * 
+ *

+ * In other words, {@code rho} represents the signed minimum distance from the + * image origin to the line, and {@code theta} indicates the angle between the + * row-axis and the minimum offset vector. + *

+ *

+ * For a given point, then, votes are placed along the curve + *

+ * + *
+ * rho = y * sin( theta ) + x * cos( theta )
+ * 
+ */ +public class HoughLineTransform< S extends RealType< S > & NativeType< S >, T extends Type< T > & Comparable< T > > extends HoughTransform< S, T > +{ + + public static final int DEFAULT_THETA = 180; + + public final double dTheta; + + public final double dRho; + + private final T threshold; + + private final int nRho; + + private final int nTheta; + + private final double[] rho; + + private final double[] theta; + + private ArrayList< double[] > rtPeaks; + + final private static float computeLength( final long[] position ) + { + float dist = 0; + + for ( int d = 0; d < position.length; ++d ) + { + final long pos = position[ d ]; + + dist += pos * pos; + } + + return ( float ) Math.sqrt( dist ); + } + + /** + * Calculates a default number of rho bins, which corresponds to a + * resolution of one pixel. + * + * @param inputImage + * the {@link Img} in question. + * @return default number of rho bins. + */ + public static int defaultRho( final Img< ? > inputImage ) + { + final long[] dims = new long[ inputImage.numDimensions() ]; + inputImage.dimensions( dims ); + return ( int ) ( 2 * computeLength( dims ) ); + } + + /** + * Creates a default {@link HoughLineTransform} with {@ShortType} vote + * space. + * + * @param + * the {@link Type} of the {@link Img} in question. + * @param inputImage + * the {@link Img} to perform the Hough Line Transform against. + * @return a default {@link HoughLineTransform} with {@link IntType} vote + * space. + */ + public static < T extends Type< T > & Comparable< T > > HoughLineTransform< ShortType, T > shortHoughLine( final Img< T > inputImage) + { + return new HoughLineTransform<>( inputImage, new ShortType()); + } + + /** + * Creates a default {@link HoughLineTransform} with {@IntType} vote space. + * + * @param + * the {@link Type} of the {@link Img} in question. + * @param inputImage + * the {@link Img} to perform the Hough Line Transform against. + * @return a default {@link HoughLineTransform} with {@link IntType} vote + * space. + */ + public static < T extends Type< T > & Comparable< T > > HoughLineTransform< IntType, T > integerHoughLine( final Img< T > inputImage) + { + return new HoughLineTransform<>( inputImage, new IntType()); + } + + /** + * Creates a default {@link HoughLineTransform} with {@link LongType} vote + * space. + * + * @param + * the {@link Type} of the {@link Img} in question. + * @param inputImage + * the {@link Img} to perform the Hough Line Transform against. + * @return a default {@link HoughLineTransform} with {@link LongType} vote + * space. + *

+ * Use this for voting against large images, but reasonably small + * vote space. If you need a big voting space, it would be better to + * create a {@link HoughLineTransform} instantiated with an + * {@link ImgFactory} capable of handling it. + */ + public static < T extends Type< T > & Comparable< T > > HoughLineTransform< LongType, T > longHoughLine( final Img< T > inputImage) + { + return new HoughLineTransform<>( inputImage, new LongType()); + } + + /** + * Create a {@link HoughLineTransform} to operate against a given + * {@link Img}, with a specific {@link Type} of vote space. Defaults are + * used for rho- and theta-resolution. + * + * @param inputImage + * the {@link Img} to operate against. + * @param type + * the {@link Type} for the vote space. + */ + public HoughLineTransform( final Img< T > inputImage, final S type) + { + this( inputImage, DEFAULT_THETA, type); + } + + /** + * Create a {@link HoughLineTransform} to operate against a given + * {@link Img}, with a specific {@link Type} of vote space and + * theta-resolution. Rho-resolution is set to the default. + * + * @param inputImage + * the {@link Img} to operate against. + * @param theta + * the number of bins for theta-resolution. + * @param type + * the {@link Type} for the vote space. + */ + public HoughLineTransform( final Img< T > inputImage, final int theta, final S type) + { + this( inputImage, defaultRho( inputImage ), theta, type); + } + + /** + * Create a {@link HoughLineTransform} to operate against a given + * {@link Img}, with a specific {@link Type} of vote space and rho- and + * theta-resolution. + * + * @param inputImage + * the {@link Img} to operate against. + * @param inNRho + * the number of bins for rho resolution. + * @param inNTheta + * the number of bins for theta resolution. + * @param type + * the {@link Type} for the vote space. + */ + public HoughLineTransform( final Img< T > inputImage, final int inNRho, final int inNTheta, final S type) + { + // Call the base constructor + super( inputImage, new long[] { inNRho, inNTheta }, type); + + // Theta by definition is in [0..pi]. + dTheta = Math.PI / inNTheta; + + // The furthest a point can be from the origin is the length calculated + // from the dimensions of the Image. + final long[] dims = new long[ inputImage.numDimensions() ]; + inputImage.dimensions( dims ); + dRho = 2 * computeLength( dims ) / ( double ) inNRho; + threshold = inputImage.firstElement().createVariable(); + nRho = inNRho; + nTheta = inNTheta; + theta = new double[ inNTheta ]; + rho = new double[ inNRho ]; + rtPeaks = null; + } + + /** + * Create a {@link HoughLineTransform} to operate against a given + * {@link Img}, with a specific {@link ImgFactory} for the vote space, and + * specific rho- and theta-resolution. + * + * @param inputImage + * the {@link Img} to operate against. + * @param factory + * the {@link ImgFactory} object. + * @param inNRho + * the number of bisn for rho resolution. + * @param inNTheta + * the number of bins for theta resolution. + * @param type + * the {@link Type} for the vote space. + */ + public HoughLineTransform( final Img< T > inputImage, final ImgFactory< S > factory, final S type, final int inNRho, final int inNTheta) + { + // Call the base constructor + super( inputImage, new long[] { inNRho, inNTheta }, factory, type); + + dTheta = Math.PI / inNTheta; + + final long[] dims = new long[ inputImage.numDimensions() ]; + inputImage.dimensions( dims ); + dRho = 2 * computeLength( dims ) / ( double ) inNRho; + threshold = inputImage.firstElement().createVariable(); + nRho = inNRho; + nTheta = inNTheta; + theta = new double[ inNTheta ]; + rho = new double[ inNRho ]; + rtPeaks = null; + } + + public void setThreshold( final T inThreshold ) + { + threshold.set( inThreshold ); + } + + @Override + public boolean process() + { + final Cursor< T > imageCursor = getImage().localizingCursor(); + final long[] position = new long[ getImage().numDimensions() ]; + final double minTheta = -Math.PI / 2; + + final long[] dims = new long[ getImage().numDimensions() ]; + getImage().dimensions( dims ); + final double minRho = -computeLength( dims ); + + final long sTime = System.currentTimeMillis(); + boolean success; + + for ( int t = 0; t < nTheta; ++t ) + { + theta[ t ] = dTheta * t + minTheta; + } + + for ( int r = 0; r < nRho; ++r ) + { + rho[ r ] = dRho * r + minRho; + } + + while ( imageCursor.hasNext() ) + { + double fRho; + int r; + final int[] voteLoc = new int[ 2 ]; + + imageCursor.fwd(); + imageCursor.localize( position ); + + for ( int t = 0; t < nTheta; ++t ) + { + if ( imageCursor.get().compareTo( threshold ) > 0 ) + { + fRho = Math.cos( theta[ t ] ) * position[ 0 ] + Math.sin( theta[ t ] ) * position[ 1 ]; + r = Math.round( ( float ) ( ( fRho - minRho ) / dRho ) ); + voteLoc[ 0 ] = r; + voteLoc[ 1 ] = t; + // place vote + super.placeVote( voteLoc ); + } + } + } + // pick peaks + success = super.pickPeaks(); + super.pTime = System.currentTimeMillis() - sTime; + return success; + } + + public ArrayList< double[] > getTranslatedPeakList() + { + if ( rtPeaks == null ) + { + final ArrayList< long[] > peaks = getPeakList(); + rtPeaks = new ArrayList<>( peaks.size() ); + for ( final long[] irt : peaks ) + { + final double[] rt = new double[ 2 ]; + rt[ 0 ] = rho[ ( int ) irt[ 0 ] ]; + rt[ 1 ] = theta[ ( int ) irt[ 1 ] ]; + rtPeaks.add( rt ); + } + } + + return rtPeaks; + } + +} diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransform.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransform.java new file mode 100644 index 000000000..97292c5df --- /dev/null +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransform.java @@ -0,0 +1,250 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package net.imglib2.algorithm.hough; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import net.imglib2.Point; +import net.imglib2.RandomAccess; +import net.imglib2.algorithm.Benchmark; +import net.imglib2.algorithm.OutputAlgorithm; +import net.imglib2.algorithm.localextrema.LocalExtrema; +import net.imglib2.algorithm.localextrema.LocalExtrema.MaximumCheck; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.type.NativeType; +import net.imglib2.type.Type; +import net.imglib2.type.numeric.RealType; + +/** + * This abstract class provides some basic functionality for use with arbitrary + * Hough-like transforms. + * + * @param + * the data type used for storing votes, usually IntType, but + * possibly LongType or even DoubleType. + * @param + * the data type of the input image. + * @author Larry Lindsey + * @author Yili Zhao + * @author Gabe Selzer + */ +public abstract class HoughTransform< S extends RealType< S > & NativeType< S >, T extends Type< T > & Comparable< T > > implements OutputAlgorithm< Img< S > >, Benchmark +{ + + protected long pTime; + + private String errorMsg; + + private final Img< T > image; + + private final Img< S > voteSpace; + + private RandomAccess< S > voteCursor; + + private ArrayList< long[] > peaks; + + private final double[] peakExclusion; + + private final S one; + + /** + * Constructor for a HoughTransform using an ArrayImageFactory to back the + * ImageFactory used to generate the voteSpace image. + * + * @param inputImage + * the image for the HoughTransform to operate over. + * @param voteSize + * array indicating the size of the voteSpace. This is passed + * directly into ImageFactory to create a voteSpace image. + * @param type + * the Type used for generating the voteSpace image. + */ + protected HoughTransform( final Img< T > inputImage, final long[] voteSize, final S type ) + { + this( inputImage, voteSize, new ArrayImgFactory<>(), type ); + } + + /** + * Constructor for a HoughTransform with a specific ImageFactory. Use this + * if you have something specific in mind as to how the vote data should be + * stored. + * + * @param inputImage + * the image for the HoughTransform to operate over. + * @param voteSize + * array indicating the size of the voteSpace. This is passed + * directly into ImageFactory to create a voteSpace image. + * @param voteFactory + * the ImgFactory used to generate the voteSpace image. + */ + protected HoughTransform( final Img< T > inputImage, final long[] voteSize, final ImgFactory< S > voteFactory, final S type ) + { + image = inputImage; + voteCursor = null; + pTime = 0; + voteSpace = voteFactory.create( voteSize, type ); + peaks = null; + peakExclusion = new double[ voteSize.length ]; + one = type.createVariable(); + one.setOne(); + Arrays.fill( peakExclusion, 0 ); + } + + /** + * Place a vote with a specific value. + * + * @param loc + * the integer array indicating the location where the vote is to + * be placed in voteSpace. + * @param vote + * the value of the vote + * @return whether the vote was successful. This here particular method + * should always return true. + */ + protected boolean placeVote( final int[] loc, final S vote ) + { + if ( voteCursor == null ) + { + voteCursor = voteSpace.randomAccess(); + } + + voteCursor.setPosition( loc ); + voteCursor.get().add( vote ); + + return true; + } + + /** + * Place a vote of value 1. + * + * @param loc + * the integer array indicating the location where the vote is to + * be placed in voteSpace. + * @return whether the vote was successful. This here particular method + * should always return true. + */ + protected boolean placeVote( final int[] loc ) + { + return placeVote( loc, one ); + } + + /** + * Returns an ArrayList of long arrays, representing the positions in the + * vote space that correspond to peaks. + * + * @return an ArrayList of vote space peak locations. + */ + public ArrayList< long[] > getPeakList() + { + return peaks; + } + + public boolean setExclusion( final double[] newExclusion ) + { + if ( newExclusion.length >= peakExclusion.length ) + { + System.arraycopy( newExclusion, 0, peakExclusion, 0, peakExclusion.length ); + return true; + } + return false; + } + + protected void setErrorMsg( final String msg ) + { + errorMsg = msg; + } + + /** + * Pick vote space peaks with a {@link LocalExtrema}. + * + * @return whether peak picking was successful + */ + protected boolean pickPeaks() + { + S maxValue = voteSpace.firstElement().copy(); + maxValue.setReal( maxValue.getRealDouble() ); + final MaximumCheck< S > maxCheck = new MaximumCheck< S >( maxValue ); + + List< Point > peaksList = LocalExtrema.findLocalExtrema( voteSpace, maxCheck); + peaks = new ArrayList(); + + long[] dims = new long[ image.numDimensions() ]; + + for ( Point p : peaksList ) + { + for ( int i = 0; i < p.numDimensions(); i++ ) + { + dims[ i ] = p.getLongPosition( i ); + } + peaks.add( dims ); + } + + return true; + } + + @Override + public boolean checkInput() + { + return voteSpace != null; + } + + @Override + public String getErrorMessage() + { + return errorMsg; + } + + @Override + public long getProcessingTime() + { + return pTime; + } + + public Img< T > getImage() + { + return image; + } + + @Override + public Img< S > getResult() + { + return voteSpace; + } + +} diff --git a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java new file mode 100644 index 000000000..52d12a652 --- /dev/null +++ b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java @@ -0,0 +1,106 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package net.imglib2.algorithm.hough; + +import static org.junit.Assert.assertEquals; + +import java.awt.image.BufferedImage; +import java.io.IOException; + +import javax.imageio.ImageIO; + +import org.junit.Before; +import org.junit.Test; + +import net.imglib2.RandomAccess; +import net.imglib2.img.Img; +import net.imglib2.img.array.ArrayImgs; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.util.Util; + +public class HoughLineTransformTest +{ + + private BufferedImage groundTruth; + + private Img< UnsignedByteType > tpImg; + + public static int N_THREADS = Runtime.getRuntime().availableProcessors(); + + @Before + public void setUp() throws IOException + { + groundTruth = ImageIO.read( getClass().getResource( "result8.png" ) ); + tpImg = ArrayImgs.unsignedBytes( 50l, 50l ); + final RandomAccess< UnsignedByteType > randomAccess = tpImg.randomAccess(); + for ( int i = 24; i < 25; ++i ) + { + for ( int j = 14; j < 35; ++j ) + { + randomAccess.setPosition( new int[] { i, j } ); + randomAccess.get().set( 255 ); + } + } + } + + @Test + public < T extends RealType< T > & NativeType< T > > void testHoughLineTransformToTarget() throws IOException + { + final HoughLineTransform line = HoughLineTransform.integerHoughLine( tpImg ); + + if ( line.process() ) + { + final Img< T > result = line.getResult(); + final long[] dims = new long[ result.numDimensions() ]; + result.dimensions( dims ); + final RandomAccess< T > ra = result.randomAccess(); + + assertEquals( "Ground truth and result height do not match.", groundTruth.getHeight(), dims[ 1 ] ); + assertEquals( "Ground truth and result width do not match.", groundTruth.getWidth(), dims[ 0 ] ); + + for ( long i = 0; i < groundTruth.getWidth(); ++i ) + { + for ( long j = 0; j < groundTruth.getHeight(); ++j ) + { + ra.setPosition( new long[] { i, j } ); + + assertEquals( "Ground truth and result pixel do not match at " + Util.printCoordinates( ra ) + ".", groundTruth.getData().getSample( ( int ) i, ( int ) j, 0 ), ra.get().getRealDouble(), 0 ); + } + } + } + } +} diff --git a/src/test/resources/net/imglib2/algorithm/hough/result8.png b/src/test/resources/net/imglib2/algorithm/hough/result8.png new file mode 100644 index 0000000000000000000000000000000000000000..9f5bce26dfc5d286554ac5f2300c46d3b0785348 GIT binary patch literal 895 zcmV-_1AzRAP)6YnEnBue9LiEC=Uwn*H~VYh;f(CWvShQ5RCre( z`p^GSkLJ^GSQ;3IrGar+nmfl)8c;Zn(y+pDl;#O%E`P%L8|NO6QUePo-0~4?rgU@~ z8zFj*1HMu?eFO z=XBIJHOGfvsm3OZIGpl%)nVk}RK@j2%o#B1aLk*85KeVK!s(;Yhhr@oL^z!W5Y7py zBC*CU&HhqrY|!Y#IVFs6Itw5iYZ^p26#@yTX$awLoKqvc7jjmWI3=7@g9zt<`2Mc8 zSm{;8IG|NKrxqcc&^T&JDONaz6FK4Ry(*rlmh2Vc!HI}gS+NuiqE%+mj~64HaB|d; zqY9<`s2#O-ON5N$tyhs0&K?aWoUKZet|w6XyZ8kKpo`YzN!U%u|BNwO8T&huN8DyZQn>=YD24# zmjx8gcI^QAMp~}(IN`*?Q6sMM?Xr_IL*S7#Ll9D#&7p(?o9RkrP7*WIbxe^`!a1go zDKoZyOlL#JG3(BQ@0jNf+PexB!?=SMV!ids(0IM~ifC21?vfM2IaV}8l!mf|fT#(O zfpaaPLTLh`RiRt&rVl5yrGS7|b;~3{8pg8CMhSpn+^>b!594l~I7eJ%uz2asmk@~YKz+obiH0i)L3_Snlc;4yiCx5SlI^v8I Date: Wed, 4 Apr 2018 15:59:08 -0500 Subject: [PATCH 03/42] HoughLineTransformTest: Optimize performance --- .../hough/HoughLineTransformTest.java | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java index 52d12a652..65e0f960e 100644 --- a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java +++ b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java @@ -35,22 +35,23 @@ package net.imglib2.algorithm.hough; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import java.awt.image.BufferedImage; +import java.awt.image.DataBufferByte; import java.io.IOException; import javax.imageio.ImageIO; -import org.junit.Before; -import org.junit.Test; - import net.imglib2.RandomAccess; import net.imglib2.img.Img; import net.imglib2.img.array.ArrayImgs; import net.imglib2.type.NativeType; import net.imglib2.type.numeric.RealType; import net.imglib2.type.numeric.integer.UnsignedByteType; -import net.imglib2.util.Util; + +import org.junit.Before; +import org.junit.Test; public class HoughLineTransformTest { @@ -71,7 +72,8 @@ public void setUp() throws IOException { for ( int j = 14; j < 35; ++j ) { - randomAccess.setPosition( new int[] { i, j } ); + randomAccess.setPosition( i, 0 ); + randomAccess.setPosition( j, 1 ); randomAccess.get().set( 255 ); } } @@ -82,24 +84,31 @@ public < T extends RealType< T > & NativeType< T > > void testHoughLineTransform { final HoughLineTransform line = HoughLineTransform.integerHoughLine( tpImg ); - if ( line.process() ) - { - final Img< T > result = line.getResult(); - final long[] dims = new long[ result.numDimensions() ]; - result.dimensions( dims ); - final RandomAccess< T > ra = result.randomAccess(); + boolean success = line.process(); + assertTrue( success ); + final Img< T > result = line.getResult(); + final long[] dims = new long[ result.numDimensions() ]; + result.dimensions( dims ); + final RandomAccess< T > ra = result.randomAccess(); + + int height = groundTruth.getHeight(); + int width = groundTruth.getWidth(); + assertEquals( "Ground truth and result height do not match.", height, dims[ 1 ] ); + assertEquals( "Ground truth and result width do not match.", width, dims[ 0 ] ); - assertEquals( "Ground truth and result height do not match.", groundTruth.getHeight(), dims[ 1 ] ); - assertEquals( "Ground truth and result width do not match.", groundTruth.getWidth(), dims[ 0 ] ); + DataBufferByte db = ( DataBufferByte ) groundTruth.getData().getDataBuffer(); + byte[] expected = db.getBankData()[ 0 ]; - for ( long i = 0; i < groundTruth.getWidth(); ++i ) + int index = 0; + for ( int j = 0; j < height; ++j ) + { + ra.setPosition( j, 1 ); + for ( int i = 0; i < width; ++i ) { - for ( long j = 0; j < groundTruth.getHeight(); ++j ) - { - ra.setPosition( new long[] { i, j } ); + ra.setPosition( i, 0 ); + + assertEquals( expected[ index++ ], ra.get().getRealDouble(), 0 ); - assertEquals( "Ground truth and result pixel do not match at " + Util.printCoordinates( ra ) + ".", groundTruth.getData().getSample( ( int ) i, ( int ) j, 0 ), ra.get().getRealDouble(), 0 ); - } } } } From c67a7c2c5dfe90f07c056f9bfda9fe0497782a1b Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Wed, 4 Apr 2018 16:21:57 -0500 Subject: [PATCH 04/42] HoughLineTransformTest: add missing javadoc --- .../net/imglib2/algorithm/hough/HoughLineTransformTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java index 65e0f960e..02af7bad8 100644 --- a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java +++ b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java @@ -53,6 +53,12 @@ import org.junit.Before; import org.junit.Test; +/** + * Tests {@link HoughLineTransform}. + * + * @author Yili Zhao + * @author Gabe Selzer + */ public class HoughLineTransformTest { From 32ed03b1c29f8173542f28953e469b5093eaf657 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 19 Apr 2018 16:14:07 -0500 Subject: [PATCH 05/42] HoughLineTransform: Clone peak storage array This stage of the transform makes use of a single array to grab each peak Point and then store it in an ArrayList of long[]. However the array was not copied as it was added into the ArrayList, resulting in all entries in the ArrayList pointing to the same array. This commit fixes this bug through use of Arrays.copyOf() to copy each array and add the result to the ArrayList. --- src/main/java/net/imglib2/algorithm/hough/HoughTransform.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransform.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransform.java index 97292c5df..192337908 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransform.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransform.java @@ -212,7 +212,7 @@ protected boolean pickPeaks() { dims[ i ] = p.getLongPosition( i ); } - peaks.add( dims ); + peaks.add( Arrays.copyOf( dims, dims.length ) ); } return true; From d36739f3d5e4c00b6f0229b58b3ff85b23088afe Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 19 Apr 2018 16:18:34 -0500 Subject: [PATCH 06/42] HoughLineTransform: fix rawType warning --- .../java/net/imglib2/algorithm/hough/HoughTransform.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransform.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransform.java index 192337908..43d6c8833 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransform.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransform.java @@ -201,9 +201,9 @@ protected boolean pickPeaks() maxValue.setReal( maxValue.getRealDouble() ); final MaximumCheck< S > maxCheck = new MaximumCheck< S >( maxValue ); - List< Point > peaksList = LocalExtrema.findLocalExtrema( voteSpace, maxCheck); - peaks = new ArrayList(); - + List< Point > peaksList = LocalExtrema.findLocalExtrema( voteSpace, maxCheck ); + peaks = new ArrayList<>(); + long[] dims = new long[ image.numDimensions() ]; for ( Point p : peaksList ) From 84621747cda53968bd68a29fa5f5a9a22e4af816 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 19 Apr 2018 17:12:56 -0500 Subject: [PATCH 07/42] HoughTransform: remove Benchmark import This import is not necessary for the class since the class is not a benchmark. --- .../imglib2/algorithm/hough/HoughLineTransform.java | 2 -- .../net/imglib2/algorithm/hough/HoughTransform.java | 12 +----------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughLineTransform.java b/src/main/java/net/imglib2/algorithm/hough/HoughLineTransform.java index 6b4e56ed5..897a4acc1 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughLineTransform.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughLineTransform.java @@ -298,7 +298,6 @@ public boolean process() getImage().dimensions( dims ); final double minRho = -computeLength( dims ); - final long sTime = System.currentTimeMillis(); boolean success; for ( int t = 0; t < nTheta; ++t ) @@ -335,7 +334,6 @@ public boolean process() } // pick peaks success = super.pickPeaks(); - super.pTime = System.currentTimeMillis() - sTime; return success; } diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransform.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransform.java index 43d6c8833..910da457c 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransform.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransform.java @@ -40,7 +40,6 @@ import net.imglib2.Point; import net.imglib2.RandomAccess; -import net.imglib2.algorithm.Benchmark; import net.imglib2.algorithm.OutputAlgorithm; import net.imglib2.algorithm.localextrema.LocalExtrema; import net.imglib2.algorithm.localextrema.LocalExtrema.MaximumCheck; @@ -64,11 +63,9 @@ * @author Yili Zhao * @author Gabe Selzer */ -public abstract class HoughTransform< S extends RealType< S > & NativeType< S >, T extends Type< T > & Comparable< T > > implements OutputAlgorithm< Img< S > >, Benchmark +public abstract class HoughTransform< S extends RealType< S > & NativeType< S >, T extends Type< T > & Comparable< T > > implements OutputAlgorithm< Img< S > > { - protected long pTime; - private String errorMsg; private final Img< T > image; @@ -117,7 +114,6 @@ protected HoughTransform( final Img< T > inputImage, final long[] voteSize, fina { image = inputImage; voteCursor = null; - pTime = 0; voteSpace = voteFactory.create( voteSize, type ); peaks = null; peakExclusion = new double[ voteSize.length ]; @@ -230,12 +226,6 @@ public String getErrorMessage() return errorMsg; } - @Override - public long getProcessingTime() - { - return pTime; - } - public Img< T > getImage() { return image; From a1c8843aa52309877cb8c6bc7d2c197089aacddb Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Wed, 25 Apr 2018 17:49:52 -0500 Subject: [PATCH 08/42] HoughLineTransform: Refactor into static methods. The previous implementation was based on stateful objects, which was not optimal for use of this algorithm. The algorithm was thus reworked into static methods for both votespace generation and peak picking. The process of the algorithm was kept but massaged to fit the new implementation style. --- .../algorithm/hough/HoughLineTransform.java | 358 ------------------ .../algorithm/hough/HoughTransform.java | 240 ------------ .../algorithm/hough/HoughTransforms.java | 257 +++++++++++++ .../hough/HoughLineTransformTest.java | 25 +- 4 files changed, 273 insertions(+), 607 deletions(-) delete mode 100644 src/main/java/net/imglib2/algorithm/hough/HoughLineTransform.java delete mode 100644 src/main/java/net/imglib2/algorithm/hough/HoughTransform.java create mode 100644 src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughLineTransform.java b/src/main/java/net/imglib2/algorithm/hough/HoughLineTransform.java deleted file mode 100644 index 897a4acc1..000000000 --- a/src/main/java/net/imglib2/algorithm/hough/HoughLineTransform.java +++ /dev/null @@ -1,358 +0,0 @@ -/* - * #%L - * ImgLib2: a general-purpose, multidimensional image processing library. - * %% - * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, - * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, - * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, - * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, - * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, - * Jean-Yves Tinevez and Michael Zinsmaier. - * %% - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * #L% - */ - -package net.imglib2.algorithm.hough; - -import java.util.ArrayList; - -import net.imglib2.Cursor; -import net.imglib2.img.Img; -import net.imglib2.img.ImgFactory; -import net.imglib2.type.NativeType; -import net.imglib2.type.Type; -import net.imglib2.type.numeric.RealType; -import net.imglib2.type.numeric.integer.IntType; -import net.imglib2.type.numeric.integer.LongType; -import net.imglib2.type.numeric.integer.ShortType; - -/** - * A class that extends {@link HoughTransform} to handle Hough Line voting over - * an edge map. This implementation uses a threshold to determine whether a - * pixel at a certain point is an edge or not. Comparison is - * strictly-greater-than. This implementation is fairly dumb in that it does not - * take gradients into account. The threshold used is the default value returned - * by calling the constructor for the {@link Type} of the input {@link Img}. - *

- * Vote space here has two dimensions: {@code rho} and {@code theta}. - * {@code theta} is measured in radians {@code [-pi/2 pi/2)}, {@code rho} is - * measured in {@code [-rhoMax, rhoMax)}. - *

- *

- * Lines are modeled as - *

- * - *
- * l(t) = | x | = rho * |  cos(theta) | + t * | sin(theta) |
- *        | y |         | -sin(theta) |       | cos(theta) |
- * 
- *

- * In other words, {@code rho} represents the signed minimum distance from the - * image origin to the line, and {@code theta} indicates the angle between the - * row-axis and the minimum offset vector. - *

- *

- * For a given point, then, votes are placed along the curve - *

- * - *
- * rho = y * sin( theta ) + x * cos( theta )
- * 
- */ -public class HoughLineTransform< S extends RealType< S > & NativeType< S >, T extends Type< T > & Comparable< T > > extends HoughTransform< S, T > -{ - - public static final int DEFAULT_THETA = 180; - - public final double dTheta; - - public final double dRho; - - private final T threshold; - - private final int nRho; - - private final int nTheta; - - private final double[] rho; - - private final double[] theta; - - private ArrayList< double[] > rtPeaks; - - final private static float computeLength( final long[] position ) - { - float dist = 0; - - for ( int d = 0; d < position.length; ++d ) - { - final long pos = position[ d ]; - - dist += pos * pos; - } - - return ( float ) Math.sqrt( dist ); - } - - /** - * Calculates a default number of rho bins, which corresponds to a - * resolution of one pixel. - * - * @param inputImage - * the {@link Img} in question. - * @return default number of rho bins. - */ - public static int defaultRho( final Img< ? > inputImage ) - { - final long[] dims = new long[ inputImage.numDimensions() ]; - inputImage.dimensions( dims ); - return ( int ) ( 2 * computeLength( dims ) ); - } - - /** - * Creates a default {@link HoughLineTransform} with {@ShortType} vote - * space. - * - * @param - * the {@link Type} of the {@link Img} in question. - * @param inputImage - * the {@link Img} to perform the Hough Line Transform against. - * @return a default {@link HoughLineTransform} with {@link IntType} vote - * space. - */ - public static < T extends Type< T > & Comparable< T > > HoughLineTransform< ShortType, T > shortHoughLine( final Img< T > inputImage) - { - return new HoughLineTransform<>( inputImage, new ShortType()); - } - - /** - * Creates a default {@link HoughLineTransform} with {@IntType} vote space. - * - * @param - * the {@link Type} of the {@link Img} in question. - * @param inputImage - * the {@link Img} to perform the Hough Line Transform against. - * @return a default {@link HoughLineTransform} with {@link IntType} vote - * space. - */ - public static < T extends Type< T > & Comparable< T > > HoughLineTransform< IntType, T > integerHoughLine( final Img< T > inputImage) - { - return new HoughLineTransform<>( inputImage, new IntType()); - } - - /** - * Creates a default {@link HoughLineTransform} with {@link LongType} vote - * space. - * - * @param - * the {@link Type} of the {@link Img} in question. - * @param inputImage - * the {@link Img} to perform the Hough Line Transform against. - * @return a default {@link HoughLineTransform} with {@link LongType} vote - * space. - *

- * Use this for voting against large images, but reasonably small - * vote space. If you need a big voting space, it would be better to - * create a {@link HoughLineTransform} instantiated with an - * {@link ImgFactory} capable of handling it. - */ - public static < T extends Type< T > & Comparable< T > > HoughLineTransform< LongType, T > longHoughLine( final Img< T > inputImage) - { - return new HoughLineTransform<>( inputImage, new LongType()); - } - - /** - * Create a {@link HoughLineTransform} to operate against a given - * {@link Img}, with a specific {@link Type} of vote space. Defaults are - * used for rho- and theta-resolution. - * - * @param inputImage - * the {@link Img} to operate against. - * @param type - * the {@link Type} for the vote space. - */ - public HoughLineTransform( final Img< T > inputImage, final S type) - { - this( inputImage, DEFAULT_THETA, type); - } - - /** - * Create a {@link HoughLineTransform} to operate against a given - * {@link Img}, with a specific {@link Type} of vote space and - * theta-resolution. Rho-resolution is set to the default. - * - * @param inputImage - * the {@link Img} to operate against. - * @param theta - * the number of bins for theta-resolution. - * @param type - * the {@link Type} for the vote space. - */ - public HoughLineTransform( final Img< T > inputImage, final int theta, final S type) - { - this( inputImage, defaultRho( inputImage ), theta, type); - } - - /** - * Create a {@link HoughLineTransform} to operate against a given - * {@link Img}, with a specific {@link Type} of vote space and rho- and - * theta-resolution. - * - * @param inputImage - * the {@link Img} to operate against. - * @param inNRho - * the number of bins for rho resolution. - * @param inNTheta - * the number of bins for theta resolution. - * @param type - * the {@link Type} for the vote space. - */ - public HoughLineTransform( final Img< T > inputImage, final int inNRho, final int inNTheta, final S type) - { - // Call the base constructor - super( inputImage, new long[] { inNRho, inNTheta }, type); - - // Theta by definition is in [0..pi]. - dTheta = Math.PI / inNTheta; - - // The furthest a point can be from the origin is the length calculated - // from the dimensions of the Image. - final long[] dims = new long[ inputImage.numDimensions() ]; - inputImage.dimensions( dims ); - dRho = 2 * computeLength( dims ) / ( double ) inNRho; - threshold = inputImage.firstElement().createVariable(); - nRho = inNRho; - nTheta = inNTheta; - theta = new double[ inNTheta ]; - rho = new double[ inNRho ]; - rtPeaks = null; - } - - /** - * Create a {@link HoughLineTransform} to operate against a given - * {@link Img}, with a specific {@link ImgFactory} for the vote space, and - * specific rho- and theta-resolution. - * - * @param inputImage - * the {@link Img} to operate against. - * @param factory - * the {@link ImgFactory} object. - * @param inNRho - * the number of bisn for rho resolution. - * @param inNTheta - * the number of bins for theta resolution. - * @param type - * the {@link Type} for the vote space. - */ - public HoughLineTransform( final Img< T > inputImage, final ImgFactory< S > factory, final S type, final int inNRho, final int inNTheta) - { - // Call the base constructor - super( inputImage, new long[] { inNRho, inNTheta }, factory, type); - - dTheta = Math.PI / inNTheta; - - final long[] dims = new long[ inputImage.numDimensions() ]; - inputImage.dimensions( dims ); - dRho = 2 * computeLength( dims ) / ( double ) inNRho; - threshold = inputImage.firstElement().createVariable(); - nRho = inNRho; - nTheta = inNTheta; - theta = new double[ inNTheta ]; - rho = new double[ inNRho ]; - rtPeaks = null; - } - - public void setThreshold( final T inThreshold ) - { - threshold.set( inThreshold ); - } - - @Override - public boolean process() - { - final Cursor< T > imageCursor = getImage().localizingCursor(); - final long[] position = new long[ getImage().numDimensions() ]; - final double minTheta = -Math.PI / 2; - - final long[] dims = new long[ getImage().numDimensions() ]; - getImage().dimensions( dims ); - final double minRho = -computeLength( dims ); - - boolean success; - - for ( int t = 0; t < nTheta; ++t ) - { - theta[ t ] = dTheta * t + minTheta; - } - - for ( int r = 0; r < nRho; ++r ) - { - rho[ r ] = dRho * r + minRho; - } - - while ( imageCursor.hasNext() ) - { - double fRho; - int r; - final int[] voteLoc = new int[ 2 ]; - - imageCursor.fwd(); - imageCursor.localize( position ); - - for ( int t = 0; t < nTheta; ++t ) - { - if ( imageCursor.get().compareTo( threshold ) > 0 ) - { - fRho = Math.cos( theta[ t ] ) * position[ 0 ] + Math.sin( theta[ t ] ) * position[ 1 ]; - r = Math.round( ( float ) ( ( fRho - minRho ) / dRho ) ); - voteLoc[ 0 ] = r; - voteLoc[ 1 ] = t; - // place vote - super.placeVote( voteLoc ); - } - } - } - // pick peaks - success = super.pickPeaks(); - return success; - } - - public ArrayList< double[] > getTranslatedPeakList() - { - if ( rtPeaks == null ) - { - final ArrayList< long[] > peaks = getPeakList(); - rtPeaks = new ArrayList<>( peaks.size() ); - for ( final long[] irt : peaks ) - { - final double[] rt = new double[ 2 ]; - rt[ 0 ] = rho[ ( int ) irt[ 0 ] ]; - rt[ 1 ] = theta[ ( int ) irt[ 1 ] ]; - rtPeaks.add( rt ); - } - } - - return rtPeaks; - } - -} diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransform.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransform.java deleted file mode 100644 index 910da457c..000000000 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransform.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * #%L - * ImgLib2: a general-purpose, multidimensional image processing library. - * %% - * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, - * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, - * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, - * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, - * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, - * Jean-Yves Tinevez and Michael Zinsmaier. - * %% - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * #L% - */ - -package net.imglib2.algorithm.hough; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import net.imglib2.Point; -import net.imglib2.RandomAccess; -import net.imglib2.algorithm.OutputAlgorithm; -import net.imglib2.algorithm.localextrema.LocalExtrema; -import net.imglib2.algorithm.localextrema.LocalExtrema.MaximumCheck; -import net.imglib2.img.Img; -import net.imglib2.img.ImgFactory; -import net.imglib2.img.array.ArrayImgFactory; -import net.imglib2.type.NativeType; -import net.imglib2.type.Type; -import net.imglib2.type.numeric.RealType; - -/** - * This abstract class provides some basic functionality for use with arbitrary - * Hough-like transforms. - * - * @param - * the data type used for storing votes, usually IntType, but - * possibly LongType or even DoubleType. - * @param - * the data type of the input image. - * @author Larry Lindsey - * @author Yili Zhao - * @author Gabe Selzer - */ -public abstract class HoughTransform< S extends RealType< S > & NativeType< S >, T extends Type< T > & Comparable< T > > implements OutputAlgorithm< Img< S > > -{ - - private String errorMsg; - - private final Img< T > image; - - private final Img< S > voteSpace; - - private RandomAccess< S > voteCursor; - - private ArrayList< long[] > peaks; - - private final double[] peakExclusion; - - private final S one; - - /** - * Constructor for a HoughTransform using an ArrayImageFactory to back the - * ImageFactory used to generate the voteSpace image. - * - * @param inputImage - * the image for the HoughTransform to operate over. - * @param voteSize - * array indicating the size of the voteSpace. This is passed - * directly into ImageFactory to create a voteSpace image. - * @param type - * the Type used for generating the voteSpace image. - */ - protected HoughTransform( final Img< T > inputImage, final long[] voteSize, final S type ) - { - this( inputImage, voteSize, new ArrayImgFactory<>(), type ); - } - - /** - * Constructor for a HoughTransform with a specific ImageFactory. Use this - * if you have something specific in mind as to how the vote data should be - * stored. - * - * @param inputImage - * the image for the HoughTransform to operate over. - * @param voteSize - * array indicating the size of the voteSpace. This is passed - * directly into ImageFactory to create a voteSpace image. - * @param voteFactory - * the ImgFactory used to generate the voteSpace image. - */ - protected HoughTransform( final Img< T > inputImage, final long[] voteSize, final ImgFactory< S > voteFactory, final S type ) - { - image = inputImage; - voteCursor = null; - voteSpace = voteFactory.create( voteSize, type ); - peaks = null; - peakExclusion = new double[ voteSize.length ]; - one = type.createVariable(); - one.setOne(); - Arrays.fill( peakExclusion, 0 ); - } - - /** - * Place a vote with a specific value. - * - * @param loc - * the integer array indicating the location where the vote is to - * be placed in voteSpace. - * @param vote - * the value of the vote - * @return whether the vote was successful. This here particular method - * should always return true. - */ - protected boolean placeVote( final int[] loc, final S vote ) - { - if ( voteCursor == null ) - { - voteCursor = voteSpace.randomAccess(); - } - - voteCursor.setPosition( loc ); - voteCursor.get().add( vote ); - - return true; - } - - /** - * Place a vote of value 1. - * - * @param loc - * the integer array indicating the location where the vote is to - * be placed in voteSpace. - * @return whether the vote was successful. This here particular method - * should always return true. - */ - protected boolean placeVote( final int[] loc ) - { - return placeVote( loc, one ); - } - - /** - * Returns an ArrayList of long arrays, representing the positions in the - * vote space that correspond to peaks. - * - * @return an ArrayList of vote space peak locations. - */ - public ArrayList< long[] > getPeakList() - { - return peaks; - } - - public boolean setExclusion( final double[] newExclusion ) - { - if ( newExclusion.length >= peakExclusion.length ) - { - System.arraycopy( newExclusion, 0, peakExclusion, 0, peakExclusion.length ); - return true; - } - return false; - } - - protected void setErrorMsg( final String msg ) - { - errorMsg = msg; - } - - /** - * Pick vote space peaks with a {@link LocalExtrema}. - * - * @return whether peak picking was successful - */ - protected boolean pickPeaks() - { - S maxValue = voteSpace.firstElement().copy(); - maxValue.setReal( maxValue.getRealDouble() ); - final MaximumCheck< S > maxCheck = new MaximumCheck< S >( maxValue ); - - List< Point > peaksList = LocalExtrema.findLocalExtrema( voteSpace, maxCheck ); - peaks = new ArrayList<>(); - - long[] dims = new long[ image.numDimensions() ]; - - for ( Point p : peaksList ) - { - for ( int i = 0; i < p.numDimensions(); i++ ) - { - dims[ i ] = p.getLongPosition( i ); - } - peaks.add( Arrays.copyOf( dims, dims.length ) ); - } - - return true; - } - - @Override - public boolean checkInput() - { - return voteSpace != null; - } - - @Override - public String getErrorMessage() - { - return errorMsg; - } - - public Img< T > getImage() - { - return image; - } - - @Override - public Img< S > getResult() - { - return voteSpace; - } - -} diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java new file mode 100644 index 000000000..1b88c90f8 --- /dev/null +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -0,0 +1,257 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package net.imglib2.algorithm.hough; + +import java.util.List; + +import net.imglib2.Cursor; +import net.imglib2.Point; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.localextrema.LocalExtrema; +import net.imglib2.algorithm.localextrema.LocalExtrema.MaximumCheck; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.util.Util; +import net.imglib2.view.Views; + +/** + * This abstract class provides some basic functionality for use with arbitrary + * Hough-like transforms. + * + * @param + * the data type of the input image. + * @author Larry Lindsey + * @author Yili Zhao + * @author Gabe Selzer + */ +public class HoughTransforms< T extends RealType< T > & Comparable< T > > +{ + + public static final int DEFAULT_THETA = 180; + + /** + * Calculates the geometric distance between [0, ... , 0] and position. + * + * @param position + * - the position of the second point + * @return {@code float} - the distance between the two points. + */ + final private static float computeLength( final long[] position ) + { + float dist = 0; + + for ( int d = 0; d < position.length; ++d ) + { + final long pos = position[ d ]; + + dist += pos * pos; + } + + return ( float ) Math.sqrt( dist ); + } + + /** + * Calculates a default number of rho bins, which corresponds to a + * resolution of one pixel. + * + * @param inputImage + * the {@link RandomAccessibleInterval} in question. + * @return default number of rho bins. + */ + private static int defaultRho( final RandomAccessibleInterval< ? > inputImage ) + { + final long[] dims = new long[ inputImage.numDimensions() ]; + inputImage.dimensions( dims ); + return ( int ) ( 2 * computeLength( dims ) ); + } + + /** + * Returns the size of the vote space output image given an input + * {@link RandomAccessibleInterval}. + * + * @param input + * - the {@link RandomAccessibleInterval} over which the Hough + * Line Transform will be run + * @return {@code long[]} - the dimensions of the vote space image + */ + public static < T extends RealType< T > > long[] getVotespaceSize( RandomAccessibleInterval< T > input ) + { + return new long[] { defaultRho( input ), DEFAULT_THETA }; + } + + /** + * Returns the size of the voteSpace output image given desired {@code nRho} + * and {@code nTheta} values. + * + * @param nRho + * - the number of bins for rho resolution + * @param nTheta + * - the number of bins for theta resolution + * @return {@code long[]} - the dimensions of the vote space image. + */ + public static long[] getVotespaceSize( int nRho, int nTheta ) + { + return new long[] { nRho, nTheta }; + } + + /** + * Pick vote space peaks with a {@link LocalExtrema}. + * + * @return {@code List} - a list of all of the local maxima of the + * {@code voteSpace}. + */ + public static < T extends RealType< T > & NativeType< T > > List< Point > pickPeaks( RandomAccessibleInterval< T > voteSpace ) + { + T maxValue = Util.getTypeFromInterval( voteSpace ).createVariable(); + maxValue.setReal( maxValue.getMaxValue() ); + final MaximumCheck< T > maxCheck = new MaximumCheck<>( maxValue ); + + return LocalExtrema.findLocalExtrema( voteSpace, maxCheck ); + } + + /** + * Runs a Hough Line Tranform on an image and populates the vote space + * parameter with the results. + * + * @param input + * - the {@link RandomAccessibleInterval} to run the Hough Line + * Transform over + * @param votespace + * - the {@link RandomAccessibleInterval} in which the results + * are stored + */ + public static < T extends RealType< T > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< T > votespace ) + { + voteLines( input, votespace, defaultRho( input ), DEFAULT_THETA ); + } + + /** + * Runs a Hough Line Tranform on an image and populates the vote space + * parameter with the results. + * + * @param input + * - the {@link RandomAccessibleInterval} to run the Hough Line + * Transform over + * @param votespace + * - the {@link RandomAccessibleInterval} in which the results + * are stored + * @param nRho + * - the number of bins for rho resolution + * @param nTheta + * - the number of bins for theta resolution + */ + public static < T extends RealType< T > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< T > votespace, final int nRho, final int nTheta ) + { + voteLines( input, votespace, nRho, nTheta, Util.getTypeFromInterval( input ).createVariable() ); + } + + /** + * Runs a Hough Line Tranform on an image and populates the vote space + * parameter with the results. + * + * @param input + * - the {@link RandomAccessibleInterval} to run the Hough Line + * Transform over + * @param votespace + * - the {@link RandomAccessibleInterval} in which the results + * are stored + * @param nRho + * - the number of bins for rho resolution + * @param nTheta + * - the number of bins for theta resolution + * @param threshold + * - the minimum value allowed by the populator. Any input less + * than or equal to this value will be disregarded by the + * populator. + */ + public static < T extends RealType< T > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< T > votespace, final int nRho, final int nTheta, final T threshold ) + { + + final long[] dims = new long[ input.numDimensions() ]; + input.dimensions( dims ); + + final Cursor< T > imageCursor = Views.iterable( input ).localizingCursor(); + final RandomAccess< T > outputRA = votespace.randomAccess(); + + final double minRho = -computeLength( dims ); + final double dRho = 2 * computeLength( dims ) / ( double ) nRho; + + final double minTheta = -Math.PI / 2; + final double dTheta = Math.PI / nTheta; + + final double[] rho = new double[ nRho ]; + final double[] theta = new double[ nTheta ]; + + for ( int t = 0; t < nTheta; ++t ) + { + theta[ t ] = dTheta * t + minTheta; + } + + for ( int r = 0; r < nRho; ++r ) + { + rho[ r ] = dRho * r + minRho; + } + + final long[] position = new long[ input.numDimensions() ]; + + final T one = Util.getTypeFromInterval( input ).createVariable(); + one.setOne(); + + while ( imageCursor.hasNext() ) + { + double fRho; + int r; + final int[] voteLoc = new int[ 2 ]; + + imageCursor.fwd(); + imageCursor.localize( position ); + + for ( int t = 0; t < nTheta; ++t ) + { + if ( imageCursor.get().compareTo( threshold ) > 0 ) + { + fRho = Math.cos( theta[ t ] ) * position[ 0 ] + Math.sin( theta[ t ] ) * position[ 1 ]; + r = Math.round( ( float ) ( ( fRho - minRho ) / dRho ) ); + voteLoc[ 0 ] = r; + voteLoc[ 1 ] = t; + // place vote + outputRA.setPosition( voteLoc ); + outputRA.get().add( one ); + } + } + } + } +} diff --git a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java index 02af7bad8..d5f362f3f 100644 --- a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java +++ b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java @@ -40,15 +40,18 @@ import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.io.IOException; +import java.util.Arrays; import javax.imageio.ImageIO; import net.imglib2.RandomAccess; import net.imglib2.img.Img; +import net.imglib2.img.array.ArrayImgFactory; import net.imglib2.img.array.ArrayImgs; import net.imglib2.type.NativeType; import net.imglib2.type.numeric.RealType; import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.util.Util; import org.junit.Before; import org.junit.Test; @@ -88,23 +91,27 @@ public void setUp() throws IOException @Test public < T extends RealType< T > & NativeType< T > > void testHoughLineTransformToTarget() throws IOException { - final HoughLineTransform line = HoughLineTransform.integerHoughLine( tpImg ); + //create votespace + final long[] dims = new long[ tpImg.numDimensions() ]; + tpImg.dimensions( dims ); + final long[] outputDims = HoughTransforms.getVotespaceSize( tpImg ); + final Img< UnsignedByteType > votespace = new ArrayImgFactory().create( outputDims, tpImg.firstElement() ); - boolean success = line.process(); - assertTrue( success ); - final Img< T > result = line.getResult(); - final long[] dims = new long[ result.numDimensions() ]; - result.dimensions( dims ); - final RandomAccess< T > ra = result.randomAccess(); + //run transform + HoughTransforms.voteLines( tpImg, votespace ); + //compare expected / actual dimensions int height = groundTruth.getHeight(); int width = groundTruth.getWidth(); - assertEquals( "Ground truth and result height do not match.", height, dims[ 1 ] ); - assertEquals( "Ground truth and result width do not match.", width, dims[ 0 ] ); + assertEquals( "Ground truth and result height do not match.", height, outputDims[ 1 ] ); + assertEquals( "Ground truth and result width do not match.", width, outputDims[ 0 ] ); DataBufferByte db = ( DataBufferByte ) groundTruth.getData().getDataBuffer(); byte[] expected = db.getBankData()[ 0 ]; + + final RandomAccess< UnsignedByteType > ra = votespace.randomAccess(); + //compare expected / actual results for regression. int index = 0; for ( int j = 0; j < height; ++j ) { From 6f673aba3d113fe367c88ad8d9f910ead5939165 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 26 Apr 2018 14:59:18 -0500 Subject: [PATCH 09/42] PickPeaks: Allow user input for min peak value. --- .../java/net/imglib2/algorithm/hough/HoughTransforms.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 1b88c90f8..42d445e56 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -133,11 +133,9 @@ public static long[] getVotespaceSize( int nRho, int nTheta ) * @return {@code List} - a list of all of the local maxima of the * {@code voteSpace}. */ - public static < T extends RealType< T > & NativeType< T > > List< Point > pickPeaks( RandomAccessibleInterval< T > voteSpace ) + public static < T extends RealType< T > & NativeType< T > > List< Point > pickPeaks( RandomAccessibleInterval< T > voteSpace, T minPeak ) { - T maxValue = Util.getTypeFromInterval( voteSpace ).createVariable(); - maxValue.setReal( maxValue.getMaxValue() ); - final MaximumCheck< T > maxCheck = new MaximumCheck<>( maxValue ); + final MaximumCheck< T > maxCheck = new MaximumCheck<>( minPeak ); return LocalExtrema.findLocalExtrema( voteSpace, maxCheck ); } From d95ef4189702e7b295e1eed40b501fed2d33a473 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 26 Apr 2018 15:05:15 -0500 Subject: [PATCH 10/42] PickPeaks: Translate votespace for correct output PickPeaks now returns a rho value between [-maxRho, maxRho), whereas before it returned a rho value between [0, maxRho * 2). Translating the votespace before finding the peaks allows for easier manipulation of the polar coordinates, and prevents the user from having to translate the coordinates or votespace themselves. --- .../net/imglib2/algorithm/hough/HoughTransforms.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 42d445e56..38eb9513b 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -45,6 +45,7 @@ import net.imglib2.type.NativeType; import net.imglib2.type.numeric.RealType; import net.imglib2.util.Util; +import net.imglib2.view.IntervalView; import net.imglib2.view.Views; /** @@ -137,7 +138,15 @@ public static < T extends RealType< T > & NativeType< T > > List< Point > pickPe { final MaximumCheck< T > maxCheck = new MaximumCheck<>( minPeak ); - return LocalExtrema.findLocalExtrema( voteSpace, maxCheck ); + // since the vote space runs [0, maxRho * 2) to allow both positive and + // negative rho values without having the user to reinterval their + // vote space, we need to translate the vote space by {@code -maxRho} in + // the first dimension so that we return accurate coordinates from the + // vote space. + long[] translation = { -( voteSpace.dimension( 0 ) / 2 ), 0 }; + IntervalView< T > translatedVotes = Views.translate( voteSpace, translation ); + + return LocalExtrema.findLocalExtrema( translatedVotes, maxCheck ); } /** From 9885dd1ec83a966dc3b18bc89dbe423d4c2d4ed6 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 26 Apr 2018 15:10:20 -0500 Subject: [PATCH 11/42] LineTest: Add PickPeaks regression test --- .../hough/HoughLineTransformTest.java | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java index d5f362f3f..08ccaea5f 100644 --- a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java +++ b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java @@ -35,20 +35,19 @@ package net.imglib2.algorithm.hough; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.io.IOException; -import java.util.Arrays; +import java.util.List; import javax.imageio.ImageIO; +import net.imglib2.Point; import net.imglib2.RandomAccess; import net.imglib2.img.Img; import net.imglib2.img.array.ArrayImgFactory; import net.imglib2.img.array.ArrayImgs; -import net.imglib2.type.NativeType; import net.imglib2.type.numeric.RealType; import net.imglib2.type.numeric.integer.UnsignedByteType; import net.imglib2.util.Util; @@ -125,4 +124,34 @@ public < T extends RealType< T > & NativeType< T > > void testHoughLineTransform } } } + + @Test + public < T extends RealType< T > > void testPickPeaks() + { + // three expected peaks + final long[] expected = { 24, 89, 24, 90, 25, 91 }; + int index = 0; + + // create votespace + final long[] dims = new long[ tpImg.numDimensions() ]; + tpImg.dimensions( dims ); + final long[] outputDims = HoughTransforms.getVotespaceSize( tpImg ); + final Img< UnsignedByteType > votespace = new ArrayImgFactory().create( outputDims, tpImg.firstElement() ); + + // run transform + HoughTransforms.voteLines( tpImg, votespace ); + + final UnsignedByteType minPeak = Util.getTypeFromInterval( votespace ).createVariable(); + minPeak.setReal( 21 ); + + List< Point > peaks = HoughTransforms.pickPeaks( votespace, minPeak ); + for ( Point p : peaks ) + { + // assert dimension 0 + assertEquals( expected[ index++ ], p.getLongPosition( 0 ), 0 ); + // assert dimension 1 + assertEquals( expected[ index++ ], p.getLongPosition( 1 ), 0 ); + } + } + } From ca8b4f5b003efe8c4df7cb02747671cc255009b4 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 26 Apr 2018 15:11:38 -0500 Subject: [PATCH 12/42] HoughTransforms: Add slope/intercept converters These methods convert the polar output of PickPeaks into more useful slope and intercept values for lines. In the case of an undefined slope getSlope() returns Double.POSITIVE_INFINITY or DOUBLE.NEGATIVE_INFINITY --- .../algorithm/hough/HoughTransforms.java | 42 +++++++++++++++++++ .../hough/HoughLineTransformTest.java | 13 ++++++ 2 files changed, 55 insertions(+) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 38eb9513b..1c1fe4054 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -261,4 +261,46 @@ public static < T extends RealType< T > > void voteLines( final RandomAccessible } } } + + /** + * Method used to convert the {rho, theta} output of the + * {@link HoughTransforms#voteLines} algorithm into a more useful + * y-intercept value. Used with {@link HoughTransforms#getSlope} to create + * line equations. + * + * @param rho + * - the {@code rho} of the line + * @param theta + * - the {@code theta} of the line + * @return {@code double} - the y-intercept of the line + */ + public static double getIntercept( final long rho, final long theta ) + { + double radians = ( Math.PI * theta ) / 180; + return ( rho / Math.sin( radians ) ); + } + + /** + * Method used to convert the {rho, theta} output of the + * {@link HoughTransforms#voteLines} algorithm into a more useful slope + * value. Used with {@link HoughTransforms#getIntercept} to create line + * equations. + * + * @param theta + * - the {@code theta} of the line + * @return {@code double} - the y-intercept of the line + */ + public static double getSlope( final long theta ) + { + double radians = ( Math.PI * theta ) / 180; + + double n = -Math.cos( radians ); + double d = Math.sin( radians ); + + // to avoid a divide by zero error return an infinite slope if the + // denominator is zero. + if ( d == 0 ) + return n > 0 ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY; + return ( n / d ); + } } diff --git a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java index 08ccaea5f..dd12c6331 100644 --- a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java +++ b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java @@ -154,4 +154,17 @@ public < T extends RealType< T > > void testPickPeaks() } } + @Test + public void testEquationConversion() + { + final int rho = 2, theta = 45; + final double slope = HoughTransforms.getSlope( theta ); + final double yint = HoughTransforms.getIntercept( rho, theta ); + + assertEquals( -1.0000000000000002, slope, 0 ); + assertEquals( 2.8284271247461903, yint, 0 ); + + // make sure undefined slope does not throw an error. + assertEquals( HoughTransforms.getSlope( 0 ), Double.NEGATIVE_INFINITY, 0 ); + } } From 45b290964f20fd3efb3d0173a8e7a7df4189debe Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 26 Apr 2018 15:14:18 -0500 Subject: [PATCH 13/42] Cleanup / format --- .../imglib2/algorithm/hough/HoughTransforms.java | 9 ++++++--- .../algorithm/hough/HoughLineTransformTest.java | 15 ++++++++------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 1c1fe4054..964879627 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -210,9 +210,6 @@ public static < T extends RealType< T > > void voteLines( final RandomAccessible final long[] dims = new long[ input.numDimensions() ]; input.dimensions( dims ); - final Cursor< T > imageCursor = Views.iterable( input ).localizingCursor(); - final RandomAccess< T > outputRA = votespace.randomAccess(); - final double minRho = -computeLength( dims ); final double dRho = 2 * computeLength( dims ) / ( double ) nRho; @@ -222,16 +219,21 @@ public static < T extends RealType< T > > void voteLines( final RandomAccessible final double[] rho = new double[ nRho ]; final double[] theta = new double[ nTheta ]; + // create theta LUT for ( int t = 0; t < nTheta; ++t ) { theta[ t ] = dTheta * t + minTheta; } + // create rho LUT for ( int r = 0; r < nRho; ++r ) { rho[ r ] = dRho * r + minRho; } + final Cursor< T > imageCursor = Views.iterable( input ).localizingCursor(); + final RandomAccess< T > outputRA = votespace.randomAccess(); + final long[] position = new long[ input.numDimensions() ]; final T one = Util.getTypeFromInterval( input ).createVariable(); @@ -254,6 +256,7 @@ public static < T extends RealType< T > > void voteLines( final RandomAccessible r = Math.round( ( float ) ( ( fRho - minRho ) / dRho ) ); voteLoc[ 0 ] = r; voteLoc[ 1 ] = t; + // place vote outputRA.setPosition( voteLoc ); outputRA.get().add( one ); diff --git a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java index dd12c6331..824ad74ed 100644 --- a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java +++ b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java @@ -56,7 +56,8 @@ import org.junit.Test; /** - * Tests {@link HoughLineTransform}. + * Tests {@link HoughTransforms#voteLines}, {@link HoughTransforms#pickPeaks}, + * {@link HoughTransforms#getSlope}, and {@link HoughTransforms#getIntercept}. * * @author Yili Zhao * @author Gabe Selzer @@ -88,18 +89,18 @@ public void setUp() throws IOException } @Test - public < T extends RealType< T > & NativeType< T > > void testHoughLineTransformToTarget() throws IOException + public < T extends RealType< T > > void testHoughLineTransformToTarget() throws IOException { - //create votespace + // create votespace final long[] dims = new long[ tpImg.numDimensions() ]; tpImg.dimensions( dims ); final long[] outputDims = HoughTransforms.getVotespaceSize( tpImg ); final Img< UnsignedByteType > votespace = new ArrayImgFactory().create( outputDims, tpImg.firstElement() ); - //run transform + // run transform HoughTransforms.voteLines( tpImg, votespace ); - //compare expected / actual dimensions + // compare expected / actual dimensions int height = groundTruth.getHeight(); int width = groundTruth.getWidth(); assertEquals( "Ground truth and result height do not match.", height, outputDims[ 1 ] ); @@ -107,10 +108,10 @@ public < T extends RealType< T > & NativeType< T > > void testHoughLineTransform DataBufferByte db = ( DataBufferByte ) groundTruth.getData().getDataBuffer(); byte[] expected = db.getBankData()[ 0 ]; - + final RandomAccess< UnsignedByteType > ra = votespace.randomAccess(); - //compare expected / actual results for regression. + // compare expected / actual results for regression. int index = 0; for ( int j = 0; j < height; ++j ) { From f232cc618f9c379cfadd40c419a7d5027fe20b73 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 26 Apr 2018 17:57:24 -0500 Subject: [PATCH 14/42] Improve line transform documentation Javadoc adapted from previous commit c1952b1, HoughTransforms.java, line 50 --- .../algorithm/hough/HoughTransforms.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 964879627..6773c8d79 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -186,8 +186,42 @@ public static < T extends RealType< T > > void voteLines( final RandomAccessible } /** + * * Runs a Hough Line Tranform on an image and populates the vote space * parameter with the results. + *

+ * Vote space here has two dimensions: {@code rho} and {@code theta}. + * {@code theta} is measured in radians {@code [-pi/2 pi/2)}, {@code rho} is + * measured in {@code [-rhoMax, rhoMax)}. + *

+ *

+ * Lines are modeled as + *

+ * + *
+	 * l(t) = | x | = rho * |  cos(theta) | + t * | sin(theta) |
+	 *        | y |         | -sin(theta) |       | cos(theta) |
+	 * 
+ *

+ * In other words, {@code rho} represents the signed minimum distance from + * the image origin to the line, and {@code theta} indicates the angle + * between the row-axis and the minimum offset vector. + *

+ *

+ * For a given point, then, votes are placed along the curve + *

+ * + *
+	 * rho = y * sin( theta ) + x * cos( theta )
+	 * 
+ *

+ * It is important to note that the interval of the first dimension of the + * vote space image is NOT {@code [-maxRho, maxRho)} but instead + * {@code [0, maxRho * 2)}. Thus if {@link HoughTransforms#pickPeaks} is not + * used to retrieve the maxima from the vote space, the vote space will have + * to be translated by {@code -maxRho} in dimension 0 to get the correct + * {@code rho} and {@code theta} values from the vote space. + *

* * @param input * - the {@link RandomAccessibleInterval} to run the Hough Line From ecb8a4b975882ac826422ed7c743a0a88b06d998 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 3 May 2018 14:26:32 -0500 Subject: [PATCH 15/42] computeLength: allow larger return values --- .../java/net/imglib2/algorithm/hough/HoughTransforms.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 6773c8d79..e01b15d79 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -68,11 +68,11 @@ public class HoughTransforms< T extends RealType< T > & Comparable< T > > * * @param position * - the position of the second point - * @return {@code float} - the distance between the two points. + * @return {@code double} - the distance between the two points. */ - final private static float computeLength( final long[] position ) + final private static double computeLength( final long[] position ) { - float dist = 0; + double dist = 0; for ( int d = 0; d < position.length; ++d ) { @@ -81,7 +81,7 @@ final private static float computeLength( final long[] position ) dist += pos * pos; } - return ( float ) Math.sqrt( dist ); + return Math.sqrt( dist ); } /** From 1518e4fcfef01a3d600abf59f0e57e0ed69c7bc7 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 3 May 2018 14:27:36 -0500 Subject: [PATCH 16/42] defaultRho: simplify instructions to one-liner --- .../java/net/imglib2/algorithm/hough/HoughTransforms.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index e01b15d79..a53cdc13a 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -44,6 +44,7 @@ import net.imglib2.algorithm.localextrema.LocalExtrema.MaximumCheck; import net.imglib2.type.NativeType; import net.imglib2.type.numeric.RealType; +import net.imglib2.util.Intervals; import net.imglib2.util.Util; import net.imglib2.view.IntervalView; import net.imglib2.view.Views; @@ -94,9 +95,7 @@ final private static double computeLength( final long[] position ) */ private static int defaultRho( final RandomAccessibleInterval< ? > inputImage ) { - final long[] dims = new long[ inputImage.numDimensions() ]; - inputImage.dimensions( dims ); - return ( int ) ( 2 * computeLength( dims ) ); + return ( int ) ( 2 * computeLength( Intervals.dimensionsAsLongArray( inputImage ) ) ); } /** From 694205d92671a22fb017bb89898d000bd955b1ad Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 3 May 2018 14:39:48 -0500 Subject: [PATCH 17/42] pickPeaks: loosen typing of parameters --- src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index a53cdc13a..f2e835333 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -133,7 +133,7 @@ public static long[] getVotespaceSize( int nRho, int nTheta ) * @return {@code List} - a list of all of the local maxima of the * {@code voteSpace}. */ - public static < T extends RealType< T > & NativeType< T > > List< Point > pickPeaks( RandomAccessibleInterval< T > voteSpace, T minPeak ) + public static < T extends Comparable< T > > List< Point > pickPeaks( RandomAccessibleInterval< T > voteSpace, T minPeak ) { final MaximumCheck< T > maxCheck = new MaximumCheck<>( minPeak ); From af244cbd3a163b84aa5f974bef85e64f12728288 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 3 May 2018 15:05:25 -0500 Subject: [PATCH 18/42] voteLines: Restrict voteSpace to only IntegerType Since the values in the votespace are only ever incremented by one in this particular transform we restrict the votespace to RandomAccessibleIntervals of types extending IntegerType only --- .../net/imglib2/algorithm/hough/HoughTransforms.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index f2e835333..4b6889b6c 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -43,6 +43,7 @@ import net.imglib2.algorithm.localextrema.LocalExtrema; import net.imglib2.algorithm.localextrema.LocalExtrema.MaximumCheck; import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.IntegerType; import net.imglib2.type.numeric.RealType; import net.imglib2.util.Intervals; import net.imglib2.util.Util; @@ -159,7 +160,7 @@ public static < T extends Comparable< T > > List< Point > pickPeaks( RandomAcces * - the {@link RandomAccessibleInterval} in which the results * are stored */ - public static < T extends RealType< T > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< T > votespace ) + public static < T extends RealType< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace ) { voteLines( input, votespace, defaultRho( input ), DEFAULT_THETA ); } @@ -179,7 +180,7 @@ public static < T extends RealType< T > > void voteLines( final RandomAccessible * @param nTheta * - the number of bins for theta resolution */ - public static < T extends RealType< T > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< T > votespace, final int nRho, final int nTheta ) + public static < T extends RealType< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nRho, final int nTheta ) { voteLines( input, votespace, nRho, nTheta, Util.getTypeFromInterval( input ).createVariable() ); } @@ -237,7 +238,7 @@ public static < T extends RealType< T > > void voteLines( final RandomAccessible * than or equal to this value will be disregarded by the * populator. */ - public static < T extends RealType< T > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< T > votespace, final int nRho, final int nTheta, final T threshold ) + public static < T extends RealType< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nRho, final int nTheta, final T threshold ) { final long[] dims = new long[ input.numDimensions() ]; @@ -265,11 +266,11 @@ public static < T extends RealType< T > > void voteLines( final RandomAccessible } final Cursor< T > imageCursor = Views.iterable( input ).localizingCursor(); - final RandomAccess< T > outputRA = votespace.randomAccess(); + final RandomAccess< U > outputRA = votespace.randomAccess(); final long[] position = new long[ input.numDimensions() ]; - final T one = Util.getTypeFromInterval( input ).createVariable(); + final U one = Util.getTypeFromInterval( votespace ).createVariable(); one.setOne(); while ( imageCursor.hasNext() ) From 71b4800436e0d77d5aedf2a8a0650a7875f4b85f Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 3 May 2018 15:48:32 -0500 Subject: [PATCH 19/42] pickPeaks: add alternative (convenient) option --- .../algorithm/hough/HoughTransforms.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 4b6889b6c..588a99192 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -128,9 +128,34 @@ public static long[] getVotespaceSize( int nRho, int nTheta ) return new long[] { nRho, nTheta }; } + /** + * Pick vote space peaks with a {@link LocalExtrema}. + * + * @param voteSpace + * - the {@link RandomAccessibleInterval} containing the output + * of a Hough Transform vote + * @param threshold + * - the {@link IntegerType} at and below which maxima will be + * ignored + * @return {@code List} - a list of all of the local maxima of the + * {@code voteSpace} + */ + public static < T extends IntegerType< T > > List< Point > pickPeaks( final RandomAccessibleInterval< T > voteSpace, final long threshold ) + { + final T minPeak = Util.getTypeFromInterval( voteSpace ).createVariable(); + minPeak.setInteger( threshold ); + return pickPeaks( voteSpace, minPeak ); + } + /** * Pick vote space peaks with a {@link LocalExtrema}. * + * @param voteSpace + * - the {@link RandomAccessibleInterval} containing the output + * of a Hough Transform vote + * @param minPeak + * - the {@link Comparable} at and below which maxima will be + * ignored * @return {@code List} - a list of all of the local maxima of the * {@code voteSpace}. */ From beb1144d0e63ccca0d6bb5df1c9b23984e239956 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 3 May 2018 16:50:23 -0500 Subject: [PATCH 20/42] voteLines: Relax typing of input RAI --- .../algorithm/hough/HoughTransforms.java | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 588a99192..e583f1400 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -35,6 +35,7 @@ package net.imglib2.algorithm.hough; import java.util.List; +import java.util.function.Predicate; import net.imglib2.Cursor; import net.imglib2.Point; @@ -185,7 +186,7 @@ public static < T extends Comparable< T > > List< Point > pickPeaks( RandomAcces * - the {@link RandomAccessibleInterval} in which the results * are stored */ - public static < T extends RealType< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace ) + public static < T extends Comparable< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace ) { voteLines( input, votespace, defaultRho( input ), DEFAULT_THETA ); } @@ -205,9 +206,34 @@ public static < T extends RealType< T >, U extends IntegerType< U > > void voteL * @param nTheta * - the number of bins for theta resolution */ - public static < T extends RealType< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nRho, final int nTheta ) + public static < T extends Comparable< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nRho, final int nTheta ) { - voteLines( input, votespace, nRho, nTheta, Util.getTypeFromInterval( input ).createVariable() ); + voteLines( input, votespace, nRho, nTheta, Util.getTypeFromInterval( input ) ); + } + + /** + * Runs a Hough Line Tranform on an image and populates the vote space + * parameter with the results. + * + * @param input + * - the {@link RandomAccessibleInterval} to run the Hough Line + * Transform over + * @param votespace + * - the {@link RandomAccessibleInterval} in which the results + * are stored + * @param nRho + * - the number of bins for rho resolution + * @param nTheta + * - the number of bins for theta resolution + * @param threshold + * - the minimum value allowed by the populator. Any input less + * than or equal to this value will be disregarded by the + * populator. + */ + public static < T extends Comparable< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nRho, final int nTheta, final T threshold ) + { + Predicate< T > p = o -> threshold.compareTo( o ) > 0; + voteLines( input, votespace, nRho, nTheta, p ); } /** @@ -258,12 +284,13 @@ public static < T extends RealType< T >, U extends IntegerType< U > > void voteL * - the number of bins for rho resolution * @param nTheta * - the number of bins for theta resolution - * @param threshold - * - the minimum value allowed by the populator. Any input less - * than or equal to this value will be disregarded by the + * @param filter + * - a {@link Predicate} judging whether or not the a value is + * above the minimum value allowed by the populator. Any input + * less than or equal to this value will be disregarded by the * populator. */ - public static < T extends RealType< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nRho, final int nTheta, final T threshold ) + public static < T, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nRho, final int nTheta, final Predicate< T > filter ) { final long[] dims = new long[ input.numDimensions() ]; @@ -309,7 +336,7 @@ public static < T extends RealType< T >, U extends IntegerType< U > > void voteL for ( int t = 0; t < nTheta; ++t ) { - if ( imageCursor.get().compareTo( threshold ) > 0 ) + if ( filter.test( imageCursor.get() ) ) { fRho = Math.cos( theta[ t ] ) * position[ 0 ] + Math.sin( theta[ t ] ) * position[ 1 ]; r = Math.round( ( float ) ( ( fRho - minRho ) / dRho ) ); From 69eac4eff07e911ca74bd56720747a1367e40269 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 3 May 2018 18:02:21 -0500 Subject: [PATCH 21/42] fixup! pickPeaks: add alternative (convenient) option --- src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index e583f1400..16e63426f 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -232,7 +232,7 @@ public static < T extends Comparable< T >, U extends IntegerType< U > > void vot */ public static < T extends Comparable< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nRho, final int nTheta, final T threshold ) { - Predicate< T > p = o -> threshold.compareTo( o ) > 0; + Predicate< T > p = o -> threshold.compareTo( o ) < 0; voteLines( input, votespace, nRho, nTheta, p ); } From 867e1c79a90ecde568d51ddc5b615d9e9cf7c628 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 3 May 2018 18:03:17 -0500 Subject: [PATCH 22/42] voteLines: Remove unnecessary variables --- .../net/imglib2/algorithm/hough/HoughTransforms.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 16e63426f..7adaaf0c0 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -322,14 +322,10 @@ public static < T, U extends IntegerType< U > > void voteLines( final RandomAcce final long[] position = new long[ input.numDimensions() ]; - final U one = Util.getTypeFromInterval( votespace ).createVariable(); - one.setOne(); - while ( imageCursor.hasNext() ) { double fRho; int r; - final int[] voteLoc = new int[ 2 ]; imageCursor.fwd(); imageCursor.localize( position ); @@ -340,12 +336,11 @@ public static < T, U extends IntegerType< U > > void voteLines( final RandomAcce { fRho = Math.cos( theta[ t ] ) * position[ 0 ] + Math.sin( theta[ t ] ) * position[ 1 ]; r = Math.round( ( float ) ( ( fRho - minRho ) / dRho ) ); - voteLoc[ 0 ] = r; - voteLoc[ 1 ] = t; // place vote - outputRA.setPosition( voteLoc ); - outputRA.get().add( one ); + outputRA.setPosition( r, 0 ); + outputRA.setPosition( t, 1 ); + outputRA.get().inc(); } } } From 0532b1b97d56e34a4120898abcd0af889900affa Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Mon, 14 May 2018 09:20:30 -0500 Subject: [PATCH 23/42] HoughLineTest: use small delta for slope and yint --- .../net/imglib2/algorithm/hough/HoughLineTransformTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java index 824ad74ed..d54ef69c7 100644 --- a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java +++ b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java @@ -162,8 +162,8 @@ public void testEquationConversion() final double slope = HoughTransforms.getSlope( theta ); final double yint = HoughTransforms.getIntercept( rho, theta ); - assertEquals( -1.0000000000000002, slope, 0 ); - assertEquals( 2.8284271247461903, yint, 0 ); + assertEquals( -1.0, slope, 1e-8 ); + assertEquals( 2 * Math.sqrt( 2 ), yint, 1e-8 ); // make sure undefined slope does not throw an error. assertEquals( HoughTransforms.getSlope( 0 ), Double.NEGATIVE_INFINITY, 0 ); From 9d61af7acc4f7b2d1d85b6e645701ed3506c39bb Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Mon, 14 May 2018 09:51:18 -0500 Subject: [PATCH 24/42] HoughTransforms: Loosen helper method parameters The getVotespaceSize() and defaultRho() method parameter was loosened from being a RandomAccessibleInterval to being a Dimensions object to allow a wider range of acceptible input types --- .../algorithm/hough/HoughTransforms.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 7adaaf0c0..0c1a8d4a2 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -38,6 +38,7 @@ import java.util.function.Predicate; import net.imglib2.Cursor; +import net.imglib2.Dimensions; import net.imglib2.Point; import net.imglib2.RandomAccess; import net.imglib2.RandomAccessibleInterval; @@ -91,27 +92,27 @@ final private static double computeLength( final long[] position ) * Calculates a default number of rho bins, which corresponds to a * resolution of one pixel. * - * @param inputImage - * the {@link RandomAccessibleInterval} in question. + * @param dimensions + * the {@link Dimensions} of the input image. * @return default number of rho bins. */ - private static int defaultRho( final RandomAccessibleInterval< ? > inputImage ) + private static int defaultRho( final Dimensions dimensions ) { - return ( int ) ( 2 * computeLength( Intervals.dimensionsAsLongArray( inputImage ) ) ); + return ( int ) ( 2 * computeLength( Intervals.dimensionsAsLongArray( dimensions ) ) ); } /** * Returns the size of the vote space output image given an input * {@link RandomAccessibleInterval}. * - * @param input - * - the {@link RandomAccessibleInterval} over which the Hough - * Line Transform will be run + * @param dimensions + * - the {@link Dimensions} over which the Hough Line Transform + * will be run * @return {@code long[]} - the dimensions of the vote space image */ - public static < T extends RealType< T > > long[] getVotespaceSize( RandomAccessibleInterval< T > input ) + public static long[] getVotespaceSize( final Dimensions dimensions ) { - return new long[] { defaultRho( input ), DEFAULT_THETA }; + return new long[] { defaultRho( dimensions ), DEFAULT_THETA }; } /** From 28a13da2a0c38138e55122306d0597a6e36154a8 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Mon, 14 May 2018 09:52:57 -0500 Subject: [PATCH 25/42] HoughLine: Remove unnecessary array --- .../java/net/imglib2/algorithm/hough/HoughTransforms.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 0c1a8d4a2..8bc5e7e08 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -321,21 +321,18 @@ public static < T, U extends IntegerType< U > > void voteLines( final RandomAcce final Cursor< T > imageCursor = Views.iterable( input ).localizingCursor(); final RandomAccess< U > outputRA = votespace.randomAccess(); - final long[] position = new long[ input.numDimensions() ]; - while ( imageCursor.hasNext() ) { double fRho; int r; imageCursor.fwd(); - imageCursor.localize( position ); for ( int t = 0; t < nTheta; ++t ) { if ( filter.test( imageCursor.get() ) ) { - fRho = Math.cos( theta[ t ] ) * position[ 0 ] + Math.sin( theta[ t ] ) * position[ 1 ]; + fRho = Math.cos( theta[ t ] ) * imageCursor.getDoublePosition( 0 ) + Math.sin( theta[ t ] ) * imageCursor.getDoublePosition( 1 ); r = Math.round( ( float ) ( ( fRho - minRho ) / dRho ) ); // place vote From f3309eb847b8ff593e87edd77f2dc46bac0564e4 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Tue, 15 May 2018 13:33:38 -0500 Subject: [PATCH 26/42] PickPeaks: Fix translation bug --- .../algorithm/hough/HoughTransforms.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 8bc5e7e08..554058821 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -165,12 +165,13 @@ public static < T extends Comparable< T > > List< Point > pickPeaks( RandomAcces { final MaximumCheck< T > maxCheck = new MaximumCheck<>( minPeak ); - // since the vote space runs [0, maxRho * 2) to allow both positive and - // negative rho values without having the user to reinterval their + // since the vote space runs [0, maxRho * 2) x [0, pi) to allow + // negative rho/pi values without having the user to reinterval their // vote space, we need to translate the vote space by {@code -maxRho} in - // the first dimension so that we return accurate coordinates from the + // the first dimension and by {@code -pi / 2} in the second dimension so + // that we return accurate coordinates from the // vote space. - long[] translation = { -( voteSpace.dimension( 0 ) / 2 ), 0 }; + long[] translation = { -( voteSpace.dimension( 0 ) / 2 ), -( voteSpace.dimension( 1 ) / 2 ) }; IntervalView< T > translatedVotes = Views.translate( voteSpace, translation ); return LocalExtrema.findLocalExtrema( translatedVotes, maxCheck ); @@ -269,9 +270,10 @@ public static < T extends Comparable< T >, U extends IntegerType< U > > void vot *

* It is important to note that the interval of the first dimension of the * vote space image is NOT {@code [-maxRho, maxRho)} but instead - * {@code [0, maxRho * 2)}. Thus if {@link HoughTransforms#pickPeaks} is not - * used to retrieve the maxima from the vote space, the vote space will have - * to be translated by {@code -maxRho} in dimension 0 to get the correct + * {@code [0, maxRho * 2)}; the same applies to the second dimension of the + * vote space as well. Thus if {@link HoughTransforms#pickPeaks} is not used + * to retrieve the maxima from the vote space, the vote space will have to + * be translated by {@code -maxRho} in dimension 0 to get the correct * {@code rho} and {@code theta} values from the vote space. *

* @@ -381,7 +383,7 @@ public static double getSlope( final long theta ) // to avoid a divide by zero error return an infinite slope if the // denominator is zero. - if ( d == 0 ) + if ( Math.abs( n ) == 1 ) return n > 0 ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY; return ( n / d ); } From 195f1631a73ce81b0a547209450d52e3fb6a0d88 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Tue, 15 May 2018 13:34:48 -0500 Subject: [PATCH 27/42] PickPeaks: Rename to PickLinePeaks Since we are translating the vote space before picking peaks (something that future Hough Transforms might not do), we will change the name to something more specific to the line transform. --- .../java/net/imglib2/algorithm/hough/HoughTransforms.java | 6 +++--- .../net/imglib2/algorithm/hough/HoughLineTransformTest.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 554058821..b50a84418 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -142,11 +142,11 @@ public static long[] getVotespaceSize( int nRho, int nTheta ) * @return {@code List} - a list of all of the local maxima of the * {@code voteSpace} */ - public static < T extends IntegerType< T > > List< Point > pickPeaks( final RandomAccessibleInterval< T > voteSpace, final long threshold ) + public static < T extends IntegerType< T > > List< Point > pickLinePeaks( final RandomAccessibleInterval< T > voteSpace, final long threshold ) { final T minPeak = Util.getTypeFromInterval( voteSpace ).createVariable(); minPeak.setInteger( threshold ); - return pickPeaks( voteSpace, minPeak ); + return pickLinePeaks( voteSpace, minPeak ); } /** @@ -161,7 +161,7 @@ public static < T extends IntegerType< T > > List< Point > pickPeaks( final Rand * @return {@code List} - a list of all of the local maxima of the * {@code voteSpace}. */ - public static < T extends Comparable< T > > List< Point > pickPeaks( RandomAccessibleInterval< T > voteSpace, T minPeak ) + public static < T extends Comparable< T > > List< Point > pickLinePeaks( RandomAccessibleInterval< T > voteSpace, T minPeak ) { final MaximumCheck< T > maxCheck = new MaximumCheck<>( minPeak ); diff --git a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java index d54ef69c7..57577c87c 100644 --- a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java +++ b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java @@ -143,9 +143,9 @@ public < T extends RealType< T > > void testPickPeaks() HoughTransforms.voteLines( tpImg, votespace ); final UnsignedByteType minPeak = Util.getTypeFromInterval( votespace ).createVariable(); - minPeak.setReal( 21 ); + minPeak.setReal( 10 ); - List< Point > peaks = HoughTransforms.pickPeaks( votespace, minPeak ); + List< Point > peaks = HoughTransforms.pickLinePeaks( votespace, minPeak ); for ( Point p : peaks ) { // assert dimension 0 From 10dd6cde4bd7647766128fded86edd7e4ae7e7b2 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Tue, 15 May 2018 13:40:30 -0500 Subject: [PATCH 28/42] VoteLines: Refactor parameter order This allows a theta-only (i.e. to allow a theta but not a rho to be passed through) signature to make sense and to keep the same parameter order --- .../algorithm/hough/HoughTransforms.java | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index b50a84418..ac9962188 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -190,7 +190,7 @@ public static < T extends Comparable< T > > List< Point > pickLinePeaks( RandomA */ public static < T extends Comparable< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace ) { - voteLines( input, votespace, defaultRho( input ), DEFAULT_THETA ); + voteLines( input, votespace, DEFAULT_THETA, defaultRho( input ) ); } /** @@ -203,14 +203,12 @@ public static < T extends Comparable< T >, U extends IntegerType< U > > void vot * @param votespace * - the {@link RandomAccessibleInterval} in which the results * are stored - * @param nRho - * - the number of bins for rho resolution * @param nTheta * - the number of bins for theta resolution */ - public static < T extends Comparable< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nRho, final int nTheta ) + public static < T extends Comparable< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nTheta ) { - voteLines( input, votespace, nRho, nTheta, Util.getTypeFromInterval( input ) ); + voteLines( input, votespace, nTheta, defaultRho( input ), Util.getTypeFromInterval( input ) ); } /** @@ -223,19 +221,38 @@ public static < T extends Comparable< T >, U extends IntegerType< U > > void vot * @param votespace * - the {@link RandomAccessibleInterval} in which the results * are stored + * @param nTheta + * - the number of bins for theta resolution * @param nRho * - the number of bins for rho resolution + */ + public static < T extends Comparable< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nTheta, final int nRho ) + { + voteLines( input, votespace, nTheta, nRho, Util.getTypeFromInterval( input ) ); + } + + /** + * Runs a Hough Line Tranform on an image and populates the vote space + * parameter with the results. + * + * @param input + * - the {@link RandomAccessibleInterval} to run the Hough Line + * Transform over + * @param votespace + * - the {@link RandomAccessibleInterval} in which the results + * are stored * @param nTheta * - the number of bins for theta resolution + * @param nRho + * - the number of bins for rho resolution * @param threshold * - the minimum value allowed by the populator. Any input less - * than or equal to this value will be disregarded by the - * populator. + * than this value will be disregarded by the populator. */ - public static < T extends Comparable< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nRho, final int nTheta, final T threshold ) + public static < T extends Comparable< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nTheta, final int nRho, final T threshold ) { Predicate< T > p = o -> threshold.compareTo( o ) < 0; - voteLines( input, votespace, nRho, nTheta, p ); + voteLines( input, votespace, nTheta, nRho, p ); } /** @@ -283,17 +300,17 @@ public static < T extends Comparable< T >, U extends IntegerType< U > > void vot * @param votespace * - the {@link RandomAccessibleInterval} in which the results * are stored - * @param nRho - * - the number of bins for rho resolution * @param nTheta * - the number of bins for theta resolution + * @param nRho + * - the number of bins for rho resolution * @param filter * - a {@link Predicate} judging whether or not the a value is * above the minimum value allowed by the populator. Any input * less than or equal to this value will be disregarded by the * populator. */ - public static < T, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nRho, final int nTheta, final Predicate< T > filter ) + public static < T, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nTheta, final int nRho, final Predicate< T > filter ) { final long[] dims = new long[ input.numDimensions() ]; From 31e63e1668d92c82aff257a6c260d86ad629185a Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Tue, 15 May 2018 13:41:50 -0500 Subject: [PATCH 29/42] VoteLines: Move check outside for-loop --- .../java/net/imglib2/algorithm/hough/HoughTransforms.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index ac9962188..0a01a2d6d 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -347,9 +347,9 @@ public static < T, U extends IntegerType< U > > void voteLines( final RandomAcce imageCursor.fwd(); - for ( int t = 0; t < nTheta; ++t ) + if ( filter.test( imageCursor.get() ) ) { - if ( filter.test( imageCursor.get() ) ) + for ( int t = 0; t < nTheta; ++t ) { fRho = Math.cos( theta[ t ] ) * imageCursor.getDoublePosition( 0 ) + Math.sin( theta[ t ] ) * imageCursor.getDoublePosition( 1 ); r = Math.round( ( float ) ( ( fRho - minRho ) / dRho ) ); From 66dfa485ba6619dbd2af62875db644c73435dab9 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Tue, 15 May 2018 13:42:52 -0500 Subject: [PATCH 30/42] HoughTransforms: Remove unused import --- src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 0a01a2d6d..90d1f66b5 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -44,7 +44,6 @@ import net.imglib2.RandomAccessibleInterval; import net.imglib2.algorithm.localextrema.LocalExtrema; import net.imglib2.algorithm.localextrema.LocalExtrema.MaximumCheck; -import net.imglib2.type.NativeType; import net.imglib2.type.numeric.IntegerType; import net.imglib2.type.numeric.RealType; import net.imglib2.util.Intervals; From 3908da74f0ed0c387179c095ff727342014e1ac9 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Tue, 15 May 2018 13:45:26 -0500 Subject: [PATCH 31/42] HoughLineTest: Refactor to better input image --- .../hough/HoughLineTransformTest.java | 34 ++++++++++++------ .../net/imglib2/algorithm/hough/result9.png | Bin 0 -> 1468 bytes 2 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 src/test/resources/net/imglib2/algorithm/hough/result9.png diff --git a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java index 57577c87c..9282daea1 100644 --- a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java +++ b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java @@ -74,17 +74,31 @@ public class HoughLineTransformTest @Before public void setUp() throws IOException { - groundTruth = ImageIO.read( getClass().getResource( "result8.png" ) ); - tpImg = ArrayImgs.unsignedBytes( 50l, 50l ); + groundTruth = ImageIO.read( getClass().getResource( "result9.png" ) ); + tpImg = ArrayImgs.unsignedBytes( 11l, 13l ); final RandomAccess< UnsignedByteType > randomAccess = tpImg.randomAccess(); - for ( int i = 24; i < 25; ++i ) + // create vertical line + randomAccess.setPosition( 5, 0 ); + for ( int i = 0; i < 13; i++ ) { - for ( int j = 14; j < 35; ++j ) - { - randomAccess.setPosition( i, 0 ); - randomAccess.setPosition( j, 1 ); - randomAccess.get().set( 255 ); - } + randomAccess.setPosition( i, 1 ); + randomAccess.get().set( 255 ); + } + + // create horizontal line + randomAccess.setPosition( 5, 1 ); + for ( int i = 0; i < 11; i++ ) + { + randomAccess.setPosition( i, 0 ); + randomAccess.get().set( 255 ); + } + + // create diagonal line + for ( int i = 0; i < 11; i++ ) + { + randomAccess.setPosition( i, 0 ); + randomAccess.setPosition( i / 3, 1 ); + randomAccess.get().set( 255 ); } } @@ -130,7 +144,7 @@ public < T extends RealType< T > > void testHoughLineTransformToTarget() throws public < T extends RealType< T > > void testPickPeaks() { // three expected peaks - final long[] expected = { 24, 89, 24, 90, 25, 91 }; + final long[] expected = { -5, -89, -5, -88, 0, -76, 0, -74, 0, -73, 5, -2, 5, -1, 5, 0, 5, 1, 5, 2, 5, 88 }; int index = 0; // create votespace diff --git a/src/test/resources/net/imglib2/algorithm/hough/result9.png b/src/test/resources/net/imglib2/algorithm/hough/result9.png new file mode 100644 index 0000000000000000000000000000000000000000..ddff4df5842a0ab2088bbc5787b554938646a54f GIT binary patch literal 1468 zcmeAS@N?(olHy`uVBq!ia0vp^NzrBUxP5c^Wq!%rAWBTpq6JM;(xU?%-MbfKeF1P2|Ajy=avz)hai0<0>x}?v#B)j&e z{>uxyGu{}A8>sy5jcDj;4LmHM@z^3;#ze1uLWoe}uGh1Ft8~Q%nF}4%P}%h^yYq~M z>M@tQSMA*buar)?d;IX0jrpuBFH^QAN Date: Tue, 15 May 2018 13:46:29 -0500 Subject: [PATCH 32/42] getVotespaceSize: Allow for non-default thetas --- .../imglib2/algorithm/hough/HoughTransforms.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 90d1f66b5..a01078478 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -114,6 +114,22 @@ public static long[] getVotespaceSize( final Dimensions dimensions ) return new long[] { defaultRho( dimensions ), DEFAULT_THETA }; } + /** + * Returns the size of the vote space output image given an input + * {@link RandomAccessibleInterval}. + * + * @param dimensions + * - the {@link Dimensions} over which the Hough Line Transform + * will be run + * @param nTheta + * - the number of theta bins. + * @return {@code long[]} - the dimensions of the vote space image + */ + public static long[] getVotespaceSize( final Dimensions dimensions, final int nTheta ) + { + return new long[] { defaultRho( dimensions ), nTheta }; + } + /** * Returns the size of the voteSpace output image given desired {@code nRho} * and {@code nTheta} values. From 11995a69623649ecd2d29dc0c66bb0ba57efe331 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Tue, 15 May 2018 15:37:00 -0500 Subject: [PATCH 33/42] HoughTransforms: Formatting --- .../algorithm/hough/HoughTransforms.java | 90 ++++++++++++------- 1 file changed, 57 insertions(+), 33 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index a01078478..cc0545ad5 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -68,7 +68,7 @@ public class HoughTransforms< T extends RealType< T > & Comparable< T > > /** * Calculates the geometric distance between [0, ... , 0] and position. - * + * * @param position * - the position of the second point * @return {@code double} - the distance between the two points. @@ -103,7 +103,7 @@ private static int defaultRho( final Dimensions dimensions ) /** * Returns the size of the vote space output image given an input * {@link RandomAccessibleInterval}. - * + * * @param dimensions * - the {@link Dimensions} over which the Hough Line Transform * will be run @@ -117,7 +117,7 @@ public static long[] getVotespaceSize( final Dimensions dimensions ) /** * Returns the size of the vote space output image given an input * {@link RandomAccessibleInterval}. - * + * * @param dimensions * - the {@link Dimensions} over which the Hough Line Transform * will be run @@ -133,21 +133,21 @@ public static long[] getVotespaceSize( final Dimensions dimensions, final int nT /** * Returns the size of the voteSpace output image given desired {@code nRho} * and {@code nTheta} values. - * + * * @param nRho * - the number of bins for rho resolution * @param nTheta * - the number of bins for theta resolution * @return {@code long[]} - the dimensions of the vote space image. */ - public static long[] getVotespaceSize( int nRho, int nTheta ) + public static long[] getVotespaceSize( final int nRho, final int nTheta ) { return new long[] { nRho, nTheta }; } /** * Pick vote space peaks with a {@link LocalExtrema}. - * + * * @param voteSpace * - the {@link RandomAccessibleInterval} containing the output * of a Hough Transform vote @@ -157,7 +157,9 @@ public static long[] getVotespaceSize( int nRho, int nTheta ) * @return {@code List} - a list of all of the local maxima of the * {@code voteSpace} */ - public static < T extends IntegerType< T > > List< Point > pickLinePeaks( final RandomAccessibleInterval< T > voteSpace, final long threshold ) + public static < T extends IntegerType< T > > List< Point > pickLinePeaks( + final RandomAccessibleInterval< T > voteSpace, + final long threshold ) { final T minPeak = Util.getTypeFromInterval( voteSpace ).createVariable(); minPeak.setInteger( threshold ); @@ -176,7 +178,9 @@ public static < T extends IntegerType< T > > List< Point > pickLinePeaks( final * @return {@code List} - a list of all of the local maxima of the * {@code voteSpace}. */ - public static < T extends Comparable< T > > List< Point > pickLinePeaks( RandomAccessibleInterval< T > voteSpace, T minPeak ) + public static < T extends Comparable< T > > List< Point > pickLinePeaks( + final RandomAccessibleInterval< T > voteSpace, + final T minPeak ) { final MaximumCheck< T > maxCheck = new MaximumCheck<>( minPeak ); @@ -186,8 +190,8 @@ public static < T extends Comparable< T > > List< Point > pickLinePeaks( RandomA // the first dimension and by {@code -pi / 2} in the second dimension so // that we return accurate coordinates from the // vote space. - long[] translation = { -( voteSpace.dimension( 0 ) / 2 ), -( voteSpace.dimension( 1 ) / 2 ) }; - IntervalView< T > translatedVotes = Views.translate( voteSpace, translation ); + final long[] translation = { -( voteSpace.dimension( 0 ) / 2 ), -( voteSpace.dimension( 1 ) / 2 ) }; + final IntervalView< T > translatedVotes = Views.translate( voteSpace, translation ); return LocalExtrema.findLocalExtrema( translatedVotes, maxCheck ); } @@ -195,7 +199,7 @@ public static < T extends Comparable< T > > List< Point > pickLinePeaks( RandomA /** * Runs a Hough Line Tranform on an image and populates the vote space * parameter with the results. - * + * * @param input * - the {@link RandomAccessibleInterval} to run the Hough Line * Transform over @@ -203,7 +207,9 @@ public static < T extends Comparable< T > > List< Point > pickLinePeaks( RandomA * - the {@link RandomAccessibleInterval} in which the results * are stored */ - public static < T extends Comparable< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace ) + public static < T extends Comparable< T >, U extends IntegerType< U > > void voteLines( + final RandomAccessibleInterval< T > input, + final RandomAccessibleInterval< U > votespace ) { voteLines( input, votespace, DEFAULT_THETA, defaultRho( input ) ); } @@ -211,7 +217,7 @@ public static < T extends Comparable< T >, U extends IntegerType< U > > void vot /** * Runs a Hough Line Tranform on an image and populates the vote space * parameter with the results. - * + * * @param input * - the {@link RandomAccessibleInterval} to run the Hough Line * Transform over @@ -221,7 +227,10 @@ public static < T extends Comparable< T >, U extends IntegerType< U > > void vot * @param nTheta * - the number of bins for theta resolution */ - public static < T extends Comparable< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nTheta ) + public static < T extends Comparable< T >, U extends IntegerType< U > > void voteLines( + final RandomAccessibleInterval< T > input, + final RandomAccessibleInterval< U > votespace, + final int nTheta ) { voteLines( input, votespace, nTheta, defaultRho( input ), Util.getTypeFromInterval( input ) ); } @@ -229,7 +238,7 @@ public static < T extends Comparable< T >, U extends IntegerType< U > > void vot /** * Runs a Hough Line Tranform on an image and populates the vote space * parameter with the results. - * + * * @param input * - the {@link RandomAccessibleInterval} to run the Hough Line * Transform over @@ -241,7 +250,11 @@ public static < T extends Comparable< T >, U extends IntegerType< U > > void vot * @param nRho * - the number of bins for rho resolution */ - public static < T extends Comparable< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nTheta, final int nRho ) + public static < T extends Comparable< T >, U extends IntegerType< U > > void voteLines( + final RandomAccessibleInterval< T > input, + final RandomAccessibleInterval< U > votespace, + final int nTheta, + final int nRho ) { voteLines( input, votespace, nTheta, nRho, Util.getTypeFromInterval( input ) ); } @@ -249,7 +262,7 @@ public static < T extends Comparable< T >, U extends IntegerType< U > > void vot /** * Runs a Hough Line Tranform on an image and populates the vote space * parameter with the results. - * + * * @param input * - the {@link RandomAccessibleInterval} to run the Hough Line * Transform over @@ -264,14 +277,20 @@ public static < T extends Comparable< T >, U extends IntegerType< U > > void vot * - the minimum value allowed by the populator. Any input less * than this value will be disregarded by the populator. */ - public static < T extends Comparable< T >, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nTheta, final int nRho, final T threshold ) + public static < T extends Comparable< T >, U extends IntegerType< U > > void voteLines( + final RandomAccessibleInterval< T > input, + final RandomAccessibleInterval< U > votespace, + final int nTheta, + final int nRho, + final T threshold ) + { - Predicate< T > p = o -> threshold.compareTo( o ) < 0; + final Predicate< T > p = o -> threshold.compareTo( o ) < 0; voteLines( input, votespace, nTheta, nRho, p ); } /** - * + * * Runs a Hough Line Tranform on an image and populates the vote space * parameter with the results. *

@@ -282,7 +301,7 @@ public static < T extends Comparable< T >, U extends IntegerType< U > > void vot *

* Lines are modeled as *

- * + * *
 	 * l(t) = | x | = rho * |  cos(theta) | + t * | sin(theta) |
 	 *        | y |         | -sin(theta) |       | cos(theta) |
@@ -295,7 +314,7 @@ public static < T extends Comparable< T >, U extends IntegerType< U > > void vot
 	 * 

* For a given point, then, votes are placed along the curve *

- * + * *
 	 * rho = y * sin( theta ) + x * cos( theta )
 	 * 
@@ -308,7 +327,7 @@ public static < T extends Comparable< T >, U extends IntegerType< U > > void vot * be translated by {@code -maxRho} in dimension 0 to get the correct * {@code rho} and {@code theta} values from the vote space. *

- * + * * @param input * - the {@link RandomAccessibleInterval} to run the Hough Line * Transform over @@ -325,14 +344,19 @@ public static < T extends Comparable< T >, U extends IntegerType< U > > void vot * less than or equal to this value will be disregarded by the * populator. */ - public static < T, U extends IntegerType< U > > void voteLines( final RandomAccessibleInterval< T > input, final RandomAccessibleInterval< U > votespace, final int nTheta, final int nRho, final Predicate< T > filter ) + public static < T, U extends IntegerType< U > > void voteLines( + final RandomAccessibleInterval< T > input, + final RandomAccessibleInterval< U > votespace, + final int nTheta, + final int nRho, + final Predicate< T > filter ) { final long[] dims = new long[ input.numDimensions() ]; input.dimensions( dims ); final double minRho = -computeLength( dims ); - final double dRho = 2 * computeLength( dims ) / ( double ) nRho; + final double dRho = 2 * computeLength( dims ) / nRho; final double minTheta = -Math.PI / 2; final double dTheta = Math.PI / nTheta; @@ -383,7 +407,7 @@ public static < T, U extends IntegerType< U > > void voteLines( final RandomAcce * {@link HoughTransforms#voteLines} algorithm into a more useful * y-intercept value. Used with {@link HoughTransforms#getSlope} to create * line equations. - * + * * @param rho * - the {@code rho} of the line * @param theta @@ -392,8 +416,8 @@ public static < T, U extends IntegerType< U > > void voteLines( final RandomAcce */ public static double getIntercept( final long rho, final long theta ) { - double radians = ( Math.PI * theta ) / 180; - return ( rho / Math.sin( radians ) ); + final double radians = Math.PI * theta / 180; + return rho / Math.sin( radians ); } /** @@ -401,22 +425,22 @@ public static double getIntercept( final long rho, final long theta ) * {@link HoughTransforms#voteLines} algorithm into a more useful slope * value. Used with {@link HoughTransforms#getIntercept} to create line * equations. - * + * * @param theta * - the {@code theta} of the line * @return {@code double} - the y-intercept of the line */ public static double getSlope( final long theta ) { - double radians = ( Math.PI * theta ) / 180; + final double radians = Math.PI * theta / 180; - double n = -Math.cos( radians ); - double d = Math.sin( radians ); + final double n = -Math.cos( radians ); + final double d = Math.sin( radians ); // to avoid a divide by zero error return an infinite slope if the // denominator is zero. if ( Math.abs( n ) == 1 ) return n > 0 ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY; - return ( n / d ); + return n / d; } } From 66998bc632556878f9b181abc2de440b2d99ec60 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Wed, 16 May 2018 09:08:25 -0500 Subject: [PATCH 34/42] VoteLines: Translate image for zeroed min --- src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index cc0545ad5..87e50d25e 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -376,7 +376,7 @@ public static < T, U extends IntegerType< U > > void voteLines( rho[ r ] = dRho * r + minRho; } - final Cursor< T > imageCursor = Views.iterable( input ).localizingCursor(); + final Cursor< T > imageCursor = Views.iterable( Views.zeroMin( input ) ).localizingCursor(); final RandomAccess< U > outputRA = votespace.randomAccess(); while ( imageCursor.hasNext() ) From 3ee8755235d6d671e20a03279c0bc975d408c5e7 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Wed, 16 May 2018 09:19:25 -0500 Subject: [PATCH 35/42] VoteLines: Refactor LUTs this commit removes the unused rho[] LUT and refactors the theta[] LUT into two different LUTs for sin(theta) and cos(theta). This refactor speeds up later calculations --- .../imglib2/algorithm/hough/HoughTransforms.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 87e50d25e..7f379ac32 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -361,19 +361,19 @@ public static < T, U extends IntegerType< U > > void voteLines( final double minTheta = -Math.PI / 2; final double dTheta = Math.PI / nTheta; - final double[] rho = new double[ nRho ]; - final double[] theta = new double[ nTheta ]; + final double[] cTheta = new double[ nTheta ]; + final double[] sTheta = new double[ nTheta ]; - // create theta LUT + // create cos(theta) LUT for ( int t = 0; t < nTheta; ++t ) { - theta[ t ] = dTheta * t + minTheta; + cTheta[ t ] = Math.cos( dTheta * t + minTheta ); } - // create rho LUT - for ( int r = 0; r < nRho; ++r ) + // create sin`(theta) LUT + for ( int t = 0; t < nTheta; ++t ) { - rho[ r ] = dRho * r + minRho; + sTheta[ t ] = Math.sin( dTheta * t + minTheta ); } final Cursor< T > imageCursor = Views.iterable( Views.zeroMin( input ) ).localizingCursor(); @@ -390,7 +390,7 @@ public static < T, U extends IntegerType< U > > void voteLines( { for ( int t = 0; t < nTheta; ++t ) { - fRho = Math.cos( theta[ t ] ) * imageCursor.getDoublePosition( 0 ) + Math.sin( theta[ t ] ) * imageCursor.getDoublePosition( 1 ); + fRho = cTheta[ t ] * imageCursor.getDoublePosition( 0 ) + sTheta[ t ] * imageCursor.getDoublePosition( 1 ); r = Math.round( ( float ) ( ( fRho - minRho ) / dRho ) ); // place vote From 8c79996cab4c6311a0c2857bbf9091328af63600 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Wed, 16 May 2018 09:34:01 -0500 Subject: [PATCH 36/42] VoteLines: fix predicate bug This commit changes the Predicate deciding whether to allow votespace population from being a strictly greater-than to a greater-than-or-equal-to comparison. This fix was made to prevent a bug in which no threshold is provided to the algorithm and at the same time the max value of the image is at the first index of the image. If the comparison is not <= then all pixels will fail the Predicate, since no pixel is greater than the max. This means that passing a value to the algorithm is still preferable but allows more scenarios where no value is given. --- src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 7f379ac32..457e477b2 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -285,7 +285,7 @@ public static < T extends Comparable< T >, U extends IntegerType< U > > void vot final T threshold ) { - final Predicate< T > p = o -> threshold.compareTo( o ) < 0; + final Predicate< T > p = o -> threshold.compareTo( o ) <= 0; voteLines( input, votespace, nTheta, nRho, p ); } From 32f685c7cb6ec4553a6532f0ec41720c0610c417 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Wed, 16 May 2018 09:45:01 -0500 Subject: [PATCH 37/42] VoteLines: Refactor fRho/r declaration --- .../java/net/imglib2/algorithm/hough/HoughTransforms.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 457e477b2..09f99ff9a 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -381,17 +381,14 @@ public static < T, U extends IntegerType< U > > void voteLines( while ( imageCursor.hasNext() ) { - double fRho; - int r; - imageCursor.fwd(); if ( filter.test( imageCursor.get() ) ) { for ( int t = 0; t < nTheta; ++t ) { - fRho = cTheta[ t ] * imageCursor.getDoublePosition( 0 ) + sTheta[ t ] * imageCursor.getDoublePosition( 1 ); - r = Math.round( ( float ) ( ( fRho - minRho ) / dRho ) ); + final double fRho = cTheta[ t ] * imageCursor.getDoublePosition( 0 ) + sTheta[ t ] * imageCursor.getDoublePosition( 1 ); + final long r = Math.round( ( float ) ( ( fRho - minRho ) / dRho ) ); // place vote outputRA.setPosition( r, 0 ); From 29df455f8524d8cd450a3cd50663f403ba6cd69f Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Wed, 16 May 2018 09:47:19 -0500 Subject: [PATCH 38/42] VoteLines: Remove cursor positions from for loop --- .../java/net/imglib2/algorithm/hough/HoughTransforms.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 09f99ff9a..d077c2827 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -385,9 +385,12 @@ public static < T, U extends IntegerType< U > > void voteLines( if ( filter.test( imageCursor.get() ) ) { + final double x = imageCursor.getDoublePosition( 0 ); + final double y = imageCursor.getDoublePosition( 1 ); + for ( int t = 0; t < nTheta; ++t ) { - final double fRho = cTheta[ t ] * imageCursor.getDoublePosition( 0 ) + sTheta[ t ] * imageCursor.getDoublePosition( 1 ); + final double fRho = cTheta[ t ] * x + sTheta[ t ] * y; final long r = Math.round( ( float ) ( ( fRho - minRho ) / dRho ) ); // place vote From abc682986904afdec245e7c8197f97c1b6197cf9 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Wed, 16 May 2018 10:14:58 -0500 Subject: [PATCH 39/42] VoteLines: remove unnecessary float cast. --- src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index d077c2827..1aa822fbc 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -391,7 +391,7 @@ public static < T, U extends IntegerType< U > > void voteLines( for ( int t = 0; t < nTheta; ++t ) { final double fRho = cTheta[ t ] * x + sTheta[ t ] * y; - final long r = Math.round( ( float ) ( ( fRho - minRho ) / dRho ) ); + final long r = Math.round( ( fRho - minRho ) / dRho ); // place vote outputRA.setPosition( r, 0 ); From 8f274a67948ad43031569875bff03f688f87e995 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Wed, 16 May 2018 10:41:57 -0500 Subject: [PATCH 40/42] HoughLineTest: use ArrayImgs to make votespace --- .../hough/HoughLineTransformTest.java | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java index 9282daea1..867e15acd 100644 --- a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java +++ b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java @@ -46,12 +46,12 @@ import net.imglib2.Point; import net.imglib2.RandomAccess; import net.imglib2.img.Img; -import net.imglib2.img.array.ArrayImgFactory; import net.imglib2.img.array.ArrayImgs; import net.imglib2.type.numeric.RealType; import net.imglib2.type.numeric.integer.UnsignedByteType; import net.imglib2.util.Util; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -109,7 +109,11 @@ public < T extends RealType< T > > void testHoughLineTransformToTarget() throws final long[] dims = new long[ tpImg.numDimensions() ]; tpImg.dimensions( dims ); final long[] outputDims = HoughTransforms.getVotespaceSize( tpImg ); - final Img< UnsignedByteType > votespace = new ArrayImgFactory().create( outputDims, tpImg.firstElement() ); + int numElements = 1; + for ( final long l : outputDims ) + numElements *= l; + final byte[] store = new byte[ numElements ]; + final Img< UnsignedByteType > votespace = ArrayImgs.unsignedBytes( store, outputDims ); // run transform HoughTransforms.voteLines( tpImg, votespace ); @@ -120,24 +124,11 @@ public < T extends RealType< T > > void testHoughLineTransformToTarget() throws assertEquals( "Ground truth and result height do not match.", height, outputDims[ 1 ] ); assertEquals( "Ground truth and result width do not match.", width, outputDims[ 0 ] ); - DataBufferByte db = ( DataBufferByte ) groundTruth.getData().getDataBuffer(); - byte[] expected = db.getBankData()[ 0 ]; - - final RandomAccess< UnsignedByteType > ra = votespace.randomAccess(); + final DataBufferByte db = ( DataBufferByte ) groundTruth.getData().getDataBuffer(); + final byte[] expected = db.getBankData()[ 0 ]; // compare expected / actual results for regression. - int index = 0; - for ( int j = 0; j < height; ++j ) - { - ra.setPosition( j, 1 ); - for ( int i = 0; i < width; ++i ) - { - ra.setPosition( i, 0 ); - - assertEquals( expected[ index++ ], ra.get().getRealDouble(), 0 ); - - } - } + Assert.assertArrayEquals( expected, store ); } @Test @@ -151,7 +142,11 @@ public < T extends RealType< T > > void testPickPeaks() final long[] dims = new long[ tpImg.numDimensions() ]; tpImg.dimensions( dims ); final long[] outputDims = HoughTransforms.getVotespaceSize( tpImg ); - final Img< UnsignedByteType > votespace = new ArrayImgFactory().create( outputDims, tpImg.firstElement() ); + int numElements = 1; + for ( final long l : outputDims ) + numElements *= l; + final byte[] store = new byte[ numElements ]; + final Img< UnsignedByteType > votespace = ArrayImgs.unsignedBytes( store, outputDims ); // run transform HoughTransforms.voteLines( tpImg, votespace ); From f1188a9635c2b6eb6088743e165a037993bd6716 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Wed, 16 May 2018 10:42:35 -0500 Subject: [PATCH 41/42] HoughLineTest: Formatting --- .../imglib2/algorithm/hough/HoughLineTransformTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java index 867e15acd..33bef363c 100644 --- a/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java +++ b/src/test/java/net/imglib2/algorithm/hough/HoughLineTransformTest.java @@ -119,8 +119,8 @@ public < T extends RealType< T > > void testHoughLineTransformToTarget() throws HoughTransforms.voteLines( tpImg, votespace ); // compare expected / actual dimensions - int height = groundTruth.getHeight(); - int width = groundTruth.getWidth(); + final int height = groundTruth.getHeight(); + final int width = groundTruth.getWidth(); assertEquals( "Ground truth and result height do not match.", height, outputDims[ 1 ] ); assertEquals( "Ground truth and result width do not match.", width, outputDims[ 0 ] ); @@ -154,8 +154,8 @@ public < T extends RealType< T > > void testPickPeaks() final UnsignedByteType minPeak = Util.getTypeFromInterval( votespace ).createVariable(); minPeak.setReal( 10 ); - List< Point > peaks = HoughTransforms.pickLinePeaks( votespace, minPeak ); - for ( Point p : peaks ) + final List< Point > peaks = HoughTransforms.pickLinePeaks( votespace, minPeak ); + for ( final Point p : peaks ) { // assert dimension 0 assertEquals( expected[ index++ ], p.getLongPosition( 0 ), 0 ); From 2e8f6b826b4fa2e5ae3f65ce474e6c158dc1c5df Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Wed, 16 May 2018 15:54:37 -0500 Subject: [PATCH 42/42] VoteLines: Fix javadoc --- .../net/imglib2/algorithm/hough/HoughTransforms.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java index 1aa822fbc..8e10c914a 100644 --- a/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java +++ b/src/main/java/net/imglib2/algorithm/hough/HoughTransforms.java @@ -52,8 +52,9 @@ import net.imglib2.view.Views; /** - * This abstract class provides some basic functionality for use with arbitrary - * Hough-like transforms. + * + * This class provides static methods used to detect imperfect shapes in + * two-dimensional images through the use of the Hough Transform. * * @param * the data type of the input image. @@ -322,9 +323,9 @@ public static < T extends Comparable< T >, U extends IntegerType< U > > void vot * It is important to note that the interval of the first dimension of the * vote space image is NOT {@code [-maxRho, maxRho)} but instead * {@code [0, maxRho * 2)}; the same applies to the second dimension of the - * vote space as well. Thus if {@link HoughTransforms#pickPeaks} is not used - * to retrieve the maxima from the vote space, the vote space will have to - * be translated by {@code -maxRho} in dimension 0 to get the correct + * vote space as well. Thus if {@link HoughTransforms#pickLinePeaks} is not + * used to retrieve the maxima from the vote space, the vote space will have + * to be translated by {@code -maxRho} in dimension 0 to get the correct * {@code rho} and {@code theta} values from the vote space. *

*