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}