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.pixel.statistics;
31
32 import java.util.Arrays;
33
34 import org.apache.commons.math.DimensionMismatchException;
35 import org.apache.commons.math.stat.descriptive.MultivariateSummaryStatistics;
36 import org.openimaj.image.FImage;
37 import org.openimaj.image.pixel.sampling.FLineSampler;
38 import org.openimaj.math.geometry.line.Line2d;
39 import org.openimaj.math.geometry.point.Point2d;
40 import org.openimaj.math.geometry.point.Point2dImpl;
41 import org.openimaj.math.util.FloatArrayStatsUtils;
42 import org.openimaj.util.array.ArrayUtils;
43
44 import Jama.Matrix;
45
46
47
48
49
50
51
52
53
54
55
56
57
58 @SuppressWarnings("deprecation")
59 public class FStatisticalPixelProfileModel implements PixelProfileModel<FImage> {
60 private MultivariateSummaryStatistics statistics;
61 private int nsamples;
62 private FLineSampler sampler;
63
64 private double[] mean;
65 private Matrix invCovar;
66
67
68
69
70
71
72
73
74
75
76 public FStatisticalPixelProfileModel(int nsamples, FLineSampler sampler) {
77 this.nsamples = nsamples;
78 this.statistics = new MultivariateSummaryStatistics(nsamples, true);
79 this.sampler = sampler;
80 }
81
82 private float[] normaliseSamples(float[] samples) {
83 final float sum = FloatArrayStatsUtils.sum(samples);
84
85 for (int i = 0; i < samples.length; i++) {
86 samples[i] /= sum;
87 }
88
89 return samples;
90 }
91
92 @Override
93 public void updateModel(FImage image, Line2d line) {
94 final float[] samples = normaliseSamples(sampler.extractSamples(line, image, nsamples));
95 try {
96 statistics.addValue(ArrayUtils.convertToDouble(samples));
97 } catch (final DimensionMismatchException e) {
98 throw new RuntimeException(e);
99 }
100
101 invCovar = null;
102 mean = null;
103 }
104
105
106
107
108 public double[] getMean() {
109 if (mean == null) {
110 mean = statistics.getMean();
111 invCovar = new Matrix(statistics.getCovariance().getData()).inverse();
112 }
113
114 return mean;
115 }
116
117
118
119
120 public Matrix getCovariance() {
121 if (mean == null) {
122 mean = statistics.getMean();
123 invCovar = new Matrix(statistics.getCovariance().getData()).inverse();
124 }
125 return new Matrix(statistics.getCovariance().getData());
126 }
127
128
129
130
131 public Matrix getInverseCovariance() {
132 if (mean == null) {
133 mean = statistics.getMean();
134 invCovar = new Matrix(statistics.getCovariance().getData()).inverse();
135 }
136 return invCovar;
137 }
138
139
140
141
142
143
144
145
146
147
148 public float computeMahalanobis(float[] vector) {
149 if (mean == null) {
150 mean = statistics.getMean();
151 try {
152 invCovar = new Matrix(statistics.getCovariance().getData()).inverse();
153 } catch (final RuntimeException e) {
154 invCovar = Matrix.identity(nsamples, nsamples);
155 }
156 }
157
158 final double[] meanCentered = new double[mean.length];
159 for (int i = 0; i < mean.length; i++) {
160 meanCentered[i] = vector[i] - mean[i];
161 }
162
163 final Matrix mct = new Matrix(new double[][] { meanCentered });
164 final Matrix mc = mct.transpose();
165
166 final Matrix dist = mct.times(invCovar).times(mc);
167
168 return (float) dist.get(0, 0);
169 }
170
171
172
173
174
175
176
177
178
179
180
181 public float computeMahalanobis(FImage image, Line2d line) {
182 final float[] samples = normaliseSamples(sampler.extractSamples(line, image, nsamples));
183 return computeMahalanobis(samples);
184 }
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203 public float[] computeMahalanobisWindowed(FImage image, Line2d line, int numSamples) {
204 final float[] samples = sampler.extractSamples(line, image, numSamples);
205 return computeMahalanobisWindowed(samples);
206 }
207
208 @Override
209 public Point2d computeNewBest(FImage image, Line2d line, int numSamples) {
210 final float[] resp = computeMahalanobisWindowed(image, line, numSamples);
211
212 final int minIdx = ArrayUtils.minIndex(resp);
213 final int offset = (numSamples - nsamples) / 2;
214
215 if (resp[offset] == resp[minIdx])
216
217 return line.calculateCentroid();
218
219
220
221 line = this.sampler.getSampleLine(line, image, numSamples);
222
223 final float x = line.begin.getX();
224 final float y = line.begin.getY();
225 final float dxStep = (line.end.getX() - x) / (numSamples - 1);
226 final float dyStep = (line.end.getY() - y) / (numSamples - 1);
227
228 return new Point2dImpl(x + (minIdx + offset) * dxStep, y + (minIdx + offset) * dyStep);
229 }
230
231 @Override
232 public float computeMovementDistance(FImage image, Line2d line, int numSamples, Point2d pt) {
233 final Line2d sampleLine = sampler.getSampleLine(line, image, numSamples);
234
235 return (float) (2 * Line2d.distance(sampleLine.calculateCentroid(), pt) / sampleLine.calculateLength());
236 }
237
238
239
240
241
242
243
244
245
246
247
248
249
250 public float[] computeMahalanobisWindowed(float[] vector) {
251 final int maxShift = vector.length - nsamples + 1;
252
253 final float[] responses = new float[maxShift];
254 float[] samples = new float[nsamples];
255 for (int i = 0; i < maxShift; i++) {
256 System.arraycopy(vector, i, samples, 0, nsamples);
257 samples = normaliseSamples(samples);
258 responses[i] = computeMahalanobis(samples);
259 }
260
261 return responses;
262 }
263
264 @Override
265 public String toString() {
266 return "\nPixelProfileModel[\n" +
267 "\tcount = " + statistics.getN() + "\n" +
268 "\tmean = " + Arrays.toString(statistics.getMean()) + "\n" +
269 "\tcovar = " + statistics.getCovariance() + "\n" +
270 "]";
271 }
272
273
274
275
276 public int getNumberSamples() {
277 return nsamples;
278 }
279
280
281
282
283 public FLineSampler getSampler() {
284 return sampler;
285 }
286
287 @Override
288 public float computeCost(FImage image, Line2d line) {
289 return computeMahalanobis(image, line);
290 }
291 }