Skip to content

Commit 934f45d

Browse files
tinevezctrueden
authored andcommitted
Add Hough circle transform and detector ops
The transform is: - 2D version only. - weigthed or not. The detectors retrieve the Hough circles from a vote image generated by a HoughCircleTransformOp. The circles are returned as a collection of HoughCircles. Two versions: - one with a DoG detector and sub-pixel accuracy. - one with plain local extrema. Signed-off-by: Curtis Rueden <[email protected]>
1 parent b0c0878 commit 934f45d

File tree

11 files changed

+1221
-13
lines changed

11 files changed

+1221
-13
lines changed

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,11 @@
273273
<artifactId>scifio</artifactId>
274274
<scope>test</scope>
275275
</dependency>
276+
<dependency>
277+
<groupId>net.imagej</groupId>
278+
<artifactId>imagej-ui-swing</artifactId>
279+
<scope>test</scope>
280+
</dependency>
276281
<dependency>
277282
<!-- Do not remove - Maven incorrectly identifies it as an unused dependency! -->
278283
<groupId>org.scijava</groupId>

src/main/java/net/imagej/ops/segment/SegmentNamespace.java

Lines changed: 155 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
* %%
77
* Redistribution and use in source and binary forms, with or without
88
* modification, are permitted provided that the following conditions are met:
9-
*
9+
*
1010
* 1. Redistributions of source code must retain the above copyright notice,
1111
* this list of conditions and the following disclaimer.
1212
* 2. Redistributions in binary form must reproduce the above copyright notice,
1313
* this list of conditions and the following disclaimer in the documentation
1414
* and/or other materials provided with the distribution.
15-
*
15+
*
1616
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1717
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1818
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -35,16 +35,23 @@
3535
import net.imagej.ops.Namespace;
3636
import net.imagej.ops.OpMethod;
3737
import net.imagej.ops.Ops;
38+
import net.imagej.ops.segment.hough.HoughCircle;
39+
import net.imglib2.IterableInterval;
40+
import net.imglib2.RandomAccessible;
3841
import net.imglib2.RandomAccessibleInterval;
3942
import net.imglib2.RealPoint;
43+
import net.imglib2.img.Img;
4044
import net.imglib2.roi.geom.real.WritablePolyline;
45+
import net.imglib2.type.BooleanType;
46+
import net.imglib2.type.NativeType;
4147
import net.imglib2.type.numeric.RealType;
48+
import net.imglib2.type.numeric.real.DoubleType;
4249

4350
import org.scijava.plugin.Plugin;
4451

4552
/**
4653
* The segment namespace contains segmentation operations.
47-
*
54+
*
4855
* @author Gabe Selzer
4956
*/
5057
@Plugin(type = Namespace.class)
@@ -61,28 +68,32 @@ public <T extends RealType<T>> List<? extends WritablePolyline> detectRidges(
6168
final int ridgeLengthMin)
6269
{
6370
@SuppressWarnings("unchecked")
64-
final List<? extends WritablePolyline> result = (List<? extends WritablePolyline>) ops().run(
65-
Ops.Segment.DetectRidges.class, input, width, lowerThreshold,
66-
higherThreshold, ridgeLengthMin);
71+
final List<? extends WritablePolyline> result =
72+
(List<? extends WritablePolyline>) ops().run(
73+
Ops.Segment.DetectRidges.class, input, width, lowerThreshold,
74+
higherThreshold, ridgeLengthMin);
6775

6876
return result;
6977
}
7078

7179
// -- detectJunctions --
72-
73-
@OpMethod(op = net.imagej.ops.segment.detectJunctions.DefaultDetectJunctions.class)
74-
public List<RealPoint> detectJunctions(final List<? extends WritablePolyline> lines)
80+
81+
@OpMethod(
82+
op = net.imagej.ops.segment.detectJunctions.DefaultDetectJunctions.class)
83+
public List<RealPoint> detectJunctions(
84+
final List<? extends WritablePolyline> lines)
7585
{
7686
@SuppressWarnings("unchecked")
7787
final List<RealPoint> result = (List<RealPoint>) ops().run(
7888
Ops.Segment.DetectJunctions.class, lines);
7989

8090
return result;
8191
}
82-
83-
@OpMethod(op = net.imagej.ops.segment.detectJunctions.DefaultDetectJunctions.class)
84-
public List<RealPoint> detectJunctions(final List<? extends WritablePolyline> lines,
85-
final double threshold)
92+
93+
@OpMethod(
94+
op = net.imagej.ops.segment.detectJunctions.DefaultDetectJunctions.class)
95+
public List<RealPoint> detectJunctions(
96+
final List<? extends WritablePolyline> lines, final double threshold)
8697
{
8798
@SuppressWarnings("unchecked")
8899
final List<RealPoint> result = (List<RealPoint>) ops().run(
@@ -91,6 +102,137 @@ public List<RealPoint> detectJunctions(final List<? extends WritablePolyline> li
91102
return result;
92103
}
93104

105+
// -- detectHoughCircleDoG --
106+
107+
@OpMethod(op = net.imagej.ops.segment.hough.HoughCircleDetectorDogOp.class)
108+
public <T extends RealType<T> & NativeType<T>> List<HoughCircle>
109+
detectHoughCircleDoG(final RandomAccessibleInterval<T> in,
110+
final double circleThickness, final double minRadius,
111+
final double stepRadius, final double sigma)
112+
{
113+
@SuppressWarnings("unchecked")
114+
final List<HoughCircle> result = (List<HoughCircle>) ops().run(
115+
net.imagej.ops.Ops.Segment.DetectHoughCircleDoG.class, in,
116+
circleThickness, minRadius, stepRadius, sigma);
117+
return result;
118+
}
119+
120+
@OpMethod(op = net.imagej.ops.segment.hough.HoughCircleDetectorDogOp.class)
121+
public <T extends RealType<T> & NativeType<T>> List<HoughCircle>
122+
detectHoughCircleDoG(final RandomAccessibleInterval<T> in,
123+
final double circleThickness, final double minRadius,
124+
final double stepRadius, final double sigma, final double sensitivity)
125+
{
126+
@SuppressWarnings("unchecked")
127+
final List<HoughCircle> result = (List<HoughCircle>) ops().run(
128+
net.imagej.ops.Ops.Segment.DetectHoughCircleDoG.class, in,
129+
circleThickness, minRadius, stepRadius, sigma, sensitivity);
130+
return result;
131+
}
132+
133+
// -- detectHoughCircleLE --
134+
135+
@OpMethod(
136+
op = net.imagej.ops.segment.hough.HoughCircleDetectorLocalExtremaOp.class)
137+
public <T extends RealType<T> & NativeType<T>> List<HoughCircle>
138+
detectHoughCircleLE(final RandomAccessibleInterval<T> in,
139+
final double minRadius, final double stepRadius)
140+
{
141+
@SuppressWarnings("unchecked")
142+
final List<HoughCircle> result = (List<HoughCircle>) ops().run(
143+
net.imagej.ops.Ops.Segment.DetectHoughCircleLE.class, in, minRadius,
144+
stepRadius);
145+
return result;
146+
}
147+
148+
@OpMethod(
149+
op = net.imagej.ops.segment.hough.HoughCircleDetectorLocalExtremaOp.class)
150+
public <T extends RealType<T> & NativeType<T>> List<HoughCircle>
151+
detectHoughCircleLE(final RandomAccessibleInterval<T> in,
152+
final double minRadius, final double stepRadius, final double sensitivity)
153+
{
154+
@SuppressWarnings("unchecked")
155+
final List<HoughCircle> result = (List<HoughCircle>) ops().run(
156+
net.imagej.ops.Ops.Segment.DetectHoughCircleLE.class, in, minRadius,
157+
stepRadius, sensitivity);
158+
return result;
159+
}
160+
161+
// -- transformHoughCircle --
162+
163+
@OpMethod(op = net.imagej.ops.segment.hough.HoughTransformOpNoWeights.class)
164+
public <T extends BooleanType<T>> Img<DoubleType> transformHoughCircle(
165+
final IterableInterval<T> in, final long minRadius, final long maxRadius)
166+
{
167+
@SuppressWarnings("unchecked")
168+
final Img<DoubleType> result = (Img<DoubleType>) ops().run(
169+
net.imagej.ops.Ops.Segment.TransformHoughCircle.class, in, minRadius,
170+
maxRadius);
171+
return result;
172+
}
173+
174+
@OpMethod(op = net.imagej.ops.segment.hough.HoughTransformOpNoWeights.class)
175+
public <T extends BooleanType<T>> Img<DoubleType> transformHoughCircle(
176+
final Img<DoubleType> out, final IterableInterval<T> in,
177+
final long minRadius, final long maxRadius)
178+
{
179+
@SuppressWarnings("unchecked")
180+
final Img<DoubleType> result = (Img<DoubleType>) ops().run(
181+
net.imagej.ops.Ops.Segment.TransformHoughCircle.class, out, in, minRadius,
182+
maxRadius);
183+
return result;
184+
}
185+
186+
@OpMethod(op = net.imagej.ops.segment.hough.HoughTransformOpNoWeights.class)
187+
public <T extends BooleanType<T>> Img<DoubleType> transformHoughCircle(
188+
final Img<DoubleType> out, final IterableInterval<T> in,
189+
final long minRadius, final long maxRadius, final long stepRadius)
190+
{
191+
@SuppressWarnings("unchecked")
192+
final Img<DoubleType> result = (Img<DoubleType>) ops().run(
193+
net.imagej.ops.Ops.Segment.TransformHoughCircle.class, out, in, minRadius,
194+
maxRadius, stepRadius);
195+
return result;
196+
}
197+
198+
@OpMethod(op = net.imagej.ops.segment.hough.HoughTransformOpWeights.class)
199+
public <T extends BooleanType<T>, R extends RealType<R>> Img<DoubleType>
200+
transformHoughCircle(final IterableInterval<T> in, final long minRadius,
201+
final long maxRadius, final RandomAccessible<R> weights)
202+
{
203+
@SuppressWarnings("unchecked")
204+
final Img<DoubleType> result = (Img<DoubleType>) ops().run(
205+
net.imagej.ops.Ops.Segment.TransformHoughCircle.class, in, minRadius,
206+
maxRadius, weights);
207+
return result;
208+
}
209+
210+
@OpMethod(op = net.imagej.ops.segment.hough.HoughTransformOpWeights.class)
211+
public <T extends BooleanType<T>, R extends RealType<R>> Img<DoubleType>
212+
transformHoughCircle(final Img<DoubleType> out,
213+
final IterableInterval<T> in, final long minRadius, final long maxRadius,
214+
final RandomAccessible<R> weights)
215+
{
216+
@SuppressWarnings("unchecked")
217+
final Img<DoubleType> result = (Img<DoubleType>) ops().run(
218+
net.imagej.ops.Ops.Segment.TransformHoughCircle.class, out, in, minRadius,
219+
maxRadius, weights);
220+
return result;
221+
}
222+
223+
@OpMethod(op = net.imagej.ops.segment.hough.HoughTransformOpWeights.class)
224+
public <T extends BooleanType<T>, R extends RealType<R>> Img<DoubleType>
225+
transformHoughCircle(final Img<DoubleType> out,
226+
final IterableInterval<T> in, final long minRadius, final long maxRadius,
227+
final long stepRadius, final RandomAccessible<R> weights)
228+
{
229+
@SuppressWarnings("unchecked")
230+
final Img<DoubleType> result = (Img<DoubleType>) ops().run(
231+
net.imagej.ops.Ops.Segment.TransformHoughCircle.class, out, in, minRadius,
232+
maxRadius, stepRadius, weights);
233+
return result;
234+
}
235+
94236
// -- Namespace methods --
95237

96238
@Override
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package net.imagej.ops.segment.hough;
2+
3+
import net.imglib2.RealLocalizable;
4+
import net.imglib2.roi.geom.real.AbstractWritableSphere;
5+
6+
/**
7+
* Representation of a circle segmented via Hough circle transformation.
8+
* <p>
9+
* Instances are {@link RealLocalizable} and have a radius (in pixel units) and
10+
* a sensitivity. The sensitivity factor is used to report a quality of the
11+
* Hough circle detection, the smallest the better the circle. The sensitivity
12+
* can be used to sort a list of circles.
13+
*
14+
* @author Jean-Yves Tinevez.
15+
* @author Gabe Selzer.
16+
*
17+
*/
18+
public class HoughCircle extends AbstractWritableSphere implements
19+
Comparable<HoughCircle>
20+
{
21+
22+
private final double sensitivity;
23+
24+
public HoughCircle( final RealLocalizable pos, final double radius, final double sensitivity )
25+
{
26+
super( getCenter(pos) , radius);
27+
this.radius = radius;
28+
this.sensitivity = sensitivity;
29+
}
30+
31+
private static double[] getCenter(RealLocalizable l) {
32+
double[] center = new double[l.numDimensions()];
33+
l.localize(center);
34+
return center;
35+
}
36+
37+
@Override
38+
public String toString()
39+
{
40+
final StringBuilder sb = new StringBuilder();
41+
char c = '(';
42+
for ( int i = 0; i < numDimensions(); i++ )
43+
{
44+
sb.append( c );
45+
sb.append( String.format( "%.1f", center[ i ] ) );
46+
c = ',';
47+
}
48+
sb.append( ")" );
49+
return String.format( "%s\tR=%.1f\tSensitivity=%.1f", sb.toString(), radius, sensitivity );
50+
}
51+
52+
public double getRadius()
53+
{
54+
return radius;
55+
}
56+
57+
public double getSensitivity()
58+
{
59+
return sensitivity;
60+
}
61+
62+
@Override
63+
public int compareTo( final HoughCircle o )
64+
{
65+
return sensitivity < o.sensitivity ? -1 : sensitivity > o.sensitivity ? +1 : 0;
66+
}
67+
68+
@Override
69+
public boolean test( final RealLocalizable point )
70+
{
71+
final double dx = center[0] - point.getDoublePosition( 0 );
72+
final double dy = center[1] - point.getDoublePosition( 1 );
73+
final double dr2 = dx * dx + dy * dy;
74+
75+
if ( dr2 < radius * radius )
76+
return false;
77+
78+
return true;
79+
}
80+
81+
}

0 commit comments

Comments
 (0)