1 /**
2 * Copyright (c) 2011, The University of Southampton and the individual contributors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * * Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * * Neither the name of the University of Southampton nor the names of its
16 * contributors may be used to endorse or promote products derived from this
17 * software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30 package org.openimaj.image;
31
32 import java.io.Serializable;
33 import java.text.AttributedString;
34 import java.util.Comparator;
35 import java.util.List;
36
37 import org.openimaj.image.analyser.ImageAnalyser;
38 import org.openimaj.image.analyser.PixelAnalyser;
39 import org.openimaj.image.combiner.AccumulatingImageCombiner;
40 import org.openimaj.image.combiner.ImageCombiner;
41 import org.openimaj.image.pixel.Pixel;
42 import org.openimaj.image.processor.GridProcessor;
43 import org.openimaj.image.processor.ImageProcessor;
44 import org.openimaj.image.processor.KernelProcessor;
45 import org.openimaj.image.processor.PixelProcessor;
46 import org.openimaj.image.processor.Processor;
47 import org.openimaj.image.renderer.ImageRenderer;
48 import org.openimaj.image.renderer.RenderHints;
49 import org.openimaj.image.typography.Font;
50 import org.openimaj.image.typography.FontStyle;
51 import org.openimaj.math.geometry.path.Path2d;
52 import org.openimaj.math.geometry.point.Point2d;
53 import org.openimaj.math.geometry.shape.Polygon;
54 import org.openimaj.math.geometry.shape.Rectangle;
55 import org.openimaj.math.geometry.shape.Shape;
56
57 import Jama.Matrix;
58
59 /**
60 * Base class for representing and manipulating images. Images are typed by the
61 * type of pixel at each coordinate and the concrete subclass type.
62 *
63 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
64 *
65 * @param <Q>
66 * the pixel type
67 * @param <I>
68 * the actual image of the concrete subclass
69 */
70 public abstract class Image<Q, I extends Image<Q, I>> implements Cloneable, Serializable, ImageProvider<I> {
71 /**
72 * Enumerator for representing the type of field interlacing operations.
73 *
74 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
75 */
76 public enum Field {
77 /**
78 * Odd field
79 */
80 ODD,
81 /**
82 * Even field
83 */
84 EVEN
85 }
86
87 private static final long serialVersionUID = 1L;
88
89 /**
90 * Accumulate this image the the given {@link AccumulatingImageCombiner}.
91 *
92 * @param combiner
93 * the combiner
94 * @see AccumulatingImageCombiner#accumulate(Image)
95 */
96 @SuppressWarnings("unchecked")
97 public void accumulateWith(AccumulatingImageCombiner<I, ?> combiner) {
98 combiner.accumulate((I) this);
99 }
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 }