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