1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 package org.openimaj.image.analysis.algorithm;
31
32 import static java.lang.Math.PI;
33 import static java.lang.Math.cos;
34 import static java.lang.Math.round;
35 import static java.lang.Math.sin;
36 import gnu.trove.map.hash.TIntFloatHashMap;
37 import gnu.trove.map.hash.TIntObjectHashMap;
38 import gnu.trove.procedure.TIntFloatProcedure;
39 import gnu.trove.procedure.TIntObjectProcedure;
40
41 import java.util.List;
42
43 import org.apache.log4j.Logger;
44 import org.openimaj.image.FImage;
45 import org.openimaj.image.analyser.ImageAnalyser;
46 import org.openimaj.math.geometry.shape.Circle;
47 import org.openimaj.util.queue.BoundedPriorityQueue;
48
49
50
51
52
53
54 public class HoughCircles implements ImageAnalyser<FImage> {
55 Logger logger = Logger.getLogger(HoughCircles.class);
56
57
58
59
60
61
62
63 public static class WeightedCircle extends Circle implements Comparable<WeightedCircle> {
64
65
66
67 public float weight;
68
69
70
71
72
73
74
75
76
77
78
79
80
81 public WeightedCircle(float x, float y, float radius, float weight) {
82 super(x, y, radius);
83 this.weight = weight;
84 }
85
86 @Override
87 public int compareTo(WeightedCircle o) {
88 return Float.compare(o.weight, this.weight);
89 }
90 }
91
92 protected int minRad;
93 protected int maxRad;
94 protected TIntObjectHashMap<TIntObjectHashMap<TIntFloatHashMap>> radmap;
95 private float[][] cosanglemap;
96 private float[][] sinanglemap;
97 private int nRadius;
98 private int nDegree;
99 private int radIncr;
100
101
102
103
104
105
106
107
108
109
110
111
112
113 public HoughCircles(int minRad, int maxRad, int radIncrement, int nDegree) {
114 super();
115 this.minRad = minRad;
116 if (this.minRad <= 0)
117 this.minRad = 1;
118 this.maxRad = maxRad;
119 this.radmap = new TIntObjectHashMap<TIntObjectHashMap<TIntFloatHashMap>>();
120 this.radIncr = radIncrement;
121 this.nRadius = (maxRad - minRad) / this.radIncr;
122 this.nDegree = nDegree;
123 this.cosanglemap = new float[nRadius][nDegree];
124 this.sinanglemap = new float[nRadius][nDegree];
125 for (int radIndex = 0; radIndex < this.nRadius; radIndex++) {
126 for (int angIndex = 0; angIndex < nDegree; angIndex++) {
127 final double ang = angIndex * (2 * PI / nDegree);
128 final double rad = minRad + (radIndex * this.radIncr);
129 this.cosanglemap[radIndex][angIndex] = (float) (rad * cos(ang));
130 this.sinanglemap[radIndex][angIndex] = (float) (rad * sin(ang));
131 }
132 }
133 }
134
135 @Override
136 public void analyseImage(FImage image) {
137 final int height = image.getHeight();
138 final int width = image.getWidth();
139 this.radmap = new TIntObjectHashMap<TIntObjectHashMap<TIntFloatHashMap>>();
140 for (int y = 0; y < height; y++) {
141 for (int x = 0; x < width; x++) {
142 if (image.pixels[y][x] == 1)
143 {
144 for (int rad = 0; rad < nRadius; rad++) {
145 final int actualrad = (rad * this.radIncr) + this.minRad;
146 final float radiusWeight = 1f / this.nDegree;
147
148
149
150
151 for (int ang = 0; ang < nDegree; ang++) {
152 final int x0 = round(x + this.cosanglemap[rad][ang]);
153 final int y0 = round(y + this.sinanglemap[rad][ang]);
154
155 TIntObjectHashMap<TIntFloatHashMap> xMap = this.radmap.get(actualrad);
156 if (xMap == null) {
157 this.radmap.put(actualrad, xMap = new TIntObjectHashMap<TIntFloatHashMap>());
158 }
159 TIntFloatHashMap yMap = xMap.get(x0);
160 if (yMap == null) {
161 xMap.put(x0, yMap = new TIntFloatHashMap());
162 }
163 yMap.adjustOrPutValue(y0, radiusWeight, radiusWeight);
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184 }
185 }
186 }
187 }
188 }
189 logger.debug("Done analysing the image!");
190 }
191
192
193
194
195
196
197
198
199 public List<WeightedCircle> getBest(int n) {
200
201 final BoundedPriorityQueue<WeightedCircle> bpq = new BoundedPriorityQueue<WeightedCircle>(n);
202 this.radmap.forEachEntry(new TIntObjectProcedure<TIntObjectHashMap<TIntFloatHashMap>>() {
203
204 @Override
205 public boolean execute(final int radius, TIntObjectHashMap<TIntFloatHashMap> b) {
206 b.forEachEntry(new TIntObjectProcedure<TIntFloatHashMap>() {
207
208 @Override
209 public boolean execute(final int x, TIntFloatHashMap b) {
210 b.forEachEntry(new TIntFloatProcedure() {
211
212 @Override
213 public boolean execute(int y, float weightedCount) {
214 bpq.offer(new WeightedCircle(x, y, radius, weightedCount));
215 return true;
216 }
217 });
218 return true;
219 }
220 });
221 return true;
222 }
223 });
224
225 return bpq.toOrderedList();
226 }
227 }