001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.image;
031
032import java.util.Comparator;
033
034import org.apache.log4j.Logger;
035import org.openimaj.image.analyser.PixelAnalyser;
036import org.openimaj.image.colour.ColourSpace;
037import org.openimaj.image.pixel.FValuePixel;
038import org.openimaj.image.pixel.Pixel;
039import org.openimaj.image.processor.KernelProcessor;
040import org.openimaj.image.processor.PixelProcessor;
041import org.openimaj.image.renderer.FImageRenderer;
042import org.openimaj.image.renderer.RenderHints;
043import org.openimaj.math.geometry.shape.Rectangle;
044import org.openimaj.math.util.Interpolation;
045
046import Jama.Matrix;
047
048/**
049 * Class representing a single-band floating-point image; that is an image where
050 * each pixel is represented by a floating-point number.
051 * <p>
052 * {@link FImage}s can be created from PGM files or from pixel arrays. If you
053 * wish to read other types of files then use the {@link ImageUtilities} class
054 * that provides read/write functions for {@link Image} objects.
055 *
056 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
057 */
058public class FImage extends SingleBandImage<Float, FImage>
059{
060        private static final long serialVersionUID = 1L;
061
062        /** The logging class */
063        protected static Logger logger = Logger.getLogger(FImage.class);
064
065        /**
066         * The default number of sigmas at which the Gaussian function is truncated
067         * when building a kernel
068         */
069        protected static final float DEFAULT_GAUSS_TRUNCATE = 4.0f;
070
071        /** The underlying pixels */
072        public float pixels[][];
073
074        /**
075         * Create an {@link FImage} from an array of floating point values with the
076         * given width and height. The length of the array must equal the width
077         * multiplied by the height.
078         *
079         * @param array
080         *            An array of floating point values.
081         * @param width
082         *            The width of the resulting image.
083         * @param height
084         *            The height of th resulting image.
085         */
086        public FImage(final float[] array, final int width, final int height)
087        {
088                assert (array.length == width * height);
089
090                this.pixels = new float[height][width];
091                this.height = height;
092                this.width = width;
093
094                for (int y = 0; y < height; y++)
095                        for (int x = 0; x < width; x++)
096                                this.pixels[y][x] = array[y * width + x];
097        }
098
099        /**
100         * Create an {@link FImage} from an array of double values with the given
101         * width and height. The length of the array must equal the width multiplied
102         * by the height. The values will be downcast to floats.
103         *
104         * @param array
105         *            An array of floating point values.
106         * @param width
107         *            The width of the resulting image.
108         * @param height
109         *            The height of th resulting image.
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         * Create an {@link FImage} from an array of double values with the given
126         * width and height. The length of the array must equal the width multiplied
127         * by the height. The values will be downcast to floats.
128         *
129         * @param array
130         *            An array of floating point values.
131         * @param width
132         *            The width of the resulting image.
133         * @param height
134         *            The height of the resulting image.
135         * @param offset
136         *            The offset in the array to begin reading from
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         * Create an {@link FImage} from an array of floating point values.
153         *
154         * @param array
155         *            the array representing pixel values to copy data from.
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         * Create an empty {@link FImage} of the given size.
166         *
167         * @param width
168         *            image width (number of columns)
169         * @param height
170         *            image height (number of rows)
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         * Construct an {@link FImage} from an array of packed ARGB integers.
181         *
182         * @param data
183         *            array of packed ARGB pixels
184         * @param width
185         *            the image width
186         * @param height
187         *            the image height
188         */
189        public FImage(final int[] data, final int width, final int height) {
190                this.internalAssign(data, width, height);
191        }
192
193        /**
194         * Construct an {@link FImage} from an array of packed ARGB integers using
195         * the specified plane.
196         *
197         * @param data
198         *            array of packed ARGB pixels
199         * @param width
200         *            the image width
201         * @param height
202         *            the image height
203         * @param plane
204         *            The {@link ARGBPlane} to copy data from
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         * {@inheritDoc}
238         *
239         * @see org.openimaj.image.Image#abs()
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         * Adds the pixel values of the given {@link FImage} to the pixels of this
251         * image. Returns a new {@link FImage} and does not affect this image or the
252         * given image. This is a version of {@link Image#add(Image)} which takes an
253         * {@link FImage}. This method directly accesses the underlying float[][]
254         * and is therefore fast. This function returns a new {@link FImage}.
255         *
256         * @see org.openimaj.image.Image#add(Image)
257         * @param im
258         *            {@link FImage} to add into this one.
259         * @return A new {@link FImage}
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         * Returns a new {@link FImage} that contains the pixels of this image
277         * increased by the given value. {@inheritDoc}
278         *
279         * @see org.openimaj.image.Image#add(java.lang.Object)
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         * {@inheritDoc} This method throws an {@link UnsupportedOperationException}
296         * if the given image is not an {@link FImage}.
297         *
298         * @see org.openimaj.image.Image#add(org.openimaj.image.Image)
299         * @exception UnsupportedOperationException
300         *                if an unsupported type is added
301         * @return a reference to this {@link FImage}
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         * Adds the given image pixel values to the pixel values of this image.
314         * Version of {@link Image#addInplace(Image)} which takes an {@link FImage}.
315         * This directly accesses the underlying float[][] and is therefore fast.
316         * This function side-affects the pixels in this {@link FImage}.
317         *
318         * @see Image#addInplace(Image)
319         * @param im
320         *            the FImage to add
321         * @return a reference to this
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         * {@inheritDoc}
337         *
338         * @see org.openimaj.image.Image#addInplace(java.lang.Object)
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         * {@inheritDoc} This method throws an {@link UnsupportedOperationException}
353         * if the given image is not an {@link FImage}.
354         *
355         * @see org.openimaj.image.Image#addInplace(org.openimaj.image.Image)
356         * @exception UnsupportedOperationException
357         *                if an unsupported type is added
358         * @return a reference to this {@link FImage}
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         * {@inheritDoc}
371         *
372         * @see org.openimaj.image.Image#clip(java.lang.Object, java.lang.Object)
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         * {@inheritDoc}
395         *
396         * @see org.openimaj.image.Image#clipMax(java.lang.Object)
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         * {@inheritDoc}
415         *
416         * @see org.openimaj.image.Image#clipMin(java.lang.Object)
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         * {@inheritDoc}
435         *
436         * @see org.openimaj.image.SingleBandImage#clone()
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         * Divides the pixels values of this image with the values from the given
462         * image. This is a version of {@link Image#divide(Image)} which takes an
463         * {@link FImage}. This directly accesses the underlying float[][] and is
464         * therefore fast. This function returns a new {@link FImage}.
465         *
466         * @see Image#divide(Image)
467         * @param im
468         *            the {@link FImage} to be the denominator.
469         * @return A new {@link FImage}
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         * Divides the pixel values of this image with the values from the given
488         * image. This is a version of {@link Image#divideInplace(Image)} which
489         * takes an {@link FImage}. This directly accesses the underlying float[][]
490         * and is therefore fast. This function side-affects this image.
491         *
492         * @see Image#divideInplace(Image)
493         * @param im
494         *            the {@link FImage} to be the denominator
495         * @return a reference to this {@link FImage}
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         * {@inheritDoc}
515         *
516         * @see org.openimaj.image.Image#divideInplace(java.lang.Object)
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         * Divide all pixels by a given value
532         *
533         * @param fval
534         *            the value
535         * @return this image
536         * @see org.openimaj.image.Image#divideInplace(java.lang.Object)
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         * {@inheritDoc}
549         *
550         * @see org.openimaj.image.Image#divideInplace(org.openimaj.image.Image)
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         * {@inheritDoc}
563         *
564         * @see org.openimaj.image.Image#extractROI(int, int,
565         *      org.openimaj.image.Image)
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         * {@inheritDoc}
586         *
587         * @see org.openimaj.image.Image#extractROI(int, int, int, int)
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         * {@inheritDoc}
610         *
611         * @see org.openimaj.image.SingleBandImage#fill(java.lang.Comparable)
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         * Fill an image with the given colour
625         *
626         * @param colour
627         *            the colour
628         * @return the image
629         * @see org.openimaj.image.SingleBandImage#fill(java.lang.Comparable)
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         * {@inheritDoc}
642         *
643         * @see org.openimaj.image.Image#getContentArea()
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         * Returns the pixels of the image as a vector (array) of doubles.
669         *
670         * @return the pixels of the image as a vector (array) of doubles.
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         * {@inheritDoc}
684         *
685         * @see org.openimaj.image.Image#getField(org.openimaj.image.Image.Field)
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         * {@inheritDoc}
707         *
708         * @see org.openimaj.image.Image#getFieldCopy(org.openimaj.image.Image.Field)
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         * {@inheritDoc}
738         *
739         * @see org.openimaj.image.Image#getFieldInterpolate(org.openimaj.image.Image.Field)
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         * Returns the pixels of the image as a vector (array) of floats.
785         *
786         * @return the pixels of the image as a vector (array) of floats.
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         * {@inheritDoc}
800         *
801         * @see org.openimaj.image.Image#getPixel(int, int)
802         */
803        @Override
804        public Float getPixel(final int x, final int y)
805        {
806                return this.pixels[y][x];
807        }
808
809        /**
810         * {@inheritDoc}
811         *
812         * @see org.openimaj.image.Image#getPixelComparator()
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         * {@inheritDoc}
828         *
829         * @see org.openimaj.image.Image#getPixelInterp(double, double)
830         * @see Interpolation#bilerp(double, double, double, double, double, double)
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         * {@inheritDoc}
874         *
875         * @see org.openimaj.image.Image#getPixelInterp(double, double)
876         * @see Interpolation#bilerp(double, double, double, double, double, double)
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()); // this.pixels[y0][x0];
907                final double f01 = (ty1 && tx0 ? this.pixels[y1][x0] : background.floatValue()); // this.pixels[y1][x0];
908                final double f10 = (ty0 && tx1 ? this.pixels[y0][x1] : background.floatValue()); // this.pixels[y0][x1];
909                final double f11 = (ty1 && tx1 ? this.pixels[y1][x1] : background.floatValue()); // this.pixels[y1][x1];
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         * Interpolate the value of a pixel at the given coordinates
924         *
925         * @param x
926         *            the x-ordinate
927         * @param y
928         *            the y-ordinate
929         * @param background
930         *            the background colour
931         * @return the interpolated pixel value
932         * @see org.openimaj.image.Image#getPixelInterp(double, double)
933         * @see Interpolation#bilerp(double, double, double, double, double, double)
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); // this.pixels[y0][x0];
963                final float f01 = (ty1 && tx0 ? this.pixels[y1][x0] : background); // this.pixels[y1][x0];
964                final float f10 = (ty0 && tx1 ? this.pixels[y0][x1] : background); // this.pixels[y0][x1];
965                final float f11 = (ty1 && tx1 ? this.pixels[y1][x1] : background); // this.pixels[y1][x1];
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         * {@inheritDoc}
980         *
981         * @see org.openimaj.image.Image#internalAssign(org.openimaj.image.Image)
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         * {@inheritDoc}
998         *
999         * @see org.openimaj.image.Image#internalAssign(org.openimaj.image.Image)
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         * {@inheritDoc}
1013         *
1014         * @see org.openimaj.image.Image#internalAssign(int [] data, int width, int
1015         *      height)
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                                // NTSC colour conversion:
1034                                // This improves keypoint detection for some reason!
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         * {@inheritDoc}
1045         *
1046         * @see org.openimaj.image.Image#inverse()
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         * {@inheritDoc}
1063         *
1064         * @see org.openimaj.image.Image#max()
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         * Get the pixel with the maximum value. Returns an {@link FValuePixel}
1082         * which contains the location and value of the pixel. If there are multiple
1083         * pixels with the same value then the first is returned. Note that this
1084         * method assumes all pixel values are greater than 0.
1085         *
1086         * @return the maximum pixel as an {@link FValuePixel}.
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         * {@inheritDoc}
1108         *
1109         * @see org.openimaj.image.Image#min()
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         * Get the pixel with the minimum value. Returns an {@link FValuePixel}
1127         * which contains the location and value of the pixel. If there are multiple
1128         * pixels with the same value then the first is returned. Note that this
1129         * method assumes all pixel values are greater than 0.
1130         *
1131         * @return The minimum pixel as an {@link FValuePixel}.
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         * {@inheritDoc}
1151         *
1152         * @see org.openimaj.image.Image#multiply(java.lang.Object)
1153         */
1154        @Override
1155        public FImage multiply(final Float num)
1156        {
1157                return super.multiply(num);
1158        }
1159
1160        /**
1161         * Multiplies this image's pixel values by the corresponding pixel values in
1162         * the given image side-affecting this image. This is a version of
1163         * {@link Image#multiplyInplace(Image)} which takes an {@link FImage}. This
1164         * directly accesses the underlying float[][] and is therefore fast. This
1165         * function works inplace.
1166         *
1167         * @see Image#multiplyInplace(Image)
1168         * @param im
1169         *            the {@link FImage} to multiply with this image
1170         * @return a reference to this image
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         * {@inheritDoc}
1190         *
1191         * @see org.openimaj.image.Image#multiplyInplace(java.lang.Object)
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         * Multiply all pixel values by the given value
1210         *
1211         * @param fnum
1212         *            the value
1213         * @return this image
1214         * @see org.openimaj.image.Image#multiplyInplace(java.lang.Object)
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         * {@inheritDoc} This method will throw an
1231         * {@link UnsupportedOperationException} if the input input is not an
1232         * {@link FImage}.
1233         *
1234         * @see org.openimaj.image.Image#multiplyInplace(org.openimaj.image.Image)
1235         * @throws UnsupportedOperationException
1236         *             if the given image is not an {@link FImage}
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         * {@inheritDoc}
1249         *
1250         * @return A new {@link FImage}
1251         * @see org.openimaj.image.Image#newInstance(int, int)
1252         */
1253        @Override
1254        public FImage newInstance(final int width, final int height)
1255        {
1256                return new FImage(width, height);
1257        }
1258
1259        /**
1260         * {@inheritDoc}
1261         *
1262         * @see org.openimaj.image.Image#normalise()
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         * {@inheritDoc}
1286         *
1287         * @see org.openimaj.image.SingleBandImage#process(org.openimaj.image.processor.KernelProcessor)
1288         */
1289        @Override
1290        public FImage process(final KernelProcessor<Float, FImage> p) {
1291                return this.process(p, false);
1292        }
1293
1294        /**
1295         * {@inheritDoc} This method has been overridden in {@link FImage} for
1296         * performance.
1297         *
1298         * @see org.openimaj.image.SingleBandImage#process(org.openimaj.image.processor.KernelProcessor,
1299         *      boolean)
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         * {@inheritDoc} This method has been overridden in {@link FImage} for
1332         * performance.
1333         *
1334         * @see org.openimaj.image.Image#processInplace(org.openimaj.image.processor.PixelProcessor)
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         * {@inheritDoc} This method has been overridden in {@link FImage} for
1352         * performance.
1353         *
1354         * @see org.openimaj.image.Image#analyseWith(org.openimaj.image.analyser.PixelAnalyser)
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         * {@inheritDoc}
1372         *
1373         * @see org.openimaj.image.Image#setPixel(int, int, java.lang.Object)
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         * Subtracts the given {@link FImage} from this image returning a new image
1383         * containing the result.
1384         *
1385         * @param im
1386         *            The image to subtract from this image.
1387         * @return A new image containing the result.
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         * {@inheritDoc}
1405         *
1406         * @see org.openimaj.image.Image#subtract(java.lang.Object)
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         * {@inheritDoc} Throws an {@link UnsupportedOperationException} if the
1425         * given image is not an {@link FImage}.
1426         *
1427         * @see org.openimaj.image.Image#subtract(org.openimaj.image.Image)
1428         * @throws UnsupportedOperationException
1429         *             if the given image is not an {@link FImage}.
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         * Subtracts (pixel-by-pixel) the given {@link FImage} from this image.
1442         * Side-affects this image.
1443         *
1444         * @param im
1445         *            The {@link FImage} to subtract from this image.
1446         * @return A reference to this image containing the result.
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         * {@inheritDoc}
1468         *
1469         * @see org.openimaj.image.Image#subtractInplace(java.lang.Object)
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         * {@inheritDoc}
1488         *
1489         * @see org.openimaj.image.Image#subtractInplace(org.openimaj.image.Image)
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         * {@inheritDoc}
1502         *
1503         * @see org.openimaj.image.Image#threshold(java.lang.Object)
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         * {@inheritDoc}
1524         *
1525         * @see org.openimaj.image.Image#toByteImage()
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         * {@inheritDoc}
1548         *
1549         * @see org.openimaj.image.Image#toPackedARGBPixels()
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         * {@inheritDoc}
1570         *
1571         * @see java.lang.Object#toString()
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         * Returns a string representation of every pixel in this image using the
1600         * format string (see {@link String#format(String, Object...)}) to format
1601         * each pixel value.
1602         *
1603         * @param format
1604         *            The format string to use for each pixel output
1605         * @return A string representation of the image
1606         * @see String#format(String, Object...)
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         * {@inheritDoc}
1621         *
1622         * @see org.openimaj.image.Image#transform(Jama.Matrix)
1623         */
1624        @Override
1625        public FImage transform(final Matrix transform) {
1626                return super.transform(transform);
1627        }
1628
1629        /**
1630         * {@inheritDoc}
1631         *
1632         * @see org.openimaj.image.Image#zero()
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         * Compare this image against another using a threshold on the absolute
1657         * difference between pixel values in order to determine equality.
1658         *
1659         * @param o
1660         *            the image to compare against
1661         * @param thresh
1662         *            the threshold for determining equality
1663         * @return true images are the same size and if all pixel values have a
1664         *         difference less than threshold; false otherwise.
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         * Get the value of the pixel at coordinate p
1682         *
1683         * @param p
1684         *            The coordinate to get
1685         *
1686         * @return The pixel value at (x, y)
1687         */
1688        public float getPixelNative(final Pixel p) {
1689                return this.getPixelNative(p.x, p.y);
1690        }
1691
1692        /**
1693         * Get the value of the pixel at coordinate <code>(x, y)</code>.
1694         *
1695         * @param x
1696         *            The x-coordinate to get
1697         * @param y
1698         *            The y-coordinate to get
1699         *
1700         * @return The pixel value at (x, y)
1701         */
1702        public float getPixelNative(final int x, final int y) {
1703                return this.pixels[y][x];
1704        }
1705
1706        /**
1707         * Returns the pixels in this image as a vector (an array of the pixel
1708         * type).
1709         *
1710         * @param f
1711         *            The array into which to place the data
1712         * @return The pixels in the image as a vector (a reference to the given
1713         *         array).
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         * Sets the pixel at <code>(x,y)</code> to the given value. Side-affects
1726         * this image.
1727         *
1728         * @param x
1729         *            The x-coordinate of the pixel to set
1730         * @param y
1731         *            The y-coordinate of the pixel to set
1732         * @param val
1733         *            The value to set the pixel to.
1734         */
1735        public void setPixelNative(final int x, final int y, final float val) {
1736                this.pixels[y][x] = val;
1737        }
1738
1739        /**
1740         * Convenience method to initialise an array of FImages
1741         *
1742         * @param num
1743         *            array length
1744         * @param width
1745         *            width of images
1746         * @param height
1747         *            height of images
1748         * @return array of newly initialised images
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         * @return The sum of all the pixels in the image
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         * Convert this {@link FImage} to an RGB {@link MBFImage}.
1775         *
1776         * @return a new RGB colour image.
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         * Overlay the given image on this image with the given alpha channel at the
1819         * given location.
1820         *
1821         * @param img
1822         *            The image to overlay
1823         * @param alpha
1824         *            The alpha channel to use
1825         * @param x
1826         *            The location to draw the image
1827         * @param y
1828         *            The location to draw the image
1829         * @return This image with the overlay on it
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         * {@inheritDoc}
1853         * <p>
1854         * This method will overlay the given image at the given location with full
1855         * opacity.
1856         *
1857         * @see org.openimaj.image.Image#overlayInplace(org.openimaj.image.Image,
1858         *      int, int)
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         * Create a random image of the given size.
1868         *
1869         * @param width
1870         *            the width
1871         * @param height
1872         *            the height
1873         * @return the image
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         * Replace pixels of a certain colour with another colour. Side-affects this
1892         * image.
1893         *
1894         * @param target
1895         *            the colour to fill the image with
1896         * @param replacement
1897         *            the colour to fill the image with
1898         * @return A reference to this image.
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}