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;
31
32 import java.util.Comparator;
33
34 import org.apache.log4j.Logger;
35 import org.openimaj.image.analyser.PixelAnalyser;
36 import org.openimaj.image.colour.ColourSpace;
37 import org.openimaj.image.pixel.FValuePixel;
38 import org.openimaj.image.pixel.Pixel;
39 import org.openimaj.image.processor.KernelProcessor;
40 import org.openimaj.image.processor.PixelProcessor;
41 import org.openimaj.image.renderer.FImageRenderer;
42 import org.openimaj.image.renderer.RenderHints;
43 import org.openimaj.math.geometry.shape.Rectangle;
44 import org.openimaj.math.util.Interpolation;
45
46 import Jama.Matrix;
47
48
49
50
51
52
53
54
55
56
57
58 public class FImage extends SingleBandImage<Float, FImage>
59 {
60 private static final long serialVersionUID = 1L;
61
62
63 protected static Logger logger = Logger.getLogger(FImage.class);
64
65
66
67
68
69 protected static final float DEFAULT_GAUSS_TRUNCATE = 4.0f;
70
71
72 public float pixels[][];
73
74
75
76
77
78
79
80
81
82
83
84
85
86 public FImage(final float[] array, final int width, final int height)
87 {
88 assert (array.length == width * height);
89
90 this.pixels = new float[height][width];
91 this.height = height;
92 this.width = width;
93
94 for (int y = 0; y < height; y++)
95 for (int x = 0; x < width; x++)
96 this.pixels[y][x] = array[y * width + x];
97 }
98
99
100
101
102
103
104
105
106
107
108
109
110
111 public FImage(final double[] array, final int width, final int height)
112 {
113 assert (array.length == width * height);
114
115 this.pixels = new float[height][width];
116 this.height = height;
117 this.width = width;
118
119 for (int y = 0; y < height; y++)
120 for (int x = 0; x < width; x++)
121 this.pixels[y][x] = (float) array[y * width + x];
122 }
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138 public FImage(final double[] array, final int width, final int height, int offset)
139 {
140 assert (array.length == width * height);
141
142 this.pixels = new float[height][width];
143 this.height = height;
144 this.width = width;
145
146 for (int y = 0; y < height; y++)
147 for (int x = 0; x < width; x++)
148 this.pixels[y][x] = (float) array[offset + y * width + x];
149 }
150
151
152
153
154
155
156
157 public FImage(final float[][] array)
158 {
159 this.pixels = array;
160 this.height = array.length;
161 this.width = array[0].length;
162 }
163
164
165
166
167
168
169
170
171
172 public FImage(final int width, final int height) {
173 this.pixels = new float[height][width];
174
175 this.height = height;
176 this.width = width;
177 }
178
179
180
181
182
183
184
185
186
187
188
189 public FImage(final int[] data, final int width, final int height) {
190 this.internalAssign(data, width, height);
191 }
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206 public FImage(final int[] data, final int width, final int height, final ARGBPlane plane) {
207 this.width = width;
208 this.height = height;
209 this.pixels = new float[height][width];
210
211 for (int y = 0; y < height; y++) {
212 for (int x = 0; x < width; x++) {
213 final int rgb = data[x + y * width];
214
215 int colour = 0;
216 switch (plane)
217 {
218 case RED:
219 colour = ((rgb >> 16) & 0xff);
220 break;
221 case GREEN:
222 colour = ((rgb >> 8) & 0xff);
223 break;
224 case BLUE:
225 colour = ((rgb) & 0xff);
226 break;
227 default:
228 break;
229 }
230
231 this.pixels[y][x] = colour;
232 }
233 }
234 }
235
236
237
238
239
240
241 @Override
242 public FImage abs() {
243 for (int r = 0; r < this.height; r++)
244 for (int c = 0; c < this.width; c++)
245 this.pixels[r][c] = Math.abs(this.pixels[r][c]);
246 return this;
247 }
248
249
250
251
252
253
254
255
256
257
258
259
260
261 public FImage add(final FImage im)
262 {
263 if (!ImageUtilities.checkSameSize(this, im))
264 throw new AssertionError("images must be the same size");
265
266 final FImage newImage = new FImage(im.width, im.height);
267
268 for (int r = 0; r < im.height; r++)
269 for (int c = 0; c < im.width; c++)
270 newImage.pixels[r][c] = this.pixels[r][c] + im.pixels[r][c];
271
272 return newImage;
273 }
274
275
276
277
278
279
280
281 @Override
282 public FImage add(final Float num)
283 {
284 final FImage newImage = new FImage(this.width, this.height);
285 final float fnum = num;
286
287 for (int r = 0; r < this.height; r++)
288 for (int c = 0; c < this.width; c++)
289 newImage.pixels[r][c] = this.pixels[r][c] + fnum;
290
291 return newImage;
292 }
293
294
295
296
297
298
299
300
301
302
303 @Override
304 public FImage add(final Image<?, ?> im)
305 {
306 if (im instanceof FImage)
307 return this.add((FImage) im);
308 else
309 throw new UnsupportedOperationException("Unsupported Type");
310 }
311
312
313
314
315
316
317
318
319
320
321
322
323 public FImage addInplace(final FImage im)
324 {
325 if (!ImageUtilities.checkSameSize(this, im))
326 throw new AssertionError("images must be the same size");
327
328 for (int r = 0; r < im.height; r++)
329 for (int c = 0; c < im.width; c++)
330 this.pixels[r][c] += im.pixels[r][c];
331
332 return this;
333 }
334
335
336
337
338
339
340 @Override
341 public FImage addInplace(final Float num)
342 {
343 final float fnum = num;
344 for (int r = 0; r < this.height; r++)
345 for (int c = 0; c < this.width; c++)
346 this.pixels[r][c] += fnum;
347
348 return this;
349 }
350
351
352
353
354
355
356
357
358
359
360 @Override
361 public FImage addInplace(final Image<?, ?> im)
362 {
363 if (im instanceof FImage)
364 return this.addInplace((FImage) im);
365 else
366 throw new UnsupportedOperationException("Unsupported Type");
367 }
368
369
370
371
372
373
374 @Override
375 public FImage clip(final Float min, final Float max)
376 {
377 int r, c;
378
379 for (r = 0; r < this.height; r++)
380 {
381 for (c = 0; c < this.width; c++)
382 {
383 if (this.pixels[r][c] < min)
384 this.pixels[r][c] = 0;
385 if (this.pixels[r][c] > max)
386 this.pixels[r][c] = 1;
387 }
388 }
389
390 return this;
391 }
392
393
394
395
396
397
398 @Override
399 public FImage clipMax(final Float thresh)
400 {
401 final float fthresh = thresh;
402 for (int r = 0; r < this.height; r++)
403 {
404 for (int c = 0; c < this.width; c++)
405 {
406 if (this.pixels[r][c] > fthresh)
407 this.pixels[r][c] = 1;
408 }
409 }
410 return this;
411 }
412
413
414
415
416
417
418 @Override
419 public FImage clipMin(final Float thresh)
420 {
421 final float fthresh = thresh;
422 for (int r = 0; r < this.height; r++)
423 {
424 for (int c = 0; c < this.width; c++)
425 {
426 if (this.pixels[r][c] < fthresh)
427 this.pixels[r][c] = 0;
428 }
429 }
430 return this;
431 }
432
433
434
435
436
437
438 @Override
439 public FImage clone()
440 {
441 final FImage cpy = new FImage(this.width, this.height);
442 int r;
443
444 for (r = 0; r < this.height; r++)
445 System.arraycopy(this.pixels[r], 0, cpy.pixels[r], 0, this.width);
446
447 return cpy;
448 }
449
450 @Override
451 public FImageRenderer createRenderer() {
452 return new FImageRenderer(this);
453 }
454
455 @Override
456 public FImageRenderer createRenderer(final RenderHints options) {
457 return new FImageRenderer(this, options);
458 }
459
460
461
462
463
464
465
466
467
468
469
470
471 public FImage divide(final FImage im)
472 {
473 if (!ImageUtilities.checkSameSize(this, im))
474 throw new AssertionError("images must be the same size");
475
476 final FImage newImage = new FImage(im.width, im.height);
477 int r, c;
478
479 for (r = 0; r < im.height; r++)
480 for (c = 0; c < im.width; c++)
481 newImage.pixels[r][c] = this.pixels[r][c] / im.pixels[r][c];
482
483 return newImage;
484 }
485
486
487
488
489
490
491
492
493
494
495
496
497 public FImage divideInplace(final FImage im)
498 {
499 if (!ImageUtilities.checkSameSize(this, im))
500 throw new AssertionError("images must be the same size");
501
502 for (int y = 0; y < this.height; y++)
503 {
504 for (int x = 0; x < this.width; x++)
505 {
506 this.pixels[y][x] /= im.pixels[y][x];
507 }
508 }
509
510 return this;
511 }
512
513
514
515
516
517
518 @Override
519 public FImage divideInplace(final Float val)
520 {
521 final float fval = val;
522
523 for (int y = 0; y < this.height; y++)
524 for (int x = 0; x < this.width; x++)
525 this.pixels[y][x] /= fval;
526
527 return this;
528 }
529
530
531
532
533
534
535
536
537
538 public FImage divideInplace(final float fval)
539 {
540 for (int y = 0; y < this.height; y++)
541 for (int x = 0; x < this.width; x++)
542 this.pixels[y][x] /= fval;
543
544 return this;
545 }
546
547
548
549
550
551
552 @Override
553 public FImage divideInplace(final Image<?, ?> im)
554 {
555 if (im instanceof FImage)
556 return this.divideInplace((FImage) im);
557 else
558 throw new UnsupportedOperationException("Unsupported Type");
559 }
560
561
562
563
564
565
566
567 @Override
568 public FImage extractROI(final int x, final int y, final FImage out)
569 {
570 for (int r = y, rr = 0; rr < out.height; r++, rr++)
571 {
572 for (int c = x, cc = 0; cc < out.width; c++, cc++)
573 {
574 if (r < 0 || r >= this.height || c < 0 || c >= this.width)
575 (out).pixels[rr][cc] = 0;
576 else
577 (out).pixels[rr][cc] = this.pixels[r][c];
578 }
579 }
580
581 return out;
582 }
583
584
585
586
587
588
589 @Override
590 public FImage extractROI(final int x, final int y, final int w, final int h)
591 {
592 final FImage out = new FImage(w, h);
593
594 for (int r = y, rr = 0; rr < h; r++, rr++)
595 {
596 for (int c = x, cc = 0; cc < w; c++, cc++)
597 {
598 if (r < 0 || r >= this.height || c < 0 || c >= this.width)
599 out.pixels[rr][cc] = 0;
600 else
601 out.pixels[rr][cc] = this.pixels[r][c];
602 }
603 }
604
605 return out;
606 }
607
608
609
610
611
612
613 @Override
614 public FImage fill(final Float colour)
615 {
616 for (int r = 0; r < this.height; r++)
617 for (int c = 0; c < this.width; c++)
618 this.pixels[r][c] = colour;
619
620 return this;
621 }
622
623
624
625
626
627
628
629
630
631 public FImage fill(final float colour)
632 {
633 for (int r = 0; r < this.height; r++)
634 for (int c = 0; c < this.width; c++)
635 this.pixels[r][c] = colour;
636
637 return this;
638 }
639
640
641
642
643
644
645 @Override
646 public Rectangle getContentArea() {
647 int minc = this.width, maxc = 0, minr = this.height, maxr = 0;
648
649 for (int r = 0; r < this.height; r++) {
650 for (int c = 0; c < this.width; c++) {
651 if (this.pixels[r][c] > 0) {
652 if (c < minc)
653 minc = c;
654 if (c > maxc)
655 maxc = c;
656 if (r < minr)
657 minr = r;
658 if (r > maxr)
659 maxr = r;
660 }
661 }
662 }
663
664 return new Rectangle(minc, minr, maxc - minc + 1, maxr - minr + 1);
665 }
666
667
668
669
670
671
672 public double[] getDoublePixelVector()
673 {
674 final double f[] = new double[this.height * this.width];
675 for (int y = 0; y < this.height; y++)
676 for (int x = 0; x < this.width; x++)
677 f[x + y * this.width] = this.pixels[y][x];
678
679 return f;
680 }
681
682
683
684
685
686
687 @Override
688 public FImage getField(final Field f)
689 {
690 final FImage img = new FImage(this.width, this.height / 2);
691
692 int r, r2, c;
693 final int init = (f.equals(Field.ODD) ? 1 : 0);
694 for (r = init, r2 = 0; r < this.height && r2 < this.height / 2; r += 2, r2++)
695 {
696 for (c = 0; c < this.width; c++)
697 {
698 img.pixels[r2][c] = this.pixels[r][c];
699 }
700 }
701
702 return img;
703 }
704
705
706
707
708
709
710 @Override
711 public FImage getFieldCopy(final Field f)
712 {
713 final FImage img = new FImage(this.width, this.height);
714
715 int r, c;
716 for (r = 0; r < this.height; r += 2)
717 {
718 for (c = 0; c < this.width; c++)
719 {
720 if (f.equals(Field.EVEN))
721 {
722 img.pixels[r][c] = this.pixels[r][c];
723 img.pixels[r + 1][c] = this.pixels[r][c];
724 }
725 else
726 {
727 img.pixels[r][c] = this.pixels[r + 1][c];
728 img.pixels[r + 1][c] = this.pixels[r + 1][c];
729 }
730 }
731 }
732
733 return img;
734 }
735
736
737
738
739
740
741 @Override
742 public FImage getFieldInterpolate(final Field f)
743 {
744 final FImage img = new FImage(this.width, this.height);
745
746 int r, c;
747 for (r = 0; r < this.height; r += 2)
748 {
749 for (c = 0; c < this.width; c++)
750 {
751 if (f.equals(Field.EVEN))
752 {
753 img.pixels[r][c] = this.pixels[r][c];
754
755 if (r + 2 == this.height)
756 {
757 img.pixels[r + 1][c] = this.pixels[r][c];
758 }
759 else
760 {
761 img.pixels[r + 1][c] = 0.5F * (this.pixels[r][c] + this.pixels[r + 2][c]);
762 }
763 }
764 else
765 {
766 img.pixels[r + 1][c] = this.pixels[r + 1][c];
767
768 if (r == 0)
769 {
770 img.pixels[r][c] = this.pixels[r + 1][c];
771 }
772 else
773 {
774 img.pixels[r][c] = 0.5F * (this.pixels[r - 1][c] + this.pixels[r + 1][c]);
775 }
776 }
777 }
778 }
779
780 return img;
781 }
782
783
784
785
786
787
788 public float[] getFloatPixelVector()
789 {
790 final float f[] = new float[this.height * this.width];
791 for (int y = 0; y < this.height; y++)
792 for (int x = 0; x < this.width; x++)
793 f[x + y * this.width] = this.pixels[y][x];
794
795 return f;
796 }
797
798
799
800
801
802
803 @Override
804 public Float getPixel(final int x, final int y)
805 {
806 return this.pixels[y][x];
807 }
808
809
810
811
812
813
814 @Override
815 public Comparator<? super Float> getPixelComparator() {
816 return new Comparator<Float>() {
817
818 @Override
819 public int compare(final Float o1, final Float o2) {
820 return o1.compareTo(o2);
821 }
822
823 };
824 }
825
826
827
828
829
830
831
832 @Override
833 public Float getPixelInterp(final double x, final double y)
834 {
835 int x0 = (int) Math.floor(x);
836 int x1 = x0 + 1;
837 int y0 = (int) Math.floor(y);
838 int y1 = y0 + 1;
839
840 if (x0 < 0)
841 x0 = 0;
842 if (x0 >= this.width)
843 x0 = this.width - 1;
844 if (y0 < 0)
845 y0 = 0;
846 if (y0 >= this.height)
847 y0 = this.height - 1;
848
849 if (x1 < 0)
850 x1 = 0;
851 if (x1 >= this.width)
852 x1 = this.width - 1;
853 if (y1 < 0)
854 y1 = 0;
855 if (y1 >= this.height)
856 y1 = this.height - 1;
857
858 final float f00 = this.pixels[y0][x0];
859 final float f01 = this.pixels[y1][x0];
860 final float f10 = this.pixels[y0][x1];
861 final float f11 = this.pixels[y1][x1];
862 float dx = (float) (x - x0);
863 float dy = (float) (y - y0);
864 if (dx < 0)
865 dx = 1 + dx;
866 if (dy < 0)
867 dy = 1 + dy;
868
869 return Interpolation.bilerp(dx, dy, f00, f01, f10, f11);
870 }
871
872
873
874
875
876
877
878 @Override
879 public Float getPixelInterp(final double x, final double y, final Float background)
880 {
881 final int x0 = (int) Math.floor(x);
882 final int x1 = x0 + 1;
883 final int y0 = (int) Math.floor(y);
884 final int y1 = y0 + 1;
885
886 boolean tx0, tx1, ty0, ty1;
887 tx0 = ty0 = tx1 = ty1 = true;
888 if (x0 < 0)
889 tx0 = false;
890 if (x0 >= this.width)
891 tx0 = false;
892 if (y0 < 0)
893 ty0 = false;
894 if (y0 >= this.height)
895 ty0 = false;
896
897 if (x1 < 0)
898 tx1 = false;
899 if (x1 >= this.width)
900 tx1 = false;
901 if (y1 < 0)
902 ty1 = false;
903 if (y1 >= this.height)
904 ty1 = false;
905
906 final double f00 = (ty0 && tx0 ? this.pixels[y0][x0] : background.floatValue());
907 final double f01 = (ty1 && tx0 ? this.pixels[y1][x0] : background.floatValue());
908 final double f10 = (ty0 && tx1 ? this.pixels[y0][x1] : background.floatValue());
909 final double f11 = (ty1 && tx1 ? this.pixels[y1][x1] : background.floatValue());
910
911 double dx = x - x0;
912 double dy = y - y0;
913 if (dx < 0)
914 dx = 1 + dx;
915 if (dy < 0)
916 dy = 1 + dy;
917
918 final double interpVal = Interpolation.bilerp(dx, dy, f00, f01, f10, f11);
919 return (float) interpVal;
920 }
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935 public float getPixelInterpNative(final float x, final float y, final float background)
936 {
937 final int x0 = (int) Math.floor(x);
938 final int x1 = x0 + 1;
939 final int y0 = (int) Math.floor(y);
940 final int y1 = y0 + 1;
941
942 boolean tx0, tx1, ty0, ty1;
943 tx0 = ty0 = tx1 = ty1 = true;
944 if (x0 < 0)
945 tx0 = false;
946 if (x0 >= this.width)
947 tx0 = false;
948 if (y0 < 0)
949 ty0 = false;
950 if (y0 >= this.height)
951 ty0 = false;
952
953 if (x1 < 0)
954 tx1 = false;
955 if (x1 >= this.width)
956 tx1 = false;
957 if (y1 < 0)
958 ty1 = false;
959 if (y1 >= this.height)
960 ty1 = false;
961
962 final float f00 = (ty0 && tx0 ? this.pixels[y0][x0] : background);
963 final float f01 = (ty1 && tx0 ? this.pixels[y1][x0] : background);
964 final float f10 = (ty0 && tx1 ? this.pixels[y0][x1] : background);
965 final float f11 = (ty1 && tx1 ? this.pixels[y1][x1] : background);
966
967 float dx = x - x0;
968 float dy = y - y0;
969 if (dx < 0)
970 dx = 1 + dx;
971 if (dy < 0)
972 dy = 1 + dy;
973
974 final float interpVal = Interpolation.bilerpf(dx, dy, f00, f01, f10, f11);
975 return interpVal;
976 }
977
978
979
980
981
982
983 @Override
984 public FImage internalCopy(final FImage im)
985 {
986 final int h = im.height;
987 final int w = im.width;
988 final float[][] impixels = im.pixels;
989
990 for (int r = 0; r < h; r++)
991 System.arraycopy(impixels[r], 0, this.pixels[r], 0, w);
992
993 return this;
994 }
995
996
997
998
999
1000
1001 @Override
1002 public FImage internalAssign(final FImage im)
1003 {
1004 this.pixels = im.pixels;
1005 this.height = im.height;
1006 this.width = im.width;
1007
1008 return this;
1009 }
1010
1011
1012
1013
1014
1015
1016
1017 @Override
1018 public FImage internalAssign(final int[] data, final int width, final int height) {
1019 if (this.height != height || this.width != width) {
1020 this.height = height;
1021 this.width = width;
1022 this.pixels = new float[height][width];
1023 }
1024
1025 for (int y = 0; y < height; y++) {
1026 for (int x = 0; x < width; x++) {
1027 final int rgb = data[x + width * y];
1028
1029 final int red = ((rgb >> 16) & 0xff);
1030 final int green = ((rgb >> 8) & 0xff);
1031 final int blue = ((rgb) & 0xff);
1032
1033
1034
1035 final float fpix = 0.299f * red + 0.587f * green + 0.114f * blue;
1036
1037 this.pixels[y][x] = ImageUtilities.BYTE_TO_FLOAT_LUT[(int) fpix];
1038 }
1039 }
1040 return this;
1041 }
1042
1043
1044
1045
1046
1047
1048 @Override
1049 public FImage inverse()
1050 {
1051 int r, c;
1052 final float max = this.max();
1053
1054 for (r = 0; r < this.height; r++)
1055 for (c = 0; c < this.width; c++)
1056 this.pixels[r][c] = max - this.pixels[r][c];
1057
1058 return this;
1059 }
1060
1061
1062
1063
1064
1065
1066 @Override
1067 public Float max()
1068 {
1069 int r, c;
1070 float max = Float.MIN_VALUE;
1071
1072 for (r = 0; r < this.height; r++)
1073 for (c = 0; c < this.width; c++)
1074 if (max < this.pixels[r][c])
1075 max = this.pixels[r][c];
1076
1077 return max;
1078 }
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088 public FValuePixel maxPixel()
1089 {
1090 final FValuePixel max = new FValuePixel(-1, -1);
1091 max.value = -Float.MAX_VALUE;
1092
1093 for (int y = 0; y < this.height; y++) {
1094 for (int x = 0; x < this.width; x++) {
1095 if (max.value < this.pixels[y][x]) {
1096 max.value = this.pixels[y][x];
1097 max.x = x;
1098 max.y = y;
1099 }
1100 }
1101 }
1102
1103 return max;
1104 }
1105
1106
1107
1108
1109
1110
1111 @Override
1112 public Float min()
1113 {
1114 int r, c;
1115 float min = Float.MAX_VALUE;
1116
1117 for (r = 0; r < this.height; r++)
1118 for (c = 0; c < this.width; c++)
1119 if (min > this.pixels[r][c])
1120 min = this.pixels[r][c];
1121
1122 return min;
1123 }
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133 public FValuePixel minPixel()
1134 {
1135 final FValuePixel min = new FValuePixel(-1, -1);
1136 min.value = Float.MAX_VALUE;
1137
1138 for (int y = 0; y < this.height; y++)
1139 for (int x = 0; x < this.width; x++)
1140 if (min.value > this.pixels[y][x]) {
1141 min.value = this.pixels[y][x];
1142 min.x = x;
1143 min.y = y;
1144 }
1145
1146 return min;
1147 }
1148
1149
1150
1151
1152
1153
1154 @Override
1155 public FImage multiply(final Float num)
1156 {
1157 return super.multiply(num);
1158 }
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172 public FImage multiplyInplace(final FImage im)
1173 {
1174 if (!ImageUtilities.checkSameSize(this, im))
1175 throw new AssertionError("images must be the same size");
1176
1177 for (int r = 0; r < this.height; r++)
1178 {
1179 for (int c = 0; c < this.width; c++)
1180 {
1181 this.pixels[r][c] *= im.pixels[r][c];
1182 }
1183 }
1184
1185 return this;
1186 }
1187
1188
1189
1190
1191
1192
1193 @Override
1194 public FImage multiplyInplace(final Float num)
1195 {
1196 final float fnum = num;
1197 for (int r = 0; r < this.height; r++)
1198 {
1199 for (int c = 0; c < this.width; c++)
1200 {
1201 this.pixels[r][c] *= fnum;
1202 }
1203 }
1204
1205 return this;
1206 }
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216 public FImage multiplyInplace(final float fnum)
1217 {
1218 for (int r = 0; r < this.height; r++)
1219 {
1220 for (int c = 0; c < this.width; c++)
1221 {
1222 this.pixels[r][c] *= fnum;
1223 }
1224 }
1225
1226 return this;
1227 }
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238 @Override
1239 public FImage multiplyInplace(final Image<?, ?> im)
1240 {
1241 if (im instanceof FImage)
1242 return this.multiplyInplace((FImage) im);
1243 else
1244 throw new UnsupportedOperationException("Unsupported Type");
1245 }
1246
1247
1248
1249
1250
1251
1252
1253 @Override
1254 public FImage newInstance(final int width, final int height)
1255 {
1256 return new FImage(width, height);
1257 }
1258
1259
1260
1261
1262
1263
1264 @Override
1265 public FImage normalise()
1266 {
1267 final float min = this.min();
1268 final float max = this.max();
1269
1270 if (max == min)
1271 return this;
1272
1273 for (int r = 0; r < this.height; r++)
1274 {
1275 for (int c = 0; c < this.width; c++)
1276 {
1277 this.pixels[r][c] = (this.pixels[r][c] - min) / (max - min);
1278 }
1279 }
1280
1281 return this;
1282 }
1283
1284
1285
1286
1287
1288
1289 @Override
1290 public FImage process(final KernelProcessor<Float, FImage> p) {
1291 return this.process(p, false);
1292 }
1293
1294
1295
1296
1297
1298
1299
1300
1301 @Override
1302 public FImage process(final KernelProcessor<Float, FImage> p, final boolean pad)
1303 {
1304 final FImage newImage = new FImage(this.width, this.height);
1305 final int kh = p.getKernelHeight();
1306 final int kw = p.getKernelWidth();
1307
1308 final FImage tmp = new FImage(kw, kh);
1309
1310 final int hh = kh / 2;
1311 final int hw = kw / 2;
1312
1313 if (!pad) {
1314 for (int y = hh; y < this.height - (kh - hh); y++) {
1315 for (int x = hw; x < this.width - (kw - hw); x++) {
1316 newImage.pixels[y][x] = p.processKernel(this.extractROI(x - hw, y - hh, tmp));
1317 }
1318 }
1319 } else {
1320 for (int y = 0; y < this.height; y++) {
1321 for (int x = 0; x < this.width; x++) {
1322 newImage.pixels[y][x] = p.processKernel(this.extractROI(x - hw, y - hh, tmp));
1323 }
1324 }
1325 }
1326
1327 return newImage;
1328 }
1329
1330
1331
1332
1333
1334
1335
1336 @Override
1337 public FImage processInplace(final PixelProcessor<Float> p)
1338 {
1339 for (int y = 0; y < this.height; y++)
1340 {
1341 for (int x = 0; x < this.width; x++)
1342 {
1343 this.pixels[y][x] = p.processPixel(this.pixels[y][x]);
1344 }
1345 }
1346
1347 return this;
1348 }
1349
1350
1351
1352
1353
1354
1355
1356 @Override
1357 public void analyseWith(final PixelAnalyser<Float> p)
1358 {
1359 p.reset();
1360
1361 for (int y = 0; y < this.height; y++)
1362 {
1363 for (int x = 0; x < this.width; x++)
1364 {
1365 p.analysePixel(this.pixels[y][x]);
1366 }
1367 }
1368 }
1369
1370
1371
1372
1373
1374
1375 @Override
1376 public void setPixel(final int x, final int y, final Float val) {
1377 if (x >= 0 && x < this.width && y >= 0 && y < this.height)
1378 this.pixels[y][x] = val;
1379 }
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389 public FImage subtract(final FImage im)
1390 {
1391 if (!ImageUtilities.checkSameSize(this, im))
1392 throw new AssertionError("images must be the same size");
1393
1394 final FImage newImage = new FImage(im.width, im.height);
1395 int r, c;
1396
1397 for (r = 0; r < im.height; r++)
1398 for (c = 0; c < im.width; c++)
1399 newImage.pixels[r][c] = this.pixels[r][c] - im.pixels[r][c];
1400 return newImage;
1401 }
1402
1403
1404
1405
1406
1407
1408 @Override
1409 public FImage subtract(final Float num)
1410 {
1411 final FImage newImage = new FImage(this.width, this.height);
1412
1413 for (int r = 0; r < this.height; r++)
1414 {
1415 for (int c = 0; c < this.width; c++)
1416 {
1417 newImage.pixels[r][c] = this.pixels[r][c] - num;
1418 }
1419 }
1420 return newImage;
1421 }
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431 @Override
1432 public FImage subtract(final Image<?, ?> input)
1433 {
1434 if (input instanceof FImage)
1435 return this.subtract((FImage) input);
1436 else
1437 throw new UnsupportedOperationException("Unsupported Type");
1438 }
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448 public FImage subtractInplace(final FImage im)
1449 {
1450 if (!ImageUtilities.checkSameSize(this, im))
1451 throw new AssertionError("images must be the same size");
1452
1453 float pix1[][], pix2[][];
1454 int r, c;
1455
1456 pix1 = this.pixels;
1457 pix2 = im.pixels;
1458
1459 for (r = 0; r < this.height; r++)
1460 for (c = 0; c < this.width; c++)
1461 pix1[r][c] -= pix2[r][c];
1462
1463 return this;
1464 }
1465
1466
1467
1468
1469
1470
1471 @Override
1472 public FImage subtractInplace(final Float num)
1473 {
1474 final float fnum = num;
1475 for (int r = 0; r < this.height; r++)
1476 {
1477 for (int c = 0; c < this.width; c++)
1478 {
1479 this.pixels[r][c] -= fnum;
1480 }
1481 }
1482
1483 return this;
1484 }
1485
1486
1487
1488
1489
1490
1491 @Override
1492 public FImage subtractInplace(final Image<?, ?> im)
1493 {
1494 if (im instanceof FImage)
1495 return this.subtractInplace((FImage) im);
1496 else
1497 throw new UnsupportedOperationException("Unsupported Type");
1498 }
1499
1500
1501
1502
1503
1504
1505 @Override
1506 public FImage threshold(final Float thresh)
1507 {
1508 final float fthresh = thresh;
1509 for (int r = 0; r < this.height; r++)
1510 {
1511 for (int c = 0; c < this.width; c++)
1512 {
1513 if (this.pixels[r][c] <= fthresh)
1514 this.pixels[r][c] = 0;
1515 else
1516 this.pixels[r][c] = 1;
1517 }
1518 }
1519 return this;
1520 }
1521
1522
1523
1524
1525
1526
1527 @Override
1528 public byte[] toByteImage()
1529 {
1530 final byte[] pgmData = new byte[this.height * this.width];
1531
1532 for (int j = 0; j < this.height; j++)
1533 {
1534 for (int i = 0; i < this.width; i++)
1535 {
1536 int v = (int) (255.0f * this.pixels[j][i]);
1537
1538 v = Math.max(0, Math.min(255, v));
1539
1540 pgmData[i + j * this.width] = (byte) (v & 0xFF);
1541 }
1542 }
1543 return pgmData;
1544 }
1545
1546
1547
1548
1549
1550
1551 @Override
1552 public int[] toPackedARGBPixels()
1553 {
1554 final int[] bimg = new int[this.width * this.height];
1555
1556 for (int r = 0; r < this.height; r++) {
1557 for (int c = 0; c < this.width; c++) {
1558 final int v = (Math.max(0, Math.min(255, (int) (this.pixels[r][c] * 255))));
1559
1560 final int rgb = 0xff << 24 | v << 16 | v << 8 | v;
1561 bimg[c + this.width * r] = rgb;
1562 }
1563 }
1564
1565 return bimg;
1566 }
1567
1568
1569
1570
1571
1572
1573 @Override
1574 public String toString() {
1575 String imageString = "";
1576 for (int y = 0; y < this.height; y++) {
1577 for (int x = 0; x < this.width; x++) {
1578 imageString += String.format("%+.3f ", this.pixels[y][x]);
1579 if (x == 16) {
1580 if (this.width - 16 <= x)
1581 continue;
1582 imageString += "... ";
1583 x = this.width - 16;
1584 }
1585 }
1586 imageString += "\n";
1587 if (y == 16) {
1588 if (this.height - 16 <= y)
1589 continue;
1590 y = this.height - 16;
1591 imageString += "... \n";
1592 }
1593
1594 }
1595 return imageString;
1596 }
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608 public String toString(final String format) {
1609 String imageString = "";
1610 for (int y = 0; y < this.height; y++) {
1611 for (int x = 0; x < this.width; x++) {
1612 imageString += String.format(format, this.pixels[y][x]);
1613 }
1614 imageString += "\n";
1615 }
1616 return imageString;
1617 }
1618
1619
1620
1621
1622
1623
1624 @Override
1625 public FImage transform(final Matrix transform) {
1626 return super.transform(transform);
1627 }
1628
1629
1630
1631
1632
1633
1634 @Override
1635 public FImage zero()
1636 {
1637 for (int r = 0; r < this.height; r++)
1638 {
1639 for (int c = 0; c < this.width; c++)
1640 {
1641 this.pixels[r][c] = 0;
1642 }
1643 }
1644 return this;
1645 }
1646
1647 @Override
1648 public boolean equals(final Object o) {
1649 if (!(o instanceof FImage)) {
1650 return false;
1651 }
1652 return this.equalsThresh((FImage) o, 0);
1653 }
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666 public boolean equalsThresh(final FImage o, final float thresh) {
1667 final FImage that = o;
1668 if (that.height != this.height || that.width != this.width)
1669 return false;
1670 for (int i = 0; i < this.height; i++) {
1671 for (int j = 0; j < this.width; j++) {
1672 if (Math.abs(that.pixels[i][j] - this.pixels[i][j]) > thresh) {
1673 return false;
1674 }
1675 }
1676 }
1677 return true;
1678 }
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688 public float getPixelNative(final Pixel p) {
1689 return this.getPixelNative(p.x, p.y);
1690 }
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702 public float getPixelNative(final int x, final int y) {
1703 return this.pixels[y][x];
1704 }
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715 public float[] getPixelVectorNative(final float[] f)
1716 {
1717 for (int y = 0; y < this.getHeight(); y++)
1718 for (int x = 0; x < this.getWidth(); x++)
1719 f[x + y * this.getWidth()] = this.pixels[y][x];
1720
1721 return f;
1722 }
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735 public void setPixelNative(final int x, final int y, final float val) {
1736 this.pixels[y][x] = val;
1737 }
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750 public static FImage[] createArray(final int num, final int width, final int height) {
1751 final FImage[] array = new FImage[num];
1752
1753 for (int i = 0; i < num; i++) {
1754 array[i] = new FImage(width, height);
1755 }
1756
1757 return array;
1758 }
1759
1760
1761
1762
1763 public float sum() {
1764 float sum = 0;
1765 for (final float[] row : this.pixels) {
1766 for (int i = 0; i < row.length; i++) {
1767 sum += row[i];
1768 }
1769 }
1770 return sum;
1771 }
1772
1773
1774
1775
1776
1777
1778 public MBFImage toRGB() {
1779 return new MBFImage(ColourSpace.RGB, this.clone(), this.clone(), this.clone());
1780 }
1781
1782 @Override
1783 public FImage flipX() {
1784 final int hwidth = this.width / 2;
1785
1786 for (int y = 0; y < this.height; y++) {
1787 for (int x = 0; x < hwidth; x++) {
1788 final int xx = this.width - x - 1;
1789
1790 final float tmp = this.pixels[y][x];
1791
1792 this.pixels[y][x] = this.pixels[y][xx];
1793 this.pixels[y][xx] = tmp;
1794 }
1795 }
1796 return this;
1797 }
1798
1799 @Override
1800 public FImage flipY() {
1801 final int hheight = this.height / 2;
1802
1803 for (int y = 0; y < hheight; y++) {
1804 final int yy = this.height - y - 1;
1805
1806 for (int x = 0; x < this.width; x++) {
1807 final float tmp = this.pixels[y][x];
1808
1809 this.pixels[y][x] = this.pixels[yy][x];
1810 this.pixels[yy][x] = tmp;
1811 }
1812 }
1813
1814 return this;
1815 }
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831 public FImage overlayInplace(final FImage img, final FImage alpha, final int x, final int y)
1832 {
1833 final int sx = Math.max(x, 0);
1834 final int sy = Math.max(y, 0);
1835 final int ex = Math.min(this.width, x + img.getWidth());
1836 final int ey = Math.min(this.height, y + img.getHeight());
1837
1838 for (int yc = sy; yc < ey; yc++)
1839 {
1840 for (int xc = sx; xc < ex; xc++)
1841 {
1842 final float a = alpha.pixels[yc - sy][xc - sx];
1843 this.pixels[yc][xc] = (a * img.pixels[yc - sy][xc - sx] +
1844 (1 - a) * this.pixels[yc][xc]);
1845 }
1846 }
1847
1848 return this;
1849 }
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860 @Override
1861 public FImage overlayInplace(final FImage image, final int x, final int y)
1862 {
1863 return this.overlayInplace(image, this.clone().fill(1f), x, y);
1864 }
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875 public static FImage randomImage(final int width, final int height) {
1876 final FImage img = new FImage(width, height);
1877
1878 for (int y = 0; y < height; y++)
1879 for (int x = 0; x < width; x++)
1880 img.pixels[y][x] = (float) Math.random();
1881
1882 return img;
1883 }
1884
1885 @Override
1886 public FImage replace(Float target, Float replacement) {
1887 return replace((float) target, (float) replacement);
1888 }
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900 public FImage replace(float target, float replacement) {
1901 for (int r = 0; r < this.height; r++)
1902 for (int c = 0; c < this.width; c++)
1903 if (this.pixels[r][c] == target)
1904 this.pixels[r][c] = replacement;
1905
1906 return this;
1907 }
1908
1909 @Override
1910 public FImage extractCentreSubPix(float cx, float cy, FImage out) {
1911 final int width = out.width;
1912 final int height = out.height;
1913 for (int y = 0; y < height; y++) {
1914 for (int x = 0; x < width; x++) {
1915 final float ix = (float) (x + cx - (width - 1) * 0.5);
1916 final float iy = (float) (y + cy - (height - 1) * 0.5);
1917 out.pixels[y][x] = this.getPixelInterpNative(ix, iy, 0f);
1918 }
1919 }
1920 return out;
1921 }
1922 }