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.io.Serializable;
033import java.text.AttributedString;
034import java.util.Comparator;
035import java.util.List;
036
037import org.openimaj.image.analyser.ImageAnalyser;
038import org.openimaj.image.analyser.PixelAnalyser;
039import org.openimaj.image.combiner.AccumulatingImageCombiner;
040import org.openimaj.image.combiner.ImageCombiner;
041import org.openimaj.image.pixel.Pixel;
042import org.openimaj.image.processor.GridProcessor;
043import org.openimaj.image.processor.ImageProcessor;
044import org.openimaj.image.processor.KernelProcessor;
045import org.openimaj.image.processor.PixelProcessor;
046import org.openimaj.image.processor.Processor;
047import org.openimaj.image.renderer.ImageRenderer;
048import org.openimaj.image.renderer.RenderHints;
049import org.openimaj.image.typography.Font;
050import org.openimaj.image.typography.FontStyle;
051import org.openimaj.math.geometry.path.Path2d;
052import org.openimaj.math.geometry.point.Point2d;
053import org.openimaj.math.geometry.shape.Polygon;
054import org.openimaj.math.geometry.shape.Rectangle;
055import org.openimaj.math.geometry.shape.Shape;
056
057import Jama.Matrix;
058
059/**
060 * Base class for representing and manipulating images. Images are typed by the
061 * type of pixel at each coordinate and the concrete subclass type.
062 *
063 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
064 *
065 * @param <Q>
066 *            the pixel type
067 * @param <I>
068 *            the actual image of the concrete subclass
069 */
070public abstract class Image<Q, I extends Image<Q, I>> implements Cloneable, Serializable, ImageProvider<I> {
071        /**
072         * Enumerator for representing the type of field interlacing operations.
073         *
074         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
075         */
076        public enum Field {
077                /**
078                 * Odd field
079                 */
080                ODD,
081                /**
082                 * Even field
083                 */
084                EVEN
085        }
086
087        private static final long serialVersionUID = 1L;
088
089        /**
090         * Accumulate this image the the given {@link AccumulatingImageCombiner}.
091         *
092         * @param combiner
093         *            the combiner
094         * @see AccumulatingImageCombiner#accumulate(Image)
095         */
096        @SuppressWarnings("unchecked")
097        public void accumulateWith(AccumulatingImageCombiner<I, ?> combiner) {
098                combiner.accumulate((I) this);
099        }
100
101        /**
102         * Set all pixels to their absolute values, so that all pixel values in the
103         * image will be greater than zero.
104         *
105         * @return The image with absolute values
106         */
107        public abstract I abs();
108
109        /**
110         * Adds the given image to this image and return new image.
111         *
112         * @param im
113         *            The image to add
114         * @return A new image that is the sum of this image and the given image.
115         */
116        public I add(Image<?, ?> im) {
117                final I newImage = this.clone();
118                newImage.addInplace(im);
119                return newImage;
120        }
121
122        /**
123         * Add a value to each pixel and return new image.
124         *
125         * @param num
126         *            The value to add to each pixel
127         * @return A new image that is the sum of this image and the given value.
128         */
129        public I add(Q num) {
130                final I newImage = this.clone();
131                newImage.addInplace(num);
132                return newImage;
133        }
134
135        /**
136         * Add the given image to this image (side-affects this image).
137         *
138         * @param im
139         *            The image to add to this image
140         * @return A reference to this image.
141         */
142        public abstract I addInplace(Image<?, ?> im);
143
144        /**
145         * Add a scalar to each pixel in this image (side-affects this image).
146         *
147         * @param num
148         *            The value to add to every pixel in this image.
149         * @return A reference to this image.
150         */
151        public abstract I addInplace(Q num);
152
153        /**
154         * Analyse this image with an {@link ImageAnalyser}.
155         *
156         * @param analyser
157         *            The analyser to analyse with.
158         * @see ImageAnalyser#analyseImage(Image)
159         */
160        @SuppressWarnings("unchecked")
161        public void analyseWith(ImageAnalyser<I> analyser) {
162                analyser.analyseImage((I) this);
163        }
164
165        /**
166         * Analyse this image with a {@link PixelAnalyser}.
167         *
168         * @param analyser
169         *            The analyser to analyse with.
170         * @see PixelAnalyser#analysePixel(Object)
171         */
172        public void analyseWith(PixelAnalyser<Q> analyser) {
173                analyser.reset();
174
175                for (int y = 0; y < getHeight(); y++) {
176                        for (int x = 0; x < getWidth(); x++) {
177                                analyser.analysePixel(getPixel(x, y));
178                        }
179                }
180        }
181
182        /**
183         * Analyse this image with the given {@link PixelAnalyser}, only analysing
184         * those pixels where the mask is non-zero.
185         *
186         * @param mask
187         *            The mask to apply to the analyser.
188         * @param analyser
189         *            The {@link PixelProcessor} to apply.
190         *
191         * @see PixelAnalyser#analysePixel(Object)
192         */
193        public void analyseWithMasked(FImage mask, PixelAnalyser<Q> analyser) {
194                analyser.reset();
195
196                for (int y = 0; y < getHeight(); y++) {
197                        for (int x = 0; x < getWidth(); x++) {
198                                if (mask.pixels[y][x] == 0)
199                                        continue;
200                                analyser.analysePixel(getPixel(x, y));
201                        }
202                }
203        }
204
205        /**
206         * Sets any pixels that are below <code>min</code> to zero or above
207         * <code>max</code> to the highest normal value that the image allows
208         * (usually 1 for floating-point images). This method may side-affect this
209         * image.
210         *
211         * @param min
212         *            The minimum value
213         * @param max
214         *            The maximum value
215         * @return The clipped image.
216         */
217        public abstract I clip(Q min, Q max);
218
219        /**
220         * Set all values greater than the given value to the highest normal value
221         * that the image allows (usually 1 for floating-point images). This method
222         * may side-affect this image.
223         *
224         * @param thresh
225         *            The value over which pixels are clipped to zero.
226         * @return The clipped image.
227         */
228        public abstract I clipMax(Q thresh);
229
230        /**
231         * Set all values less than the given value to zero. This method may
232         * side-affect this image.
233         *
234         * @param thresh
235         *            The value below which pixels are clipped to zero.
236         * @return The clipped image.
237         */
238        public abstract I clipMin(Q thresh);
239
240        /**
241         * Deep copy of an image (internal image buffers copied).
242         *
243         * @return A copy of this image.
244         */
245        @Override
246        public abstract I clone();
247
248        /**
249         * Create a {@link ImageRenderer} capable of drawing into this image.
250         *
251         * @return the renderer
252         */
253        public abstract ImageRenderer<Q, I> createRenderer();
254
255        /**
256         * Create a {@link ImageRenderer} capable of drawing into this image.
257         *
258         * @param options
259         *            Options for the renderer
260         * @return the renderer
261         */
262        public abstract ImageRenderer<Q, I> createRenderer(RenderHints options);
263
264        /**
265         * Combine this image with another using an {@link ImageCombiner}.
266         *
267         * @param <OUT>
268         *            The output {@link Image} type.
269         * @param <OTHER>
270         *            The type of the other {@link Image} being combined.
271         * @param combiner
272         *            The combiner.
273         * @param other
274         *            The image to combine with this
275         * @return The combined output.
276         */
277        @SuppressWarnings("unchecked")
278        public <OUT extends Image<?, OUT>, OTHER extends Image<?, OTHER>> OUT combineWith(
279                        ImageCombiner<I, OTHER, OUT> combiner, OTHER other)
280        {
281                return combiner.combine((I) this, other);
282        }
283
284        /**
285         * Get the default foreground colour.
286         *
287         * <p>
288         * This is a convenience method that calls {@link #createRenderer()} to get
289         * the default renderer to do the actual drawing. Create the renderer
290         * yourself and use it to draw if you need more control.
291         * </p>
292         *
293         * @return the default foreground colour.
294         */
295        public Q defaultBackgroundColour() {
296                return createRenderer().defaultBackgroundColour();
297        }
298
299        /**
300         * Get the default foreground colour.
301         *
302         * <p>
303         * This is a convenience method that calls {@link #createRenderer()} to get
304         * the default renderer to do the actual drawing. Create the renderer
305         * yourself and use it to draw if you need more control.
306         * </p>
307         *
308         * @return the default foreground colour.
309         */
310        public Q defaultForegroundColour() {
311                return createRenderer().defaultForegroundColour();
312        }
313
314        /**
315         * Divide each pixel of the image by corresponding pixel in the given image.
316         * This method should return a new image.
317         *
318         * @param im
319         *            image The image to divide this image by.
320         * @return A new image containing the result.
321         */
322        public I divide(Image<?, ?> im) {
323                final I newImage = this.clone();
324                newImage.divideInplace(im);
325                return newImage;
326        }
327
328        /**
329         * Divide each pixel of the image by the given scalar value. This method
330         * should return a new image.
331         *
332         * @param val
333         *            The value to divide the pixels in this image by.
334         * @return A new image containing the result.
335         */
336        public I divide(Q val) {
337                final I newImage = this.clone();
338                newImage.divideInplace(val);
339                return newImage;
340        }
341
342        /**
343         * Divide each pixel in this image by the corresponding pixel value in the
344         * given image. This method should side-affect this image.
345         *
346         * @param im
347         *            image The image to divide this image by.
348         * @return A reference to this image containing the result.
349         */
350        public abstract I divideInplace(Image<?, ?> im);
351
352        /**
353         * Divide each pixel of the image by the given scalar value. This method
354         * should side-affect this image.
355         *
356         * @param val
357         *            The value to divide each pixel by.
358         * @return A reference to this image containing the result.
359         */
360        public abstract I divideInplace(Q val);
361
362        /**
363         * Draw onto this image lines drawn with the given colour between the points
364         * given. No points are drawn. Side-affects this image.
365         *
366         * <p>
367         * This is a convenience method that calls {@link #createRenderer()} to get
368         * the default renderer to do the actual drawing. Create the renderer
369         * yourself and use it to draw if you need more control.
370         * </p>
371         *
372         * @param pts
373         *            The point list to draw onto this image.
374         * @param col
375         *            The colour to draw the lines
376         */
377        public void drawConnectedPoints(List<? extends Point2d> pts, Q col) {
378                createRenderer().drawConnectedPoints(pts, col);
379        }
380
381        /**
382         * Draw a cubic Bezier curve into the image with 100 point accuracy.
383         *
384         * <p>
385         * This is a convenience method that calls {@link #createRenderer()} to get
386         * the default renderer to do the actual drawing. Create the renderer
387         * yourself and use it to draw if you need more control.
388         * </p>
389         *
390         * @param p1
391         *            One end point of the line
392         * @param p2
393         *            The other end point of the line
394         * @param c1
395         *            The control point associated with p1
396         * @param c2
397         *            The control point associated with p2
398         * @param thickness
399         *            The thickness to draw the line
400         * @param col
401         *            The colour to draw the line
402         * @return The points along the bezier curve
403         */
404        public Point2d[] drawCubicBezier(Point2d p1, Point2d p2,
405                        Point2d c1, Point2d c2, int thickness, Q col)
406        {
407                return createRenderer().drawCubicBezier(p1, p2, c1, c2, thickness, col);
408        }
409
410        /**
411         * Draw into this image the provided image at the given coordinates. Parts
412         * of the image outside the bounds of this image will be ignored.
413         * Side-affects this image.
414         *
415         * <p>
416         * This is a convenience method that calls {@link #createRenderer()} to get
417         * the default renderer to do the actual drawing. Create the renderer
418         * yourself and use it to draw if you need more control.
419         * </p>
420         *
421         * @param image
422         *            The image to draw.
423         * @param x
424         *            The x-ordinate of the top-left of the image
425         * @param y
426         *            The y-ordinate of the top-left of the image
427         */
428        public void drawImage(I image, int x, int y) {
429                createRenderer().drawImage(image, x, y);
430        }
431
432        /**
433         * Draw into this image the provided image at the given coordinates. Parts
434         * of the image outside the bounds of this image will be ignored.
435         * Side-affects this image.
436         *
437         * <p>
438         * This is a convenience method that calls {@link #createRenderer()} to get
439         * the default renderer to do the actual drawing. Create the renderer
440         * yourself and use it to draw if you need more control.
441         * </p>
442         *
443         * @param image
444         *            The image to draw.
445         * @param pt
446         *            the coordinate at which to draw
447         */
448        public void drawImage(I image, Point2d pt) {
449                createRenderer().drawImage(image, (int) pt.getX(), (int) pt.getY());
450        }
451
452        /**
453         * Draw into this image the provided image at the given coordinates ignoring
454         * certain pixels. Parts of the image outside the bounds of this image will
455         * be ignored. Side-affects this image. Pixels in the ignore list will be
456         * stripped from the image to draw.
457         *
458         * <p>
459         * This is a convenience method that calls {@link #createRenderer()} to get
460         * the default renderer to do the actual drawing. Create the renderer
461         * yourself and use it to draw if you need more control.
462         * </p>
463         *
464         * @param image
465         *            The image to draw.
466         * @param x
467         *            The x-ordinate of the top-left of the image
468         * @param y
469         *            The y-ordinate of the top-left of the image
470         * @param ignoreList
471         *            The list of pixels to ignore when copying the image
472         */
473        public void drawImage(I image, int x, int y, @SuppressWarnings("unchecked") Q... ignoreList) {
474                createRenderer().drawImage(image, x, y, ignoreList);
475        }
476
477        /**
478         * Draw a line from the coordinates specified by <code>(x1,y1)</code> at an
479         * angle of <code>theta</code> with the given length, thickness and colour.
480         * Side-affects this image.
481         *
482         * <p>
483         * This is a convenience method that calls {@link #createRenderer()} to get
484         * the default renderer to do the actual drawing. Create the renderer
485         * yourself and use it to draw if you need more control.
486         * </p>
487         *
488         * @param x1
489         *            The x-ordinate to start the line.
490         * @param y1
491         *            The y-ordinate to start the line.
492         * @param theta
493         *            The angle at which to draw the line.
494         * @param length
495         *            The length to draw the line.
496         * @param thickness
497         *            The thickness to draw the line.
498         * @param col
499         *            The colour to draw the line.
500         */
501        public void drawLine(int x1, int y1, double theta, int length, int thickness, Q col) {
502                createRenderer().drawLine(x1, y1, theta, length, thickness, col);
503        }
504
505        /**
506         * Draw a line from the coordinates specified by <code>(x1,y1)</code> at an
507         * angle of <code>theta</code> with the given length and colour.
508         * Line-thickness will be 1. Side-affects this image.
509         *
510         * <p>
511         * This is a convenience method that calls {@link #createRenderer()} to get
512         * the default renderer to do the actual drawing. Create the renderer
513         * yourself and use it to draw if you need more control.
514         * </p>
515         *
516         * @param x1
517         *            The x-ordinate to start the line.
518         * @param y1
519         *            The y-ordinate to start the line.
520         * @param theta
521         *            The angle at which to draw the line.
522         * @param length
523         *            The length to draw the line.
524         * @param col
525         *            The colour to draw the line.
526         */
527        public void drawLine(int x1, int y1, double theta, int length, Q col) {
528                createRenderer().drawLine(x1, y1, theta, length, 1, col);
529        }
530
531        /**
532         * Draw a line from the coordinates specified by <code>(x0,y0)</code> to the
533         * coordinates specified by <code>(x1,y1)</code> using the given color and
534         * thickness. Side-affects this image.
535         *
536         * <p>
537         * This is a convenience method that calls {@link #createRenderer()} to get
538         * the default renderer to do the actual drawing. Create the renderer
539         * yourself and use it to draw if you need more control.
540         * </p>
541         *
542         * @param x0
543         *            The x-ordinate at the start of the line.
544         * @param y0
545         *            The y-ordinate at the start of the line.
546         * @param x1
547         *            The x-ordinate at the end of the line.
548         * @param y1
549         *            The y-ordinate at the end of the line.
550         * @param thickness
551         *            The thickness which to draw the line.
552         * @param col
553         *            The colour in which to draw the line.
554         */
555        public void drawLine(int x0, int y0, int x1, int y1, int thickness, Q col) {
556                createRenderer().drawLine(x0, y0, x1, y1, thickness, col);
557        }
558
559        /**
560         * Draw a line from the coordinates specified by <code>(x0,y0)</code> to
561         * <code>(x1,y1)</code> using the given colour. The line thickness will be 1
562         * pixel. Side-affects this image.
563         *
564         * <p>
565         * This is a convenience method that calls {@link #createRenderer()} to get
566         * the default renderer to do the actual drawing. Create the renderer
567         * yourself and use it to draw if you need more control.
568         * </p>
569         *
570         * @param x0
571         *            The x-ordinate at the start of the line.
572         * @param y0
573         *            The y-ordinate at the start of the line.
574         * @param x1
575         *            The x-ordinate at the end of the line.
576         * @param y1
577         *            The y-ordinate at the end of the line.
578         * @param col
579         *            The colour in which to draw the line.
580         */
581        public void drawLine(int x0, int y0, int x1, int y1, Q col) {
582                createRenderer().drawLine(x0, y0, x1, y1, 1, col);
583        }
584
585        /**
586         * Draw a line from the coordinates specified using the given colour. The
587         * line thickness will be 1 pixel. Side-affects this image.
588         *
589         * <p>
590         * This is a convenience method that calls {@link #createRenderer()} to get
591         * the default renderer to do the actual drawing. Create the renderer
592         * yourself and use it to draw if you need more control.
593         * </p>
594         *
595         * @param p1
596         *            The coordinate of the start of the line.
597         * @param p2
598         *            The coordinate of the end of the line.
599         * @param col
600         *            The colour in which to draw the line.
601         */
602        public void drawLine(Point2d p1, Point2d p2, Q col) {
603                createRenderer().drawLine(p1, p2, col);
604        }
605
606        /**
607         * Draw a line from the coordinates specified using the given colour and
608         * thickness. Side-affects this image.
609         *
610         * <p>
611         * This is a convenience method that calls {@link #createRenderer()} to get
612         * the default renderer to do the actual drawing. Create the renderer
613         * yourself and use it to draw if you need more control.
614         * </p>
615         *
616         * @param p1
617         *            The coordinate of the start of the line.
618         * @param p2
619         *            The coordinate of the end of the line.
620         * @param thickness
621         *            the stroke width
622         * @param col
623         *            The colour in which to draw the line.
624         */
625        public void drawLine(Point2d p1, Point2d p2, int thickness, Q col) {
626                createRenderer().drawLine(p1, p2, thickness, col);
627        }
628
629        /**
630         * Draw a line from the specified Path2d object
631         *
632         * <p>
633         * This is a convenience method that calls {@link #createRenderer()} to get
634         * the default renderer to do the actual drawing. Create the renderer
635         * yourself and use it to draw if you need more control.
636         * </p>
637         *
638         * @param line
639         *            the line
640         * @param thickness
641         *            the stroke width
642         * @param col
643         *            The colour in which to draw the line.
644         */
645        public void drawLine(Path2d line, int thickness, Q col) {
646                createRenderer().drawLine(line, thickness, col);
647        }
648
649        /**
650         * Draw a line from the specified Path2d object
651         *
652         * <p>
653         * This is a convenience method that calls {@link #createRenderer()} to get
654         * the default renderer to do the actual drawing. Create the renderer
655         * yourself and use it to draw if you need more control.
656         * </p>
657         *
658         * @param line
659         *            the line
660         * @param thickness
661         *            the stroke width
662         * @param col
663         *            The colour in which to draw the line.
664         */
665        public void drawPath(Path2d line, int thickness, Q col) {
666                createRenderer().drawPath(line, thickness, col);
667        }
668
669        /**
670         * Draw the given list of lines using {@link #drawLine(Path2d, int, Object)}
671         * with the given colour and thickness. Side-affects this image.
672         *
673         * <p>
674         * This is a convenience method that calls {@link #createRenderer()} to get
675         * the default renderer to do the actual drawing. Create the renderer
676         * yourself and use it to draw if you need more control.
677         * </p>
678         *
679         * @param lines
680         *            The list of lines to draw.
681         * @param thickness
682         *            the stroke width
683         * @param col
684         *            The colour to draw each point.
685         */
686        public void drawLines(Iterable<? extends Path2d> lines, int thickness, Q col) {
687                createRenderer().drawLines(lines, thickness, col);
688        }
689
690        /**
691         * Draw the given list of paths using {@link #drawLine(Path2d, int, Object)}
692         * with the given colour and thickness. Side-affects this image.
693         *
694         * <p>
695         * This is a convenience method that calls {@link #createRenderer()} to get
696         * the default renderer to do the actual drawing. Create the renderer
697         * yourself and use it to draw if you need more control.
698         * </p>
699         *
700         * @param lines
701         *            The list of lines to draw.
702         * @param thickness
703         *            the stroke width
704         * @param col
705         *            The colour to draw each point.
706         */
707        public void drawPaths(Iterable<? extends Path2d> lines, int thickness, Q col) {
708                createRenderer().drawPaths(lines, thickness, col);
709        }
710
711        /**
712         * Draw a dot centered on the given location (rounded to nearest integer
713         * location) at the given size and with the given color. Side-affects this
714         * image.
715         *
716         * <p>
717         * This is a convenience method that calls {@link #createRenderer()} to get
718         * the default renderer to do the actual drawing. Create the renderer
719         * yourself and use it to draw if you need more control.
720         * </p>
721         *
722         * @param p
723         *            The coordinates at which to draw the point
724         * @param col
725         *            The colour to draw the point
726         * @param size
727         *            The size at which to draw the point.
728         */
729        public void drawPoint(Point2d p, Q col, int size) {
730                createRenderer().drawPoint(p, col, size);
731        }
732
733        /**
734         * Draw the given list of points using
735         * {@link #drawPoint(Point2d, Object, int)} with the given colour and size.
736         * Side-affects this image.
737         *
738         * <p>
739         * This is a convenience method that calls {@link #createRenderer()} to get
740         * the default renderer to do the actual drawing. Create the renderer
741         * yourself and use it to draw if you need more control.
742         * </p>
743         *
744         * @param pts
745         *            The list of points to draw.
746         * @param col
747         *            The colour to draw each point.
748         * @param size
749         *            The size to draw each point.
750         */
751        public void drawPoints(Iterable<? extends Point2d> pts, Q col, int size) {
752                createRenderer().drawPoints(pts, col, size);
753        }
754
755        /**
756         * Draw the given polygon in the specified colour with the given thickness
757         * lines. Side-affects this image.
758         *
759         * <p>
760         * This is a convenience method that calls {@link #createRenderer()} to get
761         * the default renderer to do the actual drawing. Create the renderer
762         * yourself and use it to draw if you need more control.
763         * </p>
764         *
765         * @param p
766         *            The polygon to draw.
767         * @param thickness
768         *            The thickness of the lines to use
769         * @param col
770         *            The colour to draw the lines in
771         */
772        public void drawPolygon(Polygon p, int thickness, Q col) {
773                createRenderer().drawPolygon(p, thickness, col);
774        }
775
776        /**
777         * Draw the given polygon in the specified colour. Uses
778         * {@link #drawPolygon(Polygon, int, Object)} with line thickness 1.
779         * Side-affects this image.
780         *
781         * <p>
782         * This is a convenience method that calls {@link #createRenderer()} to get
783         * the default renderer to do the actual drawing. Create the renderer
784         * yourself and use it to draw if you need more control.
785         * </p>
786         *
787         * @param p
788         *            The polygon to draw.
789         * @param col
790         *            The colour to draw the polygon in.
791         */
792        public void drawPolygon(Polygon p, Q col) {
793                createRenderer().drawPolygon(p, col);
794        }
795
796        /**
797         * Draw the given polygon, filled with the specified colour. Side-affects
798         * this image.
799         *
800         * <p>
801         * This is a convenience method that calls {@link #createRenderer()} to get
802         * the default renderer to do the actual drawing. Create the renderer
803         * yourself and use it to draw if you need more control.
804         * </p>
805         *
806         * @param p
807         *            The polygon to draw.
808         * @param col
809         *            The colour to fill the polygon with.
810         */
811        public void drawPolygonFilled(Polygon p, Q col) {
812                createRenderer().drawPolygonFilled(p, col);
813        }
814
815        /**
816         * Draw the given shape in the specified colour with the given thickness
817         * lines. Side-affects this image.
818         *
819         * <p>
820         * This is a convenience method that calls {@link #createRenderer()} to get
821         * the default renderer to do the actual drawing. Create the renderer
822         * yourself and use it to draw if you need more control.
823         * </p>
824         *
825         * @param s
826         *            The shape to draw.
827         * @param thickness
828         *            The thickness of the lines to use
829         * @param col
830         *            The colour to draw the lines in
831         */
832        public void drawShape(Shape s, int thickness, Q col) {
833                createRenderer().drawShape(s, thickness, col);
834        }
835
836        /**
837         * Draw the given shape in the specified colour. Uses
838         * {@link #drawPolygon(Polygon, int, Object)} with line thickness 1.
839         * Side-affects this image.
840         *
841         * <p>
842         * This is a convenience method that calls {@link #createRenderer()} to get
843         * the default renderer to do the actual drawing. Create the renderer
844         * yourself and use it to draw if you need more control.
845         * </p>
846         *
847         * @param p
848         *            The shape to draw.
849         * @param col
850         *            The colour to draw the polygon in.
851         */
852        public void drawShape(Shape p, Q col) {
853                createRenderer().drawShape(p, col);
854        }
855
856        /**
857         * Draw the given shape, filled with the specified colour. Side-affects this
858         * image.
859         *
860         * <p>
861         * This is a convenience method that calls {@link #createRenderer()} to get
862         * the default renderer to do the actual drawing. Create the renderer
863         * yourself and use it to draw if you need more control.
864         * </p>
865         *
866         * @param s
867         *            The shape to draw.
868         * @param col
869         *            The colour to fill the polygon with.
870         */
871        public void drawShapeFilled(Shape s, Q col) {
872                createRenderer().drawShapeFilled(s, col);
873        }
874
875        /**
876         * Render the text using its attributes.
877         *
878         * <p>
879         * This is a convenience method that calls {@link #createRenderer()} to get
880         * the default renderer to do the actual drawing. Create the renderer
881         * yourself and use it to draw if you need more control.
882         * </p>
883         *
884         * @param text
885         *            the text
886         * @param x
887         *            the x-ordinate
888         * @param y
889         *            the y-ordinate
890         */
891        public void drawText(AttributedString text, int x, int y) {
892                createRenderer().drawText(text, x, y);
893        }
894
895        /**
896         * Render the text using its attributes.
897         *
898         * <p>
899         * This is a convenience method that calls {@link #createRenderer()} to get
900         * the default renderer to do the actual drawing. Create the renderer
901         * yourself and use it to draw if you need more control.
902         * </p>
903         *
904         * @param text
905         *            the text
906         * @param pt
907         *            the coordinate to render at
908         */
909        public void drawText(AttributedString text, Point2d pt) {
910                createRenderer().drawText(text, pt);
911        }
912
913        /**
914         * Render the text in the given font with the default style.
915         *
916         * <p>
917         * This is a convenience method that calls {@link #createRenderer()} to get
918         * the default renderer to do the actual drawing. Create the renderer
919         * yourself and use it to draw if you need more control.
920         * </p>
921         *
922         * @param <F>
923         *            the font
924         * @param text
925         *            the text
926         * @param x
927         *            the x-ordinate
928         * @param y
929         *            the y-ordinate
930         * @param f
931         *            the font
932         * @param sz
933         *            the size
934         */
935        public <F extends Font<F>> void drawText(String text, int x, int y, F f, int sz) {
936                createRenderer().drawText(text, x, y, f, sz);
937        }
938
939        /**
940         * Render the text in the given font in the given colour with the default
941         * style.
942         *
943         * <p>
944         * This is a convenience method that calls {@link #createRenderer()} to get
945         * the default renderer to do the actual drawing. Create the renderer
946         * yourself and use it to draw if you need more control.
947         * </p>
948         *
949         * @param <F>
950         *            the font
951         * @param text
952         *            the text
953         * @param x
954         *            the x-ordinate
955         * @param y
956         *            the y-ordinate
957         * @param f
958         *            the font
959         * @param sz
960         *            the size
961         * @param col
962         *            the font color
963         */
964        public <F extends Font<F>> void drawText(String text, int x, int y, F f, int sz, Q col) {
965                createRenderer().drawText(text, x, y, f, sz, col);
966        }
967
968        /**
969         * Render the text with the given {@link FontStyle}.
970         *
971         * <p>
972         * This is a convenience method that calls {@link #createRenderer()} to get
973         * the default renderer to do the actual drawing. Create the renderer
974         * yourself and use it to draw if you need more control.
975         * </p>
976         *
977         * @param text
978         *            the text
979         * @param x
980         *            the x-ordinate
981         * @param y
982         *            the y-ordinate
983         * @param f
984         *            the font style
985         */
986        public void drawText(String text, int x, int y, FontStyle<Q> f) {
987                createRenderer().drawText(text, x, y, f);
988        }
989
990        /**
991         * Render the text in the given font with the default style.
992         *
993         * <p>
994         * This is a convenience method that calls {@link #createRenderer()} to get
995         * the default renderer to do the actual drawing. Create the renderer
996         * yourself and use it to draw if you need more control.
997         * </p>
998         *
999         * @param <F>
1000         *            the font
1001         * @param text
1002         *            the text
1003         * @param pt
1004         *            the coordinate to render at
1005         * @param f
1006         *            the font
1007         * @param sz
1008         *            the size
1009         */
1010        public <F extends Font<F>> void drawText(String text, Point2d pt, F f, int sz) {
1011                createRenderer().drawText(text, pt, f, sz);
1012        }
1013
1014        /**
1015         * Render the text in the given font in the given colour with the default
1016         * style.
1017         *
1018         * <p>
1019         * This is a convenience method that calls {@link #createRenderer()} to get
1020         * the default renderer to do the actual drawing. Create the renderer
1021         * yourself and use it to draw if you need more control.
1022         * </p>
1023         *
1024         * @param <F>
1025         *            the font
1026         * @param text
1027         *            the text
1028         * @param pt
1029         *            the coordinate to render at
1030         * @param f
1031         *            the font
1032         * @param sz
1033         *            the size
1034         * @param col
1035         *            the font colour
1036         */
1037        public <F extends Font<F>> void drawText(String text, Point2d pt, F f, int sz, Q col) {
1038                createRenderer().drawText(text, pt, f, sz, col);
1039        }
1040
1041        /**
1042         * Render the text with the given {@link FontStyle}.
1043         *
1044         * <p>
1045         * This is a convenience method that calls {@link #createRenderer()} to get
1046         * the default renderer to do the actual drawing. Create the renderer
1047         * yourself and use it to draw if you need more control.
1048         * </p>
1049         *
1050         * @param text
1051         *            the text
1052         * @param pt
1053         *            the coordinate to render at
1054         * @param f
1055         *            the font style
1056         */
1057        public void drawText(String text, Point2d pt, FontStyle<Q> f) {
1058                createRenderer().drawText(text, pt, f);
1059        }
1060
1061        /**
1062         * Extract a rectangular region about the centre of the image with the given
1063         * width and height. The method will return a box that extends
1064         * <code>width/2</code> and <code>height/2</code> from the centre point so
1065         * that the centre point of the extracted box is also the centre point of
1066         * the image.
1067         *
1068         * @param w
1069         *            The width of the box to extract
1070         * @param h
1071         *            The height of the box to extract
1072         * @return A new image centred around the centre of the image.
1073         */
1074        public I extractCenter(int w, int h) {
1075                final int sw = (int) Math.floor((this.getWidth() - w) / 2);
1076                final int sh = (int) Math.floor((this.getHeight() - h) / 2);
1077
1078                return this.extractROI(sw, sh, w, h);
1079        }
1080
1081        /**
1082         * Extract a rectangular region centred on a given point. The method will
1083         * return a box that extends <code>width/2</code> and <code>height/2</code>
1084         * from the given point <code>(x,y)</code> such that the centre point of the
1085         * extracted box is the same as the point <code>(x,y)</code> in this image.
1086         *
1087         * @param x
1088         *            Center point of the rectangle to extract
1089         * @param y
1090         *            center point of the rectangle to extract
1091         * @param w
1092         *            The width of the rectangle to extract
1093         * @param h
1094         *            The height of the rectangle to extract
1095         * @return A new image centred around the centre of the image.
1096         */
1097        public I extractCenter(int x, int y, int w, int h) {
1098                final int sw = (int) Math.floor(x - (w / 2));
1099                final int sh = (int) Math.floor(y - (h / 2));
1100
1101                return this.extractROI(sw, sh, w, h);
1102        }
1103
1104        /**
1105         * Extract a rectangular region of interest from this image and put it in
1106         * the given image. Coordinate <code>(0,0)</code> is the top-left corner.
1107         * The width and height of the extracted image should be determined from the
1108         * given image's width and height.
1109         *
1110         * @param x
1111         *            The leftmost coordinate of the rectangle to extract
1112         * @param y
1113         *            The topmost coordinate of the rectangle to extract
1114         * @param img
1115         *            The destination image
1116         * @return A reference to the destination image containing the result
1117         */
1118        public abstract I extractROI(int x, int y, I img);
1119
1120        /**
1121         * Extract a rectangular region of interest of the given width and height.
1122         * Coordinate <code>(0,0)</code> is the top-left corner. Returns a new
1123         * image.
1124         *
1125         * @param x
1126         *            The leftmost coordinate of the rectangle to extract
1127         * @param y
1128         *            The topmost coordinate of the rectangle to extract
1129         * @param w
1130         *            The width of the rectangle to extract
1131         * @param h
1132         *            The height of the rectangle to extract
1133         * @return A new image representing the selected region
1134         */
1135        public abstract I extractROI(int x, int y, int w, int h);
1136
1137        /**
1138         * Extract a rectangular region of interest of the given width and height.
1139         * Coordinate <code>(0,0)</code> is the top-left corner. Returns a new
1140         * image.
1141         *
1142         * @param r
1143         *            the rectangle
1144         * @return A new image representing the selected region
1145         */
1146        public I extractROI(Rectangle r) {
1147                return extractROI((int) r.x, (int) r.y, (int) r.width, (int) r.height);
1148        }
1149
1150        /**
1151         * Fill this image with the given colour. Should overwrite all other data
1152         * stored in this image. Side-affects this image.
1153         *
1154         * @param colour
1155         *            the colour to fill the image with
1156         * @return A reference to this image.
1157         */
1158        public abstract I fill(Q colour);
1159
1160        /**
1161         * Flips the content horizontally. Side-affects this image.
1162         *
1163         * @return A reference to this image.
1164         */
1165        public abstract I flipX();
1166
1167        /**
1168         * Flips the content vertically. Side-affects this image.
1169         *
1170         * @return A reference to this image.
1171         */
1172        public abstract I flipY();
1173
1174        /**
1175         * Get a rectangle representing the image, with the top-left at 0,0 and the
1176         * bottom-right at width,height
1177         *
1178         * @return the bounding rectangle of the image
1179         */
1180        public Rectangle getBounds() {
1181                return new Rectangle(0, 0, this.getWidth(), this.getHeight());
1182        }
1183
1184        /**
1185         * Get the image width in pixels. This is syntactic sugar for
1186         * {@link #getWidth()};
1187         *
1188         * @return The image width in pixels.
1189         */
1190        public int getCols() {
1191                return getWidth();
1192        }
1193
1194        /**
1195         * Get bounding box of non-zero-valued pixels around the outside of the
1196         * image. Used by {@link #trim()}.
1197         *
1198         * @return A rectangle of the boundaries of the non-zero-valued image
1199         */
1200        public abstract Rectangle getContentArea();
1201
1202        /**
1203         * Get the given field of this image. Used for deinterlacing video, this
1204         * should return a new image containing the deinterlaced image. The returned
1205         * image will be half the height of this image.
1206         *
1207         * @param f
1208         *            The {@link Field} to extract from this image
1209         * @return An image containing only the odd or even fields.
1210         */
1211        public abstract I getField(Field f);
1212
1213        /**
1214         * Get the given field of this image, maintaining the image's aspect ratio
1215         * by doubling the fields. Used for deinterlacing video, this should return
1216         * a new image containing the deinterlaced image. The returned image should
1217         * be the same size as this image.
1218         *
1219         * @param f
1220         *            The {@link Field} to extract from this image
1221         * @return An image containing the odd or even fields doubled.
1222         */
1223        public abstract I getFieldCopy(Field f);
1224
1225        /**
1226         * Get the given field of this image, maintaining the image's aspect ratio
1227         * by interpolating between the fields. Used for deinterlacing video, this
1228         * should return a new image containing the detinterlaced image. The
1229         * returned image should be the same size as this image.
1230         *
1231         * @param f
1232         *            The {@link Field} to extract from this image.
1233         * @return An image containing the odd or even fields with interpolated rows
1234         *         between.
1235         */
1236        public abstract I getFieldInterpolate(Field f);
1237
1238        /**
1239         * Returns the image height in pixels.
1240         *
1241         * @return The image height in pixels.
1242         */
1243        public abstract int getHeight();
1244
1245        /**
1246         * Get the value of the pixel at coordinate <code>(x, y)</code>.
1247         *
1248         * @param x
1249         *            The x-ordinate to get
1250         * @param y
1251         *            The y-ordinate to get
1252         *
1253         * @return The pixel value at (x, y)
1254         */
1255        public abstract Q getPixel(int x, int y);
1256
1257        /**
1258         * Get the value of the pixel at coordinate p
1259         *
1260         * @param p
1261         *            The coordinate to get
1262         *
1263         * @return The pixel value at (x, y)
1264         */
1265        public Q getPixel(Pixel p) {
1266                return getPixel(p.x, p.y);
1267        }
1268
1269        /**
1270         * Returns a pixel comparator that is able to compare equality of pixels in
1271         * the given image type.
1272         *
1273         * @return A {@link Comparator} that compares pixels.
1274         */
1275        public abstract Comparator<? super Q> getPixelComparator();
1276
1277        /**
1278         * Get the value of a sub-pixel using linear-interpolation.
1279         *
1280         * @param x
1281         *            The x-ordinate to get
1282         * @param y
1283         *            The y-ordinate to get
1284         * @return The value of the interpolated point at <code>(x,y)</code>
1285         */
1286        public abstract Q getPixelInterp(double x, double y);
1287
1288        /**
1289         * Get the value of a sub-pixel using linear-interpolation. Also specify the
1290         * colour of the background (for interpolation at the edge)
1291         *
1292         * @param x
1293         *            The x-ordinate to get.
1294         * @param y
1295         *            The y-ordinate to get.
1296         * @param backgroundColour
1297         *            The colour of the background pixel.
1298         * @return The value of the interpolated point at <code>(x,y)</code>
1299         */
1300        public abstract Q getPixelInterp(double x, double y, Q backgroundColour);
1301
1302        /**
1303         * Returns the pixels in this image as a vector (an array of the pixel
1304         * type).
1305         *
1306         * @param f
1307         *            The array into which to place the data
1308         * @return The pixels in the image as a vector (a reference to the given
1309         *         array).
1310         */
1311        public Q[] getPixelVector(Q[] f) {
1312                for (int y = 0; y < getHeight(); y++)
1313                        for (int x = 0; x < getWidth(); x++)
1314                                f[x + y * getWidth()] = getPixel(x, y);
1315
1316                return f;
1317        }
1318
1319        /**
1320         * Get the height of this image. This is a syntactic sugar method for
1321         * {@link #getHeight()}.
1322         *
1323         * @return The image height in pixels.
1324         */
1325        public int getRows() {
1326                return getHeight();
1327        }
1328
1329        /**
1330         * Get the width (number of columns) in this image.
1331         *
1332         * @return the image width
1333         */
1334        public abstract int getWidth();
1335
1336        /**
1337         * Copy the internal state from another image of the same type. This method
1338         * is designed to be FAST. This means that bounds checking WILL NOT be
1339         * performed, so it is important that the images are the SAME size.
1340         *
1341         * @param im
1342         *            The source image to make a copy of.
1343         * @return A reference to this image.
1344         */
1345        public abstract I internalCopy(I im);
1346
1347        /**
1348         * Assign the internal state from another image of the same type.
1349         *
1350         * @param im
1351         *            The source image to make a copy of.
1352         * @return A reference to this image.
1353         */
1354        public abstract I internalAssign(I im);
1355
1356        /**
1357         * Copy pixels from given ARGB buffer image into this image. Side-affects
1358         * this image.
1359         *
1360         * @param pixelData
1361         *            buffer of ARGB packed integer pixels
1362         * @param width
1363         *            the width of the buffer
1364         * @param height
1365         *            the height of the buffer
1366         *
1367         * @return A reference to this image.
1368         */
1369        public abstract I internalAssign(int[] pixelData, int width, int height);
1370
1371        /**
1372         * Invert the image pixels by finding the maximum value and subtracting each
1373         * pixel value from that maximum.
1374         *
1375         * @return A reference to this image.
1376         */
1377        public abstract I inverse();
1378
1379        /**
1380         * Find the maximum pixel value.
1381         *
1382         * @return The maximum pixel value
1383         */
1384        public abstract Q max();
1385
1386        /**
1387         * Find the minimum pixel value.
1388         *
1389         * @return The minimum pixel value
1390         */
1391        public abstract Q min();
1392
1393        /**
1394         * Multiply the pixel values in this image with the corresponding pixel
1395         * values in the given image. This method returns a new image.
1396         *
1397         * @param im
1398         *            The image to multiply with this one
1399         * @return A new image containing the result.
1400         */
1401        public I multiply(Image<?, ?> im) {
1402                final I newImage = this.clone();
1403                newImage.multiplyInplace(im);
1404                return newImage;
1405        }
1406
1407        /**
1408         * Multiply each pixel of this by the given scalar and return new image.
1409         *
1410         * @param num
1411         *            The scalar which to multiply the image by
1412         * @return A new image containing the result
1413         */
1414        public I multiply(Q num) {
1415                final I newImage = this.clone();
1416                newImage.multiplyInplace(num);
1417                return newImage;
1418        }
1419
1420        /**
1421         * Multiply each pixel in this image by the corresponding pixel in the given
1422         * image. This method side-affects this image.
1423         *
1424         * @param im
1425         *            The image to multiply with this image.
1426         * @return A reference to this image.
1427         */
1428        public abstract I multiplyInplace(Image<?, ?> im);
1429
1430        /**
1431         * Multiply each pixel of this by the given scalar. This method side-affects
1432         * this image.
1433         *
1434         * @param num
1435         *            The scalar to multiply this image by.
1436         * @return A reference to this image.
1437         */
1438        public abstract I multiplyInplace(Q num);
1439
1440        /**
1441         * Create a new instance of this image subclass with given dimensions.
1442         *
1443         * @param width
1444         *            The image width
1445         * @param height
1446         *            The image height
1447         *
1448         * @return A new instance of an image of type <code>I</code>
1449         */
1450        public abstract I newInstance(int width, int height);
1451
1452        /**
1453         * Normalise all pixel values to fall within the range 0.0 - 1.0. This
1454         * should be scaled by both the maximum and minimum values. This method
1455         * side-affects this image.
1456         *
1457         * @return A reference to this image.
1458         */
1459        public abstract I normalise();
1460
1461        /**
1462         * Adds padding as in {@link Image#padding(int, int, Object)}. The padding
1463         * colour is the colour of the closest border pixel.
1464         *
1465         * @param paddingWidth
1466         *            padding in the x direction
1467         * @param paddingHeight
1468         *            padding in the y direction
1469         * @return padded image
1470         */
1471        public I padding(int paddingWidth, int paddingHeight) {
1472                return this.padding(paddingWidth, paddingHeight, null);
1473        }
1474
1475        /**
1476         * Adds this many pixels to both sides of the image such that the new image
1477         * width = padding + width + padding with the original image in the middle
1478         *
1479         * @param paddingWidth
1480         *            left and right padding width
1481         * @param paddingHeight
1482         *            top and bottom padding width
1483         * @param paddingColour
1484         *            colour of padding, if null the closes border pixel is used
1485         * @return padded image
1486         */
1487        @SuppressWarnings("unchecked")
1488        public I padding(int paddingWidth, int paddingHeight, Q paddingColour) {
1489                final I out = this.newInstance(paddingWidth + this.getWidth() + paddingWidth, paddingHeight + this.getHeight()
1490                                + paddingHeight);
1491
1492                out.createRenderer().drawImage((I) this, paddingWidth, paddingHeight);
1493                final int rightLimit = paddingWidth + this.getWidth();
1494                final int bottomLimit = paddingHeight + this.getHeight();
1495                // Fill the padding with a colour if it isn't null
1496                if (paddingColour != null)
1497                        for (int y = 0; y < out.getHeight(); y++) {
1498                                for (int x = 0; x < out.getWidth(); x++) {
1499                                        if (x >= paddingWidth && x < rightLimit && y >= paddingHeight && y < bottomLimit)
1500                                                continue;
1501                                        out.setPixel(x, y, paddingColour);
1502                                }
1503                        }
1504                else
1505                        for (int y = 0; y < out.getHeight(); y++) {
1506                                for (int x = 0; x < out.getWidth(); x++) {
1507                                        if (x >= paddingWidth && x < rightLimit && y >= paddingHeight && y < bottomLimit)
1508                                                continue;
1509                                        if (x < paddingWidth && y < paddingHeight)
1510                                                out.setPixel(x, y, this.getPixel(0, 0)); // Top Left
1511                                        else if (x < paddingWidth && y >= bottomLimit)
1512                                                out.setPixel(x, y, this.getPixel(0, this.getHeight() - 1)); // Bottom
1513                                        // Left
1514                                        else if (x >= rightLimit && y < paddingHeight)
1515                                                out.setPixel(x, y, this.getPixel(this.getWidth() - 1, 0)); // Top
1516                                        // Right
1517                                        else if (x >= rightLimit && y >= bottomLimit)
1518                                                out.setPixel(x, y, this.getPixel(this.getWidth() - 1, this.getHeight() - 1)); // Bottom
1519                                        // Right
1520                                        else {
1521                                                if (x < paddingWidth)
1522                                                        out.setPixel(x, y, this.getPixel(0, y - paddingHeight)); // Left
1523                                                else if (x >= rightLimit)
1524                                                        out.setPixel(x, y, this.getPixel(this.getWidth() - 1, y - paddingHeight)); // Right
1525                                                else if (y < paddingHeight)
1526                                                        out.setPixel(x, y, this.getPixel(x - paddingWidth, 0)); // Top
1527                                                else if (y >= bottomLimit)
1528                                                        out.setPixel(x, y, this.getPixel(x - paddingWidth, this.getHeight() - 1)); // Bottom
1529                                        }
1530                                }
1531                        }
1532
1533                return out;
1534        }
1535
1536        /**
1537         * Adds pixels to around the image such that the new image width =
1538         * paddingLeft + width + paddingRight with the original image in the middle.
1539         * The values of the padding pixels are formed from repeated symmetric
1540         * reflections of the original image.
1541         *
1542         * @param paddingLeft
1543         *            left padding width
1544         * @param paddingRight
1545         *            right padding width
1546         * @param paddingTop
1547         *            top padding width
1548         * @param paddingBottom
1549         *            bottom padding width
1550         * @return padded image
1551         */
1552        public I paddingSymmetric(int paddingLeft, int paddingRight, int paddingTop, int paddingBottom) {
1553                final I out = this.newInstance(paddingLeft + this.getWidth() + paddingRight, paddingTop + this.getHeight()
1554                                + paddingBottom);
1555                final I clone = this.clone();
1556                final I hflip = clone.clone().flipX();
1557
1558                final ImageRenderer<Q, I> rend = out.createRenderer();
1559                rend.drawImage(clone, paddingLeft, paddingTop);
1560
1561                // left
1562                for (int i = paddingLeft - this.getWidth(), c = 0; i > -this.getWidth(); i -= this.getWidth(), c++) {
1563                        if (c % 2 == 0) {
1564                                rend.drawImage(hflip, i, paddingTop);
1565                        } else {
1566                                rend.drawImage(clone, i, paddingTop);
1567                        }
1568                }
1569
1570                // right
1571                for (int i = paddingLeft + this.getWidth(), c = 0; i < paddingLeft + paddingRight + this.getWidth(); i += this
1572                                .getWidth(), c++)
1573                {
1574                        if (c % 2 == 0) {
1575                                rend.drawImage(hflip, i, paddingTop);
1576                        } else {
1577                                rend.drawImage(clone, i, paddingTop);
1578                        }
1579                }
1580
1581                final I centre = out.extractROI(0, paddingTop, paddingLeft + this.getWidth() + paddingRight, this.getHeight());
1582                final I yflip = centre.clone().flipY();
1583
1584                // up
1585                for (int i = paddingTop - this.getHeight(), c = 0; i > -this.getHeight(); i -= this.getHeight(), c++) {
1586                        if (c % 2 == 0) {
1587                                rend.drawImage(yflip, 0, i);
1588                        } else {
1589                                rend.drawImage(centre, 0, i);
1590                        }
1591                }
1592
1593                // down
1594                for (int i = paddingTop + this.getHeight(), c = 0; i < paddingTop + paddingBottom + this.getHeight(); i += this
1595                                .getHeight(), c++)
1596                {
1597                        if (c % 2 == 0) {
1598                                rend.drawImage(yflip, 0, i);
1599                        } else {
1600                                rend.drawImage(centre, 0, i);
1601                        }
1602                }
1603
1604                return out;
1605        }
1606
1607        /**
1608         * Process this image with the given {@link GridProcessor} and return new
1609         * image containing the result.
1610         *
1611         * @param p
1612         *            {@link GridProcessor} to apply to this image.
1613         * @return A new image containing the result.
1614         */
1615        public I process(GridProcessor<Q, I> p) {
1616                final int height = p.getVerticalGridElements();
1617                final int width = p.getHorizontalGridElements();
1618                final I newImage = this.newInstance(width, height);
1619                newImage.zero();
1620
1621                final int gridWidth = getWidth() / width;
1622                final int gridHeight = getHeight() / height;
1623                for (int y = 0; y < height; y++)
1624                        for (int x = 0; x < width; x++)
1625                                newImage.setPixel(x, y,
1626                                                p.processGridElement(this.extractROI(gridWidth * x, gridHeight * y, gridWidth, gridHeight)));
1627
1628                return newImage;
1629        }
1630
1631        /**
1632         * Process this image with an {@link ImageProcessor} and return new image
1633         * containing the result.
1634         *
1635         * @param p
1636         *            The {@link ImageProcessor} to apply to this image.
1637         * @return A new image containing the result.
1638         */
1639        public I process(ImageProcessor<I> p) {
1640                final I newImage = this.clone();
1641                newImage.processInplace(p);
1642                return newImage;
1643        }
1644
1645        /**
1646         * Process this image with the given {@link KernelProcessor} and return new
1647         * image containing the result.
1648         *
1649         * @param p
1650         *            The {@link KernelProcessor} to apply.
1651         * @return A new image containing the result.
1652         */
1653        public I process(KernelProcessor<Q, I> p) {
1654                return process(p, false);
1655        }
1656
1657        /**
1658         * Process this image with the given {@link KernelProcessor} and return new
1659         * image containing the result.
1660         *
1661         * @param p
1662         *            The {@link KernelProcessor} to apply.
1663         * @param pad
1664         *            Should the image be zero padded so the kernel reaches the
1665         *            edges of the output
1666         * @return A new image containing the result.
1667         */
1668        public I process(KernelProcessor<Q, I> p, boolean pad) {
1669                final I newImage = this.clone();
1670                newImage.zero();
1671
1672                final int kh = p.getKernelHeight();
1673                final int kw = p.getKernelWidth();
1674
1675                final int hh = p.getKernelHeight() / 2;
1676                final int hw = p.getKernelWidth() / 2;
1677
1678                final I tmp = newInstance(kw, kh);
1679
1680                if (!pad) {
1681                        for (int y = hh; y < getHeight() - (kh - hh); y++) {
1682                                for (int x = hw; x < getWidth() - (kw - hw); x++) {
1683                                        newImage.setPixel(x, y, p.processKernel(this.extractROI(x - hw, y - hh, tmp)));
1684                                }
1685                        }
1686                } else {
1687                        for (int y = 0; y < getHeight(); y++) {
1688                                for (int x = 0; x < getWidth(); x++) {
1689                                        newImage.setPixel(x, y, p.processKernel(this.extractROI(x - hw, y - hh, tmp)));
1690                                }
1691                        }
1692                }
1693
1694                return newImage;
1695        }
1696
1697        /**
1698         * Process this image with the given {@link PixelProcessor} and return a new
1699         * image containing the result.
1700         *
1701         * @param p
1702         *            The {@link PixelProcessor} to apply.
1703         * @return A new image containing the result.
1704         */
1705        public I process(PixelProcessor<Q> p) {
1706                final I newImage = this.clone();
1707                newImage.processInplace(p);
1708                return newImage;
1709        }
1710
1711        /**
1712         * Process this image with an {@link Processor} and return new image
1713         * containing the result.
1714         *
1715         * @param p
1716         *            The {@link Processor} to apply to this image.
1717         * @return A new image containing the result.
1718         */
1719        public I process(Processor<I> p) {
1720                final I newImage = this.clone();
1721                newImage.processInplace(p);
1722                return newImage;
1723        }
1724
1725        /**
1726         * Process this image with the given {@link Processor} side-affecting this
1727         * image.
1728         *
1729         * @param p
1730         *            The {@link Processor} to apply.
1731         * @return A reference to this image containing the result.
1732         */
1733        @SuppressWarnings("unchecked")
1734        public I processInplace(Processor<I> p) {
1735                if (p == null)
1736                        return (I) this;
1737                if (p instanceof ImageProcessor)
1738                        return processInplace((ImageProcessor<I>) p);
1739                if (p instanceof KernelProcessor)
1740                        return processInplace((KernelProcessor<Q, I>) p);
1741                if (p instanceof PixelProcessor)
1742                        return processInplace((PixelProcessor<Q>) p);
1743
1744                throw new UnsupportedOperationException("Unsupported Processor type");
1745        }
1746
1747        /**
1748         * Process this image with the given {@link ImageProcessor} side-affecting
1749         * this image.
1750         *
1751         * @param p
1752         *            The {@link ImageProcessor} to apply.
1753         * @return A reference to this image containing the result.
1754         */
1755        @SuppressWarnings("unchecked")
1756        public I processInplace(ImageProcessor<I> p) {
1757                p.processImage((I) this);
1758                return (I) this;
1759        }
1760
1761        /**
1762         * Process this image with the given {@link KernelProcessor} side-affecting
1763         * this image.
1764         *
1765         * @param p
1766         *            The {@link KernelProcessor} to apply.
1767         * @return A reference to this image containing the result.
1768         */
1769        public I processInplace(KernelProcessor<Q, I> p) {
1770                return processInplace(p, false);
1771        }
1772
1773        /**
1774         * Process this image with the given {@link KernelProcessor} side-affecting
1775         * this image.
1776         *
1777         * @param p
1778         *            The {@link KernelProcessor} to apply.
1779         * @param pad
1780         *            Should the image be zero padded so the kernel reaches the
1781         *            edges of the output
1782         * @return A reference to this image containing the result.
1783         */
1784        @SuppressWarnings("unchecked")
1785        public I processInplace(KernelProcessor<Q, I> p, boolean pad) {
1786                final I newImage = process(p, pad);
1787                this.internalAssign(newImage);
1788                return (I) this;
1789        }
1790
1791        /**
1792         * Process this image with the given {@link PixelProcessor} side-affecting
1793         * this image.
1794         *
1795         * @param p
1796         *            The {@link PixelProcessor} to apply.
1797         * @return A reference to this image containing the result.
1798         */
1799        @SuppressWarnings("unchecked")
1800        public I processInplace(PixelProcessor<Q> p) {
1801                for (int y = 0; y < getHeight(); y++) {
1802                        for (int x = 0; x < getWidth(); x++) {
1803                                setPixel(x, y, p.processPixel(getPixel(x, y)));
1804                        }
1805                }
1806
1807                return (I) this;
1808        }
1809
1810        /**
1811         * Process this image with the given {@link PixelProcessor} only affecting
1812         * those pixels where the mask is non-zero. Returns a new image.
1813         *
1814         * @param mask
1815         *            The mask to apply to the processing.
1816         * @param p
1817         *            The {@link PixelProcessor} to apply.
1818         * @return A new image containing the result.
1819         */
1820        public I processMasked(FImage mask, PixelProcessor<Q> p) {
1821                final I newImage = this.clone();
1822                newImage.processMaskedInplace(mask, p);
1823                return newImage;
1824        }
1825
1826        /**
1827         * Process this image with the given {@link PixelProcessor}, only affecting
1828         * those pixels where the mask is non-zero. Side-affects this image.
1829         *
1830         * @param mask
1831         *            The mask to apply to the processor.
1832         * @param p
1833         *            The {@link PixelProcessor} to apply.
1834         * @return A reference to this image containing the result.
1835         */
1836        @SuppressWarnings("unchecked")
1837        public I processMaskedInplace(FImage mask, PixelProcessor<Q> p) {
1838                for (int y = 0; y < getHeight(); y++) {
1839                        for (int x = 0; x < getWidth(); x++) {
1840                                if (mask.pixels[y][x] == 0)
1841                                        continue;
1842                                setPixel(x, y, p.processPixel(getPixel(x, y)));
1843                        }
1844                }
1845                return (I) this;
1846        }
1847
1848        /**
1849         * Sets the pixel at <code>(x,y)</code> to the given value. Side-affects
1850         * this image.
1851         *
1852         * @param x
1853         *            The x-ordinate of the pixel to set
1854         * @param y
1855         *            The y-ordinate of the pixel to set
1856         * @param val
1857         *            The value to set the pixel to.
1858         */
1859        public abstract void setPixel(int x, int y, Q val);
1860
1861        /**
1862         * Subtract the corresponding pixel value from the given image from the
1863         * pixel values in this image. Returns a new image.
1864         *
1865         * @param im
1866         *            The image to subtract from this image.
1867         * @return A new image containing the result.
1868         */
1869        public I subtract(Image<?, ?> im) {
1870                final I newImage = this.clone();
1871                newImage.subtractInplace(im);
1872                return newImage;
1873        }
1874
1875        /**
1876         * Subtract a scalar from every pixel value in this image and return new
1877         * image.
1878         *
1879         * @param num
1880         *            A value to subtract from each pixel.
1881         * @return A new image containing the result.
1882         */
1883        public I subtract(Q num) {
1884                final I newImage = this.clone();
1885                newImage.subtractInplace(num);
1886                return newImage;
1887        }
1888
1889        /**
1890         * Subtract the corresponding pixel value from the given image from the
1891         * pixel values in this image. Side-affects this image.
1892         *
1893         * @param im
1894         *            The image to subtract from this image.
1895         * @return A reference to this containing the result.
1896         */
1897        public abstract I subtractInplace(Image<?, ?> im);
1898
1899        /**
1900         * Subtract a scalar from every pixel value in this image. Side-affects this
1901         * image.
1902         *
1903         * @param num
1904         *            A value to subtract from each pixel.
1905         * @return A reference to this image containing the result.
1906         */
1907        public abstract I subtractInplace(Q num);
1908
1909        /**
1910         * Set all values less than the given threshold to 0 and all others to 1.
1911         * Side-affects this image.
1912         *
1913         * @param thresh
1914         *            The threshold value
1915         * @return A reference to this image containing the result.
1916         */
1917        public abstract I threshold(Q thresh);
1918
1919        /**
1920         * Convert the image to a byte representation suitable for writing to a pnm
1921         * type format. Each byte should represent a single pixel. Multiband images
1922         * should interleave the data; e.g. [R1,G1,B1,R2,G2,B2...etc.]
1923         *
1924         * @return This image as a byte array
1925         */
1926        public abstract byte[] toByteImage();
1927
1928        /**
1929         * Returns a 1D array representation of this image with each pixel
1930         * represented as a packed ARGB integer.
1931         *
1932         * @return An array of ARGB pixels.
1933         */
1934        public abstract int[] toPackedARGBPixels();
1935
1936        /**
1937         * Apply a transform matrix to the image and returns the result as a new
1938         * image.
1939         *
1940         * @param transform
1941         *            The transform matrix to apply.
1942         * @return A new image containing the result.
1943         */
1944        public I transform(Matrix transform) {
1945                boolean unset = true;
1946                double minX = 0, minY = 0, maxX = 0, maxY = 0;
1947                final double[][][] extrema = new double[][][] {
1948                                { { 0 }, { 0 }, { 1 } },
1949                                { { 0 }, { this.getHeight() }, { 1 } },
1950                                { { this.getWidth() }, { 0 }, { 1 } },
1951                                { { this.getWidth() }, { this.getHeight() }, { 1 } },
1952                };
1953                for (final double[][] ext : extrema) {
1954                        final Matrix tmp = transform.times(Matrix.constructWithCopy(ext));
1955                        if (unset) {
1956                                minX = maxX = tmp.get(0, 0);
1957                                maxY = minY = tmp.get(1, 0);
1958                                unset = false;
1959                        } else {
1960                                if (tmp.get(0, 0) > maxX)
1961                                        maxX = tmp.get(0, 0);
1962                                if (tmp.get(1, 0) > maxY)
1963                                        maxY = tmp.get(1, 0);
1964                                if (tmp.get(0, 0) < minX)
1965                                        minX = tmp.get(0, 0);
1966                                if (tmp.get(1, 0) < minY)
1967                                        minY = tmp.get(1, 0);
1968                        }
1969                }
1970                final I output = this.newInstance((int) (Math.abs(maxX - minX)), (int) (Math.abs(maxY - minY)));
1971                final Matrix invTrans = transform.inverse();
1972                final double[][] invTransData = invTrans.getArray();
1973
1974                for (int x = 0; x < output.getWidth(); x++) {
1975                        for (int y = 0; y < output.getHeight(); y++) {
1976                                double oldx = invTransData[0][0] * x + invTransData[0][1] * y + invTransData[0][2];
1977                                double oldy = invTransData[1][0] * x + invTransData[1][1] * y + invTransData[1][2];
1978                                final double norm = invTransData[2][0] * x + invTransData[2][1] * y + invTransData[2][2];
1979
1980                                oldx /= norm;
1981                                oldy /= norm;
1982
1983                                if (oldx < 0 || oldx >= this.getWidth() || oldy < 0 || oldy >= this.getHeight())
1984                                        continue;
1985
1986                                output.setPixel(x, y, this.getPixelInterp(oldx, oldy));
1987                        }
1988                }
1989                return output;
1990        }
1991
1992        /**
1993         * Removes zero-valued pixels from around the outside of the image.
1994         * Analagous to {@link String#trim()}.
1995         *
1996         * @return A new image containing the trimmed image.
1997         */
1998        public I trim() {
1999                final Rectangle rect = this.getContentArea();
2000                return this.extractROI((int) rect.minX(), (int) rect.minY(), (int) (rect.getWidth()), (int) (rect.getHeight()));
2001        }
2002
2003        /**
2004         * Set all pixels in the image to zero. Side-affects this image.
2005         *
2006         * @return A reference to this image containing the result.
2007         */
2008        public abstract I zero();
2009
2010        /**
2011         * Shifts all the pixels to the left by one pixel
2012         *
2013         * @return A reference to this image.
2014         */
2015        public I shiftLeftInplace() {
2016                return shiftLeftInplace(1);
2017        }
2018
2019        /**
2020         * Shifts all the pixels to the right by one pixel
2021         *
2022         * @return A reference to this image.
2023         */
2024        public I shiftRightInplace() {
2025                return shiftRightInplace(1);
2026        }
2027
2028        /**
2029         * Shifts all the pixels to the left by count pixel
2030         *
2031         * @param count
2032         *            The number of pixels
2033         *
2034         * @return A reference to this image.
2035         */
2036        public I shiftLeftInplace(int count) {
2037                return this.internalAssign(this.shiftLeft(count));
2038        }
2039
2040        /**
2041         * Shifts all the pixels to the right by count pixel
2042         *
2043         * @param count
2044         *            The number of pixels
2045         *
2046         * @return A reference to this image.
2047         */
2048        public I shiftRightInplace(int count) {
2049                return this.internalAssign(this.shiftRight(count));
2050        }
2051
2052        /**
2053         * Returns a new image that is it shifted around the x-ordinates by one
2054         * pixel
2055         *
2056         * @return A new image shifted around to the left by one pixel
2057         */
2058        public I shiftLeft() {
2059                return shiftLeft(1);
2060        }
2061
2062        /**
2063         * Returns a new image that is it shifted around the x-ordinates by the
2064         * number of pixels given.
2065         *
2066         * @param nPixels
2067         *            The number of pixels
2068         *
2069         * @return A new image shifted around to the left by the number of pixels
2070         */
2071        public I shiftLeft(int nPixels) {
2072                final I output = this.newInstance(getWidth(), getHeight());
2073                final I img = this.extractROI(0, 0, nPixels, getHeight());
2074                output.createRenderer().drawImage(
2075                                this.extractROI(nPixels, 0, getWidth() - nPixels, getHeight()), 0, 0);
2076                output.createRenderer().drawImage(img, getWidth() - nPixels, 0);
2077                return output;
2078        }
2079
2080        /**
2081         * Returns a new image that is it shifted around the x-ordinates by one
2082         * pixel
2083         *
2084         * @return A new image shifted around to the right by one pixel
2085         */
2086        public I shiftRight() {
2087                return shiftRight(1);
2088        }
2089
2090        /**
2091         * Returns a new image that is it shifted around the x-ordinates by the
2092         * number of pixels given.
2093         *
2094         * @param nPixels
2095         *            the number of pixels
2096         *
2097         * @return A new image shifted around to the right by the number of pixels
2098         */
2099        public I shiftRight(int nPixels) {
2100                final I output = this.newInstance(getWidth(), getHeight());
2101                final I img = this.extractROI(getWidth() - nPixels, 0, nPixels, getHeight());
2102                output.createRenderer().drawImage(
2103                                this.extractROI(0, 0, getWidth() - nPixels, getHeight()), nPixels, 0);
2104                output.createRenderer().drawImage(img, 0, 0);
2105                return output;
2106        }
2107
2108        /**
2109         * Shifts all the pixels up by one pixel
2110         *
2111         * @return A reference to this image.
2112         */
2113        public I shiftUpInplace() {
2114                return shiftUpInplace(1);
2115        }
2116
2117        /**
2118         * Shifts all the pixels down by one pixels
2119         *
2120         * @return A reference to this image.
2121         */
2122        public I shiftDownInplace() {
2123                return shiftDownInplace(1);
2124        }
2125
2126        /**
2127         * Shifts all the pixels up by count pixels
2128         *
2129         * @param count
2130         *            The number of pixels
2131         *
2132         * @return A reference to this image.
2133         */
2134        public I shiftUpInplace(int count) {
2135                return this.internalAssign(this.shiftUp(count));
2136        }
2137
2138        /**
2139         * Shifts all the pixels down by count pixels
2140         *
2141         * @param count
2142         *            The number of pixels
2143         *
2144         * @return A reference to this image.
2145         */
2146        public I shiftDownInplace(int count) {
2147                return this.internalAssign(this.shiftDown(count));
2148        }
2149
2150        /**
2151         * Returns a new image that is it shifted around the x-ordinates by one
2152         * pixel
2153         *
2154         * @return A new image shifted around up by one pixel
2155         */
2156        public I shiftUp() {
2157                return shiftUp(1);
2158        }
2159
2160        /**
2161         * Returns a new image that is it shifted around the x-ordinates by the
2162         * number of pixels given.
2163         *
2164         * @param nPixels
2165         *            The number of pixels
2166         *
2167         * @return A new image shifted around up by the number of pixels
2168         */
2169        public I shiftUp(int nPixels) {
2170                final I output = this.newInstance(getWidth(), getHeight());
2171                final I img = this.extractROI(0, 0, getWidth(), nPixels);
2172                output.createRenderer().drawImage(
2173                                this.extractROI(0, nPixels, getWidth(), getHeight() - nPixels), 0, 0);
2174                output.createRenderer().drawImage(img, 0, getHeight() - nPixels);
2175                return output;
2176        }
2177
2178        /**
2179         * Returns a new image that is it shifted around the x-ordinates by one
2180         * pixel
2181         *
2182         * @return A new image shifted around down by one pixel
2183         */
2184        public I shiftDown() {
2185                return shiftDown(1);
2186        }
2187
2188        /**
2189         * Returns a new image that is it shifted around the x-ordinates by the
2190         * number of pixels given.
2191         *
2192         * @param nPixels
2193         *            the number of pixels
2194         *
2195         * @return A new image shifted around down by the number of pixels
2196         */
2197        public I shiftDown(int nPixels) {
2198                final I output = this.newInstance(getWidth(), getHeight());
2199                final I img = this.extractROI(0, getHeight() - nPixels, getWidth(), nPixels);
2200                output.createRenderer().drawImage(
2201                                this.extractROI(0, 0, getWidth(), getHeight() - nPixels), 0, nPixels);
2202                output.createRenderer().drawImage(img, 0, 0);
2203                return output;
2204        }
2205
2206        /**
2207         * Overlays the given image on this image and returns a new image containing
2208         * the result.
2209         *
2210         * @param image
2211         *            The image to overlay on this image.
2212         * @param x
2213         *            The location at which to overlay the image
2214         * @param y
2215         *            The location at which to overlay the image
2216         * @return A new image containing the result
2217         */
2218        public I overlay(I image, int x, int y) {
2219                final I img = this.clone();
2220                img.overlayInplace(image, x, y);
2221                return img;
2222        }
2223
2224        /**
2225         * Overlays the given image on this image directly. The method returns this
2226         * image for chaining.
2227         *
2228         * @param image
2229         *            The image to overlay
2230         * @param x
2231         *            The location at which to overlay the image
2232         * @param y
2233         *            The location at which to overlay the image
2234         * @return Returns this image
2235         */
2236        public abstract I overlayInplace(I image, int x, int y);
2237
2238        @SuppressWarnings("unchecked")
2239        @Override
2240        public I getImage() {
2241                return (I) this;
2242        }
2243
2244        /**
2245         * Replace pixels of a certain colour with another colour. Side-affects this
2246         * image.
2247         *
2248         * @param target
2249         *            the colour to fill the image with
2250         * @param replacement
2251         *            the colour to fill the image with
2252         * @return A reference to this image.
2253         */
2254        public abstract I replace(Q target, Q replacement);
2255
2256        /**
2257         * Sub-pixel sampling of a centred rectangular region such that
2258         * <code>dst(x, y) = src(x + center.x  (width(dst)  1) ⇤ 0.5, y + center.y  (height(dst)  1) ⇤ 0.5)</code>
2259         * . Sub-pixels values are estimated using bilinear interpolation.
2260         *
2261         * @see #getPixelInterp(double, double)
2262         *
2263         * @param centre
2264         *            the centre
2265         * @param width
2266         *            the region width
2267         * @param height
2268         *            the region height
2269         * @return the extracted sub-pixel region
2270         */
2271        public I extractCentreSubPix(Point2d centre, int width, int height) {
2272                return extractCentreSubPix(centre.getX(), centre.getY(), width, height);
2273        }
2274
2275        /**
2276         * Sub-pixel sampling of a centred rectangular region such that
2277         * <code>dst(x, y) = src(x + center.x  (width(dst)  1) ⇤ 0.5, y + center.y  (height(dst)  1) ⇤ 0.5)</code>
2278         * . Sub-pixels values are estimated using bilinear interpolation.
2279         *
2280         * @see #getPixelInterp(double, double)
2281         * @param cx
2282         *            the x-ordinate of the centre
2283         * @param cy
2284         *            the y-ordinate of the centre
2285         * @param width
2286         *            the region width
2287         * @param height
2288         *            the region height
2289         * @return the extracted sub-pixel region
2290         */
2291        public I extractCentreSubPix(float cx, float cy, int width, int height) {
2292                final I out = newInstance(width, height);
2293                return extractCentreSubPix(cx, cy, out);
2294        }
2295
2296        /**
2297         * Sub-pixel sampling of a centred rectangular region such that
2298         * <code>dst(x, y) = src(x + center.x  (width(dst)  1) ⇤ 0.5, y + center.y  (height(dst)  1) ⇤ 0.5)</code>
2299         * . Sub-pixels values are estimated using bilinear interpolation.
2300         *
2301         * @see #getPixelInterp(double, double)
2302         *
2303         * @param centre
2304         *            the centre
2305         * @param out
2306         *            the output image (also defines the size of the extracted
2307         *            region)
2308         * @return <code>out</code>
2309         */
2310        public I extractCentreSubPix(Point2d centre, I out) {
2311                return extractCentreSubPix(centre.getX(), centre.getY(), out);
2312        }
2313
2314        /**
2315         * Sub-pixel sampling of a centred rectangular region such that
2316         * <code>dst(x, y) = src(x + center.x  (width(dst)  1) ⇤ 0.5, y + center.y  (height(dst)  1) ⇤ 0.5)</code>
2317         * . Sub-pixels values are estimated using bilinear interpolation.
2318         *
2319         * @see #getPixelInterp(double, double)
2320         * @param cx
2321         *            the x-ordinate of the centre
2322         * @param cy
2323         *            the y-ordinate of the centre
2324         * @param out
2325         *            the output image (also defines the size of the extracted
2326         *            region)
2327         * @return <code>out</code>
2328         */
2329        public abstract I extractCentreSubPix(float cx, float cy, I out);
2330}