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.watershed.feature;
31
32 import org.openimaj.image.FImage;
33 import org.openimaj.image.pixel.IntValuePixel;
34 import org.openimaj.math.geometry.point.Point2d;
35 import org.openimaj.math.geometry.shape.Circle;
36 import org.openimaj.math.geometry.shape.Ellipse;
37 import org.openimaj.math.geometry.shape.EllipseUtilities;
38 import org.openimaj.math.geometry.shape.Polygon;
39 import org.openimaj.math.util.QuadraticEquation;
40
41 import Jama.Matrix;
42
43
44
45
46
47
48
49
50 public class MomentFeature implements ComponentFeature
51 {
52 int n = 0;
53 double mx = 0;
54 double my = 0;
55 double Mx2 = 0;
56 double My2 = 0;
57 double sxy = 0;
58 double sx = 0;
59 double sy = 0;
60
61 @Override
62 public void merge(ComponentFeature f) {
63 MomentFeature mf = (MomentFeature) f;
64
65 double dx = mf.mx - mx;
66 double dy = mf.my - my;
67
68 mx = (n*mx + mf.n*mf.mx) / (n + mf.n);
69 my = (n*my + mf.n*mf.my) / (n + mf.n);
70
71 Mx2 += mf.Mx2 + dx*dx*n*mf.n / (n + mf.n);
72 My2 += mf.My2 + dy*dy*n*mf.n / (n + mf.n);
73
74 n += mf.n;
75 sxy += mf.sxy;
76 sx += mf.sx;
77 sy += mf.sy;
78 }
79
80 @Override
81 public void addSample(IntValuePixel p) {
82 n++;
83 double dx = p.x - mx;
84 double dy = p.y - my;
85
86 mx += dx / n;
87 my += dy / n;
88
89 Mx2 += dx * (p.x - mx);
90 My2 += dy * (p.y - my);
91
92 sx += p.x;
93 sy += p.y;
94 sxy += p.x*p.y;
95 }
96
97
98
99
100
101 public double n() {
102 return n;
103 }
104
105
106
107
108
109 public double u11() {
110 return (sxy - sx*sy / n) / n;
111 }
112
113
114
115
116
117 public double u20() {
118 return Mx2 / n;
119 }
120
121
122
123
124
125 public double u02() {
126 return My2 / n;
127 }
128
129
130
131
132
133 public double m10() {
134 return mx;
135 }
136
137
138
139
140
141 public double m01() {
142 return my;
143 }
144
145 @Override
146 public MomentFeature clone() {
147 try {
148 return (MomentFeature) super.clone();
149 } catch (CloneNotSupportedException e) {
150 throw new AssertionError(e);
151 }
152 }
153
154
155
156
157
158 public Ellipse getEllipse() {
159 return getEllipse(1);
160 }
161
162
163
164
165
166
167 public Ellipse getEllipse(float sf) {
168 float u = (float) m10();
169 float v = (float) m01();
170 Matrix sm = new Matrix(new double[][]{
171 {u20(),u11()},
172 {u11(),u02()}
173 });
174 return EllipseUtilities.ellipseFromCovariance(u, v, sm, sf);
175 }
176
177
178
179
180
181
182
183 public Circle getCircle(float sf) {
184 Ellipse e = getEllipse(sf);
185 Point2d p = e.calculateCentroid();
186 return new Circle(p.getX(),p.getY(),(float)(e.getMajor() + e.getMinor())/2);
187 }
188
189
190
191
192
193
194
195
196 public Polygon getEllipseBoundingBox(float sf) {
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220 return this.getEllipse(sf).calculateOrientedBoundingBox();
221 }
222
223
224
225
226
227 public double getOrientation() {
228 double xx = u20();
229 double xy = u11();
230 double yy = u02();
231
232 return 0.5 * Math.atan2(2*xy, xx-yy);
233 }
234
235 protected double[] getEllipseBoundingRectsData(double sf) {
236 double xx = u20();
237 double xy = u11();
238 double yy = u02();
239
240 double theta = 0.5 * Math.atan2(2*xy, xx-yy);
241
242 double trace = xx + yy;
243 double det = (xx*yy) - (xy*xy);
244 double [] eigval = QuadraticEquation.solveGeneralQuadratic(1, -1*trace, det);
245
246 double a = 1.0 + Math.sqrt(Math.abs(eigval[1])) * sf * 4;
247 double b = 1.0 + Math.sqrt(Math.abs(eigval[0])) * sf * 4;
248
249 double [] data = {Math.max(a, b), Math.min(a, b), theta};
250
251 return data;
252 }
253
254
255
256
257
258
259
260
261 public FImage extractEllipsePatch(FImage image, double sf) {
262 double [] data = getEllipseBoundingRectsData(sf);
263 double height = data[1], width = data[0], ori = data[2];
264
265 int sx = (int) Math.rint(width);
266 int sy = (int) Math.rint(height);
267
268 FImage patch = new FImage(sx, sy);
269
270
271 for (int y=0; y<sy; y++) {
272 for (int x=0; x<sx; x++) {
273 double xbar = x - sx / 2.0;
274 double ybar = y - sy / 2.0;
275
276 double xx = (xbar * Math.cos(ori) - ybar * Math.sin(ori)) + mx;
277 double yy = (xbar * Math.sin(ori) + ybar * Math.cos(ori)) + my;
278
279 patch.setPixel(x, y, image.getPixelInterp(xx, yy));
280 }
281 }
282
283 return patch;
284 }
285 }