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.line.Line2d; 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, 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 Line2d 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(Line2d line, int thickness, Q col) { 646 createRenderer().drawLine(line, thickness, col); 647 } 648 649 /** 650 * Draw the given list of lines using {@link #drawLine(Line2d, int, Object)} 651 * with the given colour and thickness. Side-affects this image. 652 * 653 * <p> 654 * This is a convenience method that calls {@link #createRenderer()} to get 655 * the default renderer to do the actual drawing. Create the renderer 656 * yourself and use it to draw if you need more control. 657 * </p> 658 * 659 * @param lines 660 * The list of lines to draw. 661 * @param thickness 662 * the stroke width 663 * @param col 664 * The colour to draw each point. 665 */ 666 public void drawLines(Iterable<? extends Line2d> lines, int thickness, Q col) { 667 createRenderer().drawLines(lines, thickness, col); 668 } 669 670 /** 671 * Draw a dot centered on the given location (rounded to nearest integer 672 * location) at the given size and with the given color. Side-affects this 673 * image. 674 * 675 * <p> 676 * This is a convenience method that calls {@link #createRenderer()} to get 677 * the default renderer to do the actual drawing. Create the renderer 678 * yourself and use it to draw if you need more control. 679 * </p> 680 * 681 * @param p 682 * The coordinates at which to draw the point 683 * @param col 684 * The colour to draw the point 685 * @param size 686 * The size at which to draw the point. 687 */ 688 public void drawPoint(Point2d p, Q col, int size) { 689 createRenderer().drawPoint(p, col, size); 690 } 691 692 /** 693 * Draw the given list of points using 694 * {@link #drawPoint(Point2d, Object, int)} with the given colour and size. 695 * Side-affects this image. 696 * 697 * <p> 698 * This is a convenience method that calls {@link #createRenderer()} to get 699 * the default renderer to do the actual drawing. Create the renderer 700 * yourself and use it to draw if you need more control. 701 * </p> 702 * 703 * @param pts 704 * The list of points to draw. 705 * @param col 706 * The colour to draw each point. 707 * @param size 708 * The size to draw each point. 709 */ 710 public void drawPoints(Iterable<? extends Point2d> pts, Q col, int size) { 711 createRenderer().drawPoints(pts, col, size); 712 } 713 714 /** 715 * Draw the given polygon in the specified colour with the given thickness 716 * lines. Side-affects this image. 717 * 718 * <p> 719 * This is a convenience method that calls {@link #createRenderer()} to get 720 * the default renderer to do the actual drawing. Create the renderer 721 * yourself and use it to draw if you need more control. 722 * </p> 723 * 724 * @param p 725 * The polygon to draw. 726 * @param thickness 727 * The thickness of the lines to use 728 * @param col 729 * The colour to draw the lines in 730 */ 731 public void drawPolygon(Polygon p, int thickness, Q col) { 732 createRenderer().drawPolygon(p, thickness, col); 733 } 734 735 /** 736 * Draw the given polygon in the specified colour. Uses 737 * {@link #drawPolygon(Polygon, int, Object)} with line thickness 1. 738 * Side-affects this image. 739 * 740 * <p> 741 * This is a convenience method that calls {@link #createRenderer()} to get 742 * the default renderer to do the actual drawing. Create the renderer 743 * yourself and use it to draw if you need more control. 744 * </p> 745 * 746 * @param p 747 * The polygon to draw. 748 * @param col 749 * The colour to draw the polygon in. 750 */ 751 public void drawPolygon(Polygon p, Q col) { 752 createRenderer().drawPolygon(p, col); 753 } 754 755 /** 756 * Draw the given polygon, filled with the specified colour. Side-affects 757 * 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 col 768 * The colour to fill the polygon with. 769 */ 770 public void drawPolygonFilled(Polygon p, Q col) { 771 createRenderer().drawPolygonFilled(p, col); 772 } 773 774 /** 775 * Draw the given shape in the specified colour with the given thickness 776 * lines. Side-affects this image. 777 * 778 * <p> 779 * This is a convenience method that calls {@link #createRenderer()} to get 780 * the default renderer to do the actual drawing. Create the renderer 781 * yourself and use it to draw if you need more control. 782 * </p> 783 * 784 * @param s 785 * The shape to draw. 786 * @param thickness 787 * The thickness of the lines to use 788 * @param col 789 * The colour to draw the lines in 790 */ 791 public void drawShape(Shape s, int thickness, Q col) { 792 createRenderer().drawShape(s, thickness, col); 793 } 794 795 /** 796 * Draw the given shape in the specified colour. Uses 797 * {@link #drawPolygon(Polygon, int, Object)} with line thickness 1. 798 * Side-affects 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 shape to draw. 808 * @param col 809 * The colour to draw the polygon in. 810 */ 811 public void drawShape(Shape p, Q col) { 812 createRenderer().drawShape(p, col); 813 } 814 815 /** 816 * Draw the given shape, filled with the specified colour. Side-affects this 817 * 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 col 828 * The colour to fill the polygon with. 829 */ 830 public void drawShapeFilled(Shape s, Q col) { 831 createRenderer().drawShapeFilled(s, col); 832 } 833 834 /** 835 * Render the text using its attributes. 836 * 837 * <p> 838 * This is a convenience method that calls {@link #createRenderer()} to get 839 * the default renderer to do the actual drawing. Create the renderer 840 * yourself and use it to draw if you need more control. 841 * </p> 842 * 843 * @param text 844 * the text 845 * @param x 846 * the x-ordinate 847 * @param y 848 * the y-ordinate 849 */ 850 public void drawText(AttributedString text, int x, int y) { 851 createRenderer().drawText(text, x, y); 852 } 853 854 /** 855 * Render the text using its attributes. 856 * 857 * <p> 858 * This is a convenience method that calls {@link #createRenderer()} to get 859 * the default renderer to do the actual drawing. Create the renderer 860 * yourself and use it to draw if you need more control. 861 * </p> 862 * 863 * @param text 864 * the text 865 * @param pt 866 * the coordinate to render at 867 */ 868 public void drawText(AttributedString text, Point2d pt) { 869 createRenderer().drawText(text, pt); 870 } 871 872 /** 873 * Render the text in the given font with the default style. 874 * 875 * <p> 876 * This is a convenience method that calls {@link #createRenderer()} to get 877 * the default renderer to do the actual drawing. Create the renderer 878 * yourself and use it to draw if you need more control. 879 * </p> 880 * 881 * @param <F> 882 * the font 883 * @param text 884 * the text 885 * @param x 886 * the x-ordinate 887 * @param y 888 * the y-ordinate 889 * @param f 890 * the font 891 * @param sz 892 * the size 893 */ 894 public <F extends Font<F>> void drawText(String text, int x, int y, F f, int sz) { 895 createRenderer().drawText(text, x, y, f, sz); 896 } 897 898 /** 899 * Render the text in the given font in the given colour with the default 900 * style. 901 * 902 * <p> 903 * This is a convenience method that calls {@link #createRenderer()} to get 904 * the default renderer to do the actual drawing. Create the renderer 905 * yourself and use it to draw if you need more control. 906 * </p> 907 * 908 * @param <F> 909 * the font 910 * @param text 911 * the text 912 * @param x 913 * the x-ordinate 914 * @param y 915 * the y-ordinate 916 * @param f 917 * the font 918 * @param sz 919 * the size 920 * @param col 921 * the font color 922 */ 923 public <F extends Font<F>> void drawText(String text, int x, int y, F f, int sz, Q col) { 924 createRenderer().drawText(text, x, y, f, sz, col); 925 } 926 927 /** 928 * Render the text with the given {@link FontStyle}. 929 * 930 * <p> 931 * This is a convenience method that calls {@link #createRenderer()} to get 932 * the default renderer to do the actual drawing. Create the renderer 933 * yourself and use it to draw if you need more control. 934 * </p> 935 * 936 * @param text 937 * the text 938 * @param x 939 * the x-ordinate 940 * @param y 941 * the y-ordinate 942 * @param f 943 * the font style 944 */ 945 public void drawText(String text, int x, int y, FontStyle<Q> f) { 946 createRenderer().drawText(text, x, y, f); 947 } 948 949 /** 950 * Render the text in the given font with the default style. 951 * 952 * <p> 953 * This is a convenience method that calls {@link #createRenderer()} to get 954 * the default renderer to do the actual drawing. Create the renderer 955 * yourself and use it to draw if you need more control. 956 * </p> 957 * 958 * @param <F> 959 * the font 960 * @param text 961 * the text 962 * @param pt 963 * the coordinate to render at 964 * @param f 965 * the font 966 * @param sz 967 * the size 968 */ 969 public <F extends Font<F>> void drawText(String text, Point2d pt, F f, int sz) { 970 createRenderer().drawText(text, pt, f, sz); 971 } 972 973 /** 974 * Render the text in the given font in the given colour with the default 975 * style. 976 * 977 * <p> 978 * This is a convenience method that calls {@link #createRenderer()} to get 979 * the default renderer to do the actual drawing. Create the renderer 980 * yourself and use it to draw if you need more control. 981 * </p> 982 * 983 * @param <F> 984 * the font 985 * @param text 986 * the text 987 * @param pt 988 * the coordinate to render at 989 * @param f 990 * the font 991 * @param sz 992 * the size 993 * @param col 994 * the font colour 995 */ 996 public <F extends Font<F>> void drawText(String text, Point2d pt, F f, int sz, Q col) { 997 createRenderer().drawText(text, pt, f, sz, col); 998 } 999 1000 /** 1001 * Render the text with the given {@link FontStyle}. 1002 * 1003 * <p> 1004 * This is a convenience method that calls {@link #createRenderer()} to get 1005 * the default renderer to do the actual drawing. Create the renderer 1006 * yourself and use it to draw if you need more control. 1007 * </p> 1008 * 1009 * @param text 1010 * the text 1011 * @param pt 1012 * the coordinate to render at 1013 * @param f 1014 * the font style 1015 */ 1016 public void drawText(String text, Point2d pt, FontStyle<Q> f) { 1017 createRenderer().drawText(text, pt, f); 1018 } 1019 1020 /** 1021 * Extract a rectangular region about the centre of the image with the given 1022 * width and height. The method will return a box that extends 1023 * <code>width/2</code> and <code>height/2</code> from the centre point so 1024 * that the centre point of the extracted box is also the centre point of 1025 * the image. 1026 * 1027 * @param w 1028 * The width of the box to extract 1029 * @param h 1030 * The height of the box to extract 1031 * @return A new image centred around the centre of the image. 1032 */ 1033 public I extractCenter(int w, int h) { 1034 final int sw = (int) Math.floor((this.getWidth() - w) / 2); 1035 final int sh = (int) Math.floor((this.getHeight() - h) / 2); 1036 1037 return this.extractROI(sw, sh, w, h); 1038 } 1039 1040 /** 1041 * Extract a rectangular region centred on a given point. The method will 1042 * return a box that extends <code>width/2</code> and <code>height/2</code> 1043 * from the given point <code>(x,y)</code> such that the centre point of the 1044 * extracted box is the same as the point <code>(x,y)</code> in this image. 1045 * 1046 * @param x 1047 * Center point of the rectangle to extract 1048 * @param y 1049 * center point of the rectangle to extract 1050 * @param w 1051 * The width of the rectangle to extract 1052 * @param h 1053 * The height of the rectangle to extract 1054 * @return A new image centred around the centre of the image. 1055 */ 1056 public I extractCenter(int x, int y, int w, int h) { 1057 final int sw = (int) Math.floor(x - (w / 2)); 1058 final int sh = (int) Math.floor(y - (h / 2)); 1059 1060 return this.extractROI(sw, sh, w, h); 1061 } 1062 1063 /** 1064 * Extract a rectangular region of interest from this image and put it in 1065 * the given image. Coordinate <code>(0,0)</code> is the top-left corner. 1066 * The width and height of the extracted image should be determined from the 1067 * given image's width and height. 1068 * 1069 * @param x 1070 * The leftmost coordinate of the rectangle to extract 1071 * @param y 1072 * The topmost coordinate of the rectangle to extract 1073 * @param img 1074 * The destination image 1075 * @return A reference to the destination image containing the result 1076 */ 1077 public abstract I extractROI(int x, int y, I img); 1078 1079 /** 1080 * Extract a rectangular region of interest of the given width and height. 1081 * Coordinate <code>(0,0)</code> is the top-left corner. Returns a new 1082 * image. 1083 * 1084 * @param x 1085 * The leftmost coordinate of the rectangle to extract 1086 * @param y 1087 * The topmost coordinate of the rectangle to extract 1088 * @param w 1089 * The width of the rectangle to extract 1090 * @param h 1091 * The height of the rectangle to extract 1092 * @return A new image representing the selected region 1093 */ 1094 public abstract I extractROI(int x, int y, int w, int h); 1095 1096 /** 1097 * Extract a rectangular region of interest of the given width and height. 1098 * Coordinate <code>(0,0)</code> is the top-left corner. Returns a new 1099 * image. 1100 * 1101 * @param r 1102 * the rectangle 1103 * @return A new image representing the selected region 1104 */ 1105 public I extractROI(Rectangle r) { 1106 return extractROI((int) r.x, (int) r.y, (int) r.width, (int) r.height); 1107 } 1108 1109 /** 1110 * Fill this image with the given colour. Should overwrite all other data 1111 * stored in this image. Side-affects this image. 1112 * 1113 * @param colour 1114 * the colour to fill the image with 1115 * @return A reference to this image. 1116 */ 1117 public abstract I fill(Q colour); 1118 1119 /** 1120 * Flips the content horizontally. Side-affects this image. 1121 * 1122 * @return A reference to this image. 1123 */ 1124 public abstract I flipX(); 1125 1126 /** 1127 * Flips the content vertically. Side-affects this image. 1128 * 1129 * @return A reference to this image. 1130 */ 1131 public abstract I flipY(); 1132 1133 /** 1134 * Get a rectangle representing the image, with the top-left at 0,0 and the 1135 * bottom-right at width,height 1136 * 1137 * @return the bounding rectangle of the image 1138 */ 1139 public Rectangle getBounds() { 1140 return new Rectangle(0, 0, this.getWidth(), this.getHeight()); 1141 } 1142 1143 /** 1144 * Get the image width in pixels. This is syntactic sugar for 1145 * {@link #getWidth()}; 1146 * 1147 * @return The image width in pixels. 1148 */ 1149 public int getCols() { 1150 return getWidth(); 1151 } 1152 1153 /** 1154 * Get bounding box of non-zero-valued pixels around the outside of the 1155 * image. Used by {@link #trim()}. 1156 * 1157 * @return A rectangle of the boundaries of the non-zero-valued image 1158 */ 1159 public abstract Rectangle getContentArea(); 1160 1161 /** 1162 * Get the given field of this image. Used for deinterlacing video, this 1163 * should return a new image containing the deinterlaced image. The returned 1164 * image will be half the height of this image. 1165 * 1166 * @param f 1167 * The {@link Field} to extract from this image 1168 * @return An image containing only the odd or even fields. 1169 */ 1170 public abstract I getField(Field f); 1171 1172 /** 1173 * Get the given field of this image, maintaining the image's aspect ratio 1174 * by doubling the fields. Used for deinterlacing video, this should return 1175 * a new image containing the deinterlaced image. The returned image should 1176 * be the same size as this image. 1177 * 1178 * @param f 1179 * The {@link Field} to extract from this image 1180 * @return An image containing the odd or even fields doubled. 1181 */ 1182 public abstract I getFieldCopy(Field f); 1183 1184 /** 1185 * Get the given field of this image, maintaining the image's aspect ratio 1186 * by interpolating between the fields. Used for deinterlacing video, this 1187 * should return a new image containing the detinterlaced image. The 1188 * returned image should be the same size as this image. 1189 * 1190 * @param f 1191 * The {@link Field} to extract from this image. 1192 * @return An image containing the odd or even fields with interpolated rows 1193 * between. 1194 */ 1195 public abstract I getFieldInterpolate(Field f); 1196 1197 /** 1198 * Returns the image height in pixels. 1199 * 1200 * @return The image height in pixels. 1201 */ 1202 public abstract int getHeight(); 1203 1204 /** 1205 * Get the value of the pixel at coordinate <code>(x, y)</code>. 1206 * 1207 * @param x 1208 * The x-ordinate to get 1209 * @param y 1210 * The y-ordinate to get 1211 * 1212 * @return The pixel value at (x, y) 1213 */ 1214 public abstract Q getPixel(int x, int y); 1215 1216 /** 1217 * Get the value of the pixel at coordinate p 1218 * 1219 * @param p 1220 * The coordinate to get 1221 * 1222 * @return The pixel value at (x, y) 1223 */ 1224 public Q getPixel(Pixel p) { 1225 return getPixel(p.x, p.y); 1226 } 1227 1228 /** 1229 * Returns a pixel comparator that is able to compare equality of pixels in 1230 * the given image type. 1231 * 1232 * @return A {@link Comparator} that compares pixels. 1233 */ 1234 public abstract Comparator<? super Q> getPixelComparator(); 1235 1236 /** 1237 * Get the value of a sub-pixel using linear-interpolation. 1238 * 1239 * @param x 1240 * The x-ordinate to get 1241 * @param y 1242 * The y-ordinate to get 1243 * @return The value of the interpolated point at <code>(x,y)</code> 1244 */ 1245 public abstract Q getPixelInterp(double x, double y); 1246 1247 /** 1248 * Get the value of a sub-pixel using linear-interpolation. Also specify the 1249 * colour of the background (for interpolation at the edge) 1250 * 1251 * @param x 1252 * The x-ordinate to get. 1253 * @param y 1254 * The y-ordinate to get. 1255 * @param backgroundColour 1256 * The colour of the background pixel. 1257 * @return The value of the interpolated point at <code>(x,y)</code> 1258 */ 1259 public abstract Q getPixelInterp(double x, double y, Q backgroundColour); 1260 1261 /** 1262 * Returns the pixels in this image as a vector (an array of the pixel 1263 * type). 1264 * 1265 * @param f 1266 * The array into which to place the data 1267 * @return The pixels in the image as a vector (a reference to the given 1268 * array). 1269 */ 1270 public Q[] getPixelVector(Q[] f) { 1271 for (int y = 0; y < getHeight(); y++) 1272 for (int x = 0; x < getWidth(); x++) 1273 f[x + y * getWidth()] = getPixel(x, y); 1274 1275 return f; 1276 } 1277 1278 /** 1279 * Get the height of this image. This is a syntactic sugar method for 1280 * {@link #getHeight()}. 1281 * 1282 * @return The image height in pixels. 1283 */ 1284 public int getRows() { 1285 return getHeight(); 1286 } 1287 1288 /** 1289 * Get the width (number of columns) in this image. 1290 * 1291 * @return the image width 1292 */ 1293 public abstract int getWidth(); 1294 1295 /** 1296 * Copy the internal state from another image of the same type. This method 1297 * is designed to be FAST. This means that bounds checking WILL NOT be 1298 * performed, so it is important that the images are the SAME size. 1299 * 1300 * @param im 1301 * The source image to make a copy of. 1302 * @return A reference to this image. 1303 */ 1304 public abstract I internalCopy(I im); 1305 1306 /** 1307 * Assign the internal state from another image of the same type. 1308 * 1309 * @param im 1310 * The source image to make a copy of. 1311 * @return A reference to this image. 1312 */ 1313 public abstract I internalAssign(I im); 1314 1315 /** 1316 * Copy pixels from given ARGB buffer image into this image. Side-affects 1317 * this image. 1318 * 1319 * @param pixelData 1320 * buffer of ARGB packed integer pixels 1321 * @param width 1322 * the width of the buffer 1323 * @param height 1324 * the height of the buffer 1325 * 1326 * @return A reference to this image. 1327 */ 1328 public abstract I internalAssign(int[] pixelData, int width, int height); 1329 1330 /** 1331 * Invert the image pixels by finding the maximum value and subtracting each 1332 * pixel value from that maximum. 1333 * 1334 * @return A reference to this image. 1335 */ 1336 public abstract I inverse(); 1337 1338 /** 1339 * Find the maximum pixel value. 1340 * 1341 * @return The maximum pixel value 1342 */ 1343 public abstract Q max(); 1344 1345 /** 1346 * Find the minimum pixel value. 1347 * 1348 * @return The minimum pixel value 1349 */ 1350 public abstract Q min(); 1351 1352 /** 1353 * Multiply the pixel values in this image with the corresponding pixel 1354 * values in the given image. This method returns a new image. 1355 * 1356 * @param im 1357 * The image to multiply with this one 1358 * @return A new image containing the result. 1359 */ 1360 public I multiply(Image<?, ?> im) { 1361 final I newImage = this.clone(); 1362 newImage.multiplyInplace(im); 1363 return newImage; 1364 } 1365 1366 /** 1367 * Multiply each pixel of this by the given scalar and return new image. 1368 * 1369 * @param num 1370 * The scalar which to multiply the image by 1371 * @return A new image containing the result 1372 */ 1373 public I multiply(Q num) { 1374 final I newImage = this.clone(); 1375 newImage.multiplyInplace(num); 1376 return newImage; 1377 } 1378 1379 /** 1380 * Multiply each pixel in this image by the corresponding pixel in the given 1381 * image. This method side-affects this image. 1382 * 1383 * @param im 1384 * The image to multiply with this image. 1385 * @return A reference to this image. 1386 */ 1387 public abstract I multiplyInplace(Image<?, ?> im); 1388 1389 /** 1390 * Multiply each pixel of this by the given scalar. This method side-affects 1391 * this image. 1392 * 1393 * @param num 1394 * The scalar to multiply this image by. 1395 * @return A reference to this image. 1396 */ 1397 public abstract I multiplyInplace(Q num); 1398 1399 /** 1400 * Create a new instance of this image subclass with given dimensions. 1401 * 1402 * @param width 1403 * The image width 1404 * @param height 1405 * The image height 1406 * 1407 * @return A new instance of an image of type <code>I</code> 1408 */ 1409 public abstract I newInstance(int width, int height); 1410 1411 /** 1412 * Normalise all pixel values to fall within the range 0.0 - 1.0. This 1413 * should be scaled by both the maximum and minimum values. This method 1414 * side-affects this image. 1415 * 1416 * @return A reference to this image. 1417 */ 1418 public abstract I normalise(); 1419 1420 /** 1421 * Adds padding as in {@link Image#padding(int, int, Object)}. The padding 1422 * colour is the colour of the closest border pixel. 1423 * 1424 * @param paddingWidth 1425 * padding in the x direction 1426 * @param paddingHeight 1427 * padding in the y direction 1428 * @return padded image 1429 */ 1430 public I padding(int paddingWidth, int paddingHeight) { 1431 return this.padding(paddingWidth, paddingHeight, null); 1432 } 1433 1434 /** 1435 * Adds this many pixels to both sides of the image such that the new image 1436 * width = padding + width + padding with the original image in the middle 1437 * 1438 * @param paddingWidth 1439 * left and right padding width 1440 * @param paddingHeight 1441 * top and bottom padding width 1442 * @param paddingColour 1443 * colour of padding, if null the closes border pixel is used 1444 * @return padded image 1445 */ 1446 @SuppressWarnings("unchecked") 1447 public I padding(int paddingWidth, int paddingHeight, Q paddingColour) { 1448 final I out = this.newInstance(paddingWidth + this.getWidth() + paddingWidth, paddingHeight + this.getHeight() 1449 + paddingHeight); 1450 1451 out.createRenderer().drawImage((I) this, paddingWidth, paddingHeight); 1452 final int rightLimit = paddingWidth + this.getWidth(); 1453 final int bottomLimit = paddingHeight + this.getHeight(); 1454 // Fill the padding with a colour if it isn't null 1455 if (paddingColour != null) 1456 for (int y = 0; y < out.getHeight(); y++) { 1457 for (int x = 0; x < out.getWidth(); x++) { 1458 if (x >= paddingWidth && x < rightLimit && y >= paddingHeight && y < bottomLimit) 1459 continue; 1460 out.setPixel(x, y, paddingColour); 1461 } 1462 } 1463 else 1464 for (int y = 0; y < out.getHeight(); y++) { 1465 for (int x = 0; x < out.getWidth(); x++) { 1466 if (x >= paddingWidth && x < rightLimit && y >= paddingHeight && y < bottomLimit) 1467 continue; 1468 if (x < paddingWidth && y < paddingHeight) 1469 out.setPixel(x, y, this.getPixel(0, 0)); // Top Left 1470 else if (x < paddingWidth && y >= bottomLimit) 1471 out.setPixel(x, y, this.getPixel(0, this.getHeight() - 1)); // Bottom 1472 // Left 1473 else if (x >= rightLimit && y < paddingHeight) 1474 out.setPixel(x, y, this.getPixel(this.getWidth() - 1, 0)); // Top 1475 // Right 1476 else if (x >= rightLimit && y >= bottomLimit) 1477 out.setPixel(x, y, this.getPixel(this.getWidth() - 1, this.getHeight() - 1)); // Bottom 1478 // Right 1479 else { 1480 if (x < paddingWidth) 1481 out.setPixel(x, y, this.getPixel(0, y - paddingHeight)); // Left 1482 else if (x >= rightLimit) 1483 out.setPixel(x, y, this.getPixel(this.getWidth() - 1, y - paddingHeight)); // Right 1484 else if (y < paddingHeight) 1485 out.setPixel(x, y, this.getPixel(x - paddingWidth, 0)); // Top 1486 else if (y >= bottomLimit) 1487 out.setPixel(x, y, this.getPixel(x - paddingWidth, this.getHeight() - 1)); // Bottom 1488 } 1489 } 1490 } 1491 1492 return out; 1493 } 1494 1495 /** 1496 * Adds pixels to around the image such that the new image width = 1497 * paddingLeft + width + paddingRight with the original image in the middle. 1498 * The values of the padding pixels are formed from repeated symmetric 1499 * reflections of the original image. 1500 * 1501 * @param paddingLeft 1502 * left padding width 1503 * @param paddingRight 1504 * right padding width 1505 * @param paddingTop 1506 * top padding width 1507 * @param paddingBottom 1508 * bottom padding width 1509 * @return padded image 1510 */ 1511 public I paddingSymmetric(int paddingLeft, int paddingRight, int paddingTop, int paddingBottom) { 1512 final I out = this.newInstance(paddingLeft + this.getWidth() + paddingRight, paddingTop + this.getHeight() 1513 + paddingBottom); 1514 final I clone = this.clone(); 1515 final I hflip = clone.clone().flipX(); 1516 1517 final ImageRenderer<Q, I> rend = out.createRenderer(); 1518 rend.drawImage(clone, paddingLeft, paddingTop); 1519 1520 // left 1521 for (int i = paddingLeft - this.getWidth(), c = 0; i > -this.getWidth(); i -= this.getWidth(), c++) { 1522 if (c % 2 == 0) { 1523 rend.drawImage(hflip, i, paddingTop); 1524 } else { 1525 rend.drawImage(clone, i, paddingTop); 1526 } 1527 } 1528 1529 // right 1530 for (int i = paddingLeft + this.getWidth(), c = 0; i < paddingLeft + paddingRight + this.getWidth(); i += this 1531 .getWidth(), c++) 1532 { 1533 if (c % 2 == 0) { 1534 rend.drawImage(hflip, i, paddingTop); 1535 } else { 1536 rend.drawImage(clone, i, paddingTop); 1537 } 1538 } 1539 1540 final I centre = out.extractROI(0, paddingTop, paddingLeft + this.getWidth() + paddingRight, this.getHeight()); 1541 final I yflip = centre.clone().flipY(); 1542 1543 // up 1544 for (int i = paddingTop - this.getHeight(), c = 0; i > -this.getHeight(); i -= this.getHeight(), c++) { 1545 if (c % 2 == 0) { 1546 rend.drawImage(yflip, 0, i); 1547 } else { 1548 rend.drawImage(centre, 0, i); 1549 } 1550 } 1551 1552 // down 1553 for (int i = paddingTop + this.getHeight(), c = 0; i < paddingTop + paddingBottom + this.getHeight(); i += this 1554 .getHeight(), c++) 1555 { 1556 if (c % 2 == 0) { 1557 rend.drawImage(yflip, 0, i); 1558 } else { 1559 rend.drawImage(centre, 0, i); 1560 } 1561 } 1562 1563 return out; 1564 } 1565 1566 /** 1567 * Process this image with the given {@link GridProcessor} and return new 1568 * image containing the result. 1569 * 1570 * @param p 1571 * {@link GridProcessor} to apply to this image. 1572 * @return A new image containing the result. 1573 */ 1574 public I process(GridProcessor<Q, I> p) { 1575 final int height = p.getVerticalGridElements(); 1576 final int width = p.getHorizontalGridElements(); 1577 final I newImage = this.newInstance(width, height); 1578 newImage.zero(); 1579 1580 final int gridWidth = getWidth() / width; 1581 final int gridHeight = getHeight() / height; 1582 for (int y = 0; y < height; y++) 1583 for (int x = 0; x < width; x++) 1584 newImage.setPixel(x, y, 1585 p.processGridElement(this.extractROI(gridWidth * x, gridHeight * y, gridWidth, gridHeight))); 1586 1587 return newImage; 1588 } 1589 1590 /** 1591 * Process this image with an {@link ImageProcessor} and return new image 1592 * containing the result. 1593 * 1594 * @param p 1595 * The {@link ImageProcessor} to apply to this image. 1596 * @return A new image containing the result. 1597 */ 1598 public I process(ImageProcessor<I> p) { 1599 final I newImage = this.clone(); 1600 newImage.processInplace(p); 1601 return newImage; 1602 } 1603 1604 /** 1605 * Process this image with the given {@link KernelProcessor} and return new 1606 * image containing the result. 1607 * 1608 * @param p 1609 * The {@link KernelProcessor} to apply. 1610 * @return A new image containing the result. 1611 */ 1612 public I process(KernelProcessor<Q, I> p) { 1613 return process(p, false); 1614 } 1615 1616 /** 1617 * Process this image with the given {@link KernelProcessor} and return new 1618 * image containing the result. 1619 * 1620 * @param p 1621 * The {@link KernelProcessor} to apply. 1622 * @param pad 1623 * Should the image be zero padded so the kernel reaches the 1624 * edges of the output 1625 * @return A new image containing the result. 1626 */ 1627 public I process(KernelProcessor<Q, I> p, boolean pad) { 1628 final I newImage = this.clone(); 1629 newImage.zero(); 1630 1631 final int kh = p.getKernelHeight(); 1632 final int kw = p.getKernelWidth(); 1633 1634 final int hh = p.getKernelHeight() / 2; 1635 final int hw = p.getKernelWidth() / 2; 1636 1637 final I tmp = newInstance(kw, kh); 1638 1639 if (!pad) { 1640 for (int y = hh; y < getHeight() - (kh - hh); y++) { 1641 for (int x = hw; x < getWidth() - (kw - hw); x++) { 1642 newImage.setPixel(x, y, p.processKernel(this.extractROI(x - hw, y - hh, tmp))); 1643 } 1644 } 1645 } else { 1646 for (int y = 0; y < getHeight(); y++) { 1647 for (int x = 0; x < getWidth(); x++) { 1648 newImage.setPixel(x, y, p.processKernel(this.extractROI(x - hw, y - hh, tmp))); 1649 } 1650 } 1651 } 1652 1653 return newImage; 1654 } 1655 1656 /** 1657 * Process this image with the given {@link PixelProcessor} and return a new 1658 * image containing the result. 1659 * 1660 * @param p 1661 * The {@link PixelProcessor} to apply. 1662 * @return A new image containing the result. 1663 */ 1664 public I process(PixelProcessor<Q> p) { 1665 final I newImage = this.clone(); 1666 newImage.processInplace(p); 1667 return newImage; 1668 } 1669 1670 /** 1671 * Process this image with an {@link Processor} and return new image 1672 * containing the result. 1673 * 1674 * @param p 1675 * The {@link Processor} to apply to this image. 1676 * @return A new image containing the result. 1677 */ 1678 public I process(Processor<I> p) { 1679 final I newImage = this.clone(); 1680 newImage.processInplace(p); 1681 return newImage; 1682 } 1683 1684 /** 1685 * Process this image with the given {@link Processor} side-affecting this 1686 * image. 1687 * 1688 * @param p 1689 * The {@link Processor} to apply. 1690 * @return A reference to this image containing the result. 1691 */ 1692 @SuppressWarnings("unchecked") 1693 public I processInplace(Processor<I> p) { 1694 if (p == null) 1695 return (I) this; 1696 if (p instanceof ImageProcessor) 1697 return processInplace((ImageProcessor<I>) p); 1698 if (p instanceof KernelProcessor) 1699 return processInplace((KernelProcessor<Q, I>) p); 1700 if (p instanceof PixelProcessor) 1701 return processInplace((PixelProcessor<Q>) p); 1702 1703 throw new UnsupportedOperationException("Unsupported Processor type"); 1704 } 1705 1706 /** 1707 * Process this image with the given {@link ImageProcessor} side-affecting 1708 * this image. 1709 * 1710 * @param p 1711 * The {@link ImageProcessor} to apply. 1712 * @return A reference to this image containing the result. 1713 */ 1714 @SuppressWarnings("unchecked") 1715 public I processInplace(ImageProcessor<I> p) { 1716 p.processImage((I) this); 1717 return (I) this; 1718 } 1719 1720 /** 1721 * Process this image with the given {@link KernelProcessor} side-affecting 1722 * this image. 1723 * 1724 * @param p 1725 * The {@link KernelProcessor} to apply. 1726 * @return A reference to this image containing the result. 1727 */ 1728 public I processInplace(KernelProcessor<Q, I> p) { 1729 return processInplace(p, false); 1730 } 1731 1732 /** 1733 * Process this image with the given {@link KernelProcessor} side-affecting 1734 * this image. 1735 * 1736 * @param p 1737 * The {@link KernelProcessor} to apply. 1738 * @param pad 1739 * Should the image be zero padded so the kernel reaches the 1740 * edges of the output 1741 * @return A reference to this image containing the result. 1742 */ 1743 @SuppressWarnings("unchecked") 1744 public I processInplace(KernelProcessor<Q, I> p, boolean pad) { 1745 final I newImage = process(p, pad); 1746 this.internalAssign(newImage); 1747 return (I) this; 1748 } 1749 1750 /** 1751 * Process this image with the given {@link PixelProcessor} side-affecting 1752 * this image. 1753 * 1754 * @param p 1755 * The {@link PixelProcessor} to apply. 1756 * @return A reference to this image containing the result. 1757 */ 1758 @SuppressWarnings("unchecked") 1759 public I processInplace(PixelProcessor<Q> p) { 1760 for (int y = 0; y < getHeight(); y++) { 1761 for (int x = 0; x < getWidth(); x++) { 1762 setPixel(x, y, p.processPixel(getPixel(x, y))); 1763 } 1764 } 1765 1766 return (I) this; 1767 } 1768 1769 /** 1770 * Process this image with the given {@link PixelProcessor} only affecting 1771 * those pixels where the mask is non-zero. Returns a new image. 1772 * 1773 * @param mask 1774 * The mask to apply to the processing. 1775 * @param p 1776 * The {@link PixelProcessor} to apply. 1777 * @return A new image containing the result. 1778 */ 1779 public I processMasked(FImage mask, PixelProcessor<Q> p) { 1780 final I newImage = this.clone(); 1781 newImage.processMaskedInplace(mask, p); 1782 return newImage; 1783 } 1784 1785 /** 1786 * Process this image with the given {@link PixelProcessor}, only affecting 1787 * those pixels where the mask is non-zero. Side-affects this image. 1788 * 1789 * @param mask 1790 * The mask to apply to the processor. 1791 * @param p 1792 * The {@link PixelProcessor} to apply. 1793 * @return A reference to this image containing the result. 1794 */ 1795 @SuppressWarnings("unchecked") 1796 public I processMaskedInplace(FImage mask, PixelProcessor<Q> p) { 1797 for (int y = 0; y < getHeight(); y++) { 1798 for (int x = 0; x < getWidth(); x++) { 1799 if (mask.pixels[y][x] == 0) 1800 continue; 1801 setPixel(x, y, p.processPixel(getPixel(x, y))); 1802 } 1803 } 1804 return (I) this; 1805 } 1806 1807 /** 1808 * Sets the pixel at <code>(x,y)</code> to the given value. Side-affects 1809 * this image. 1810 * 1811 * @param x 1812 * The x-ordinate of the pixel to set 1813 * @param y 1814 * The y-ordinate of the pixel to set 1815 * @param val 1816 * The value to set the pixel to. 1817 */ 1818 public abstract void setPixel(int x, int y, Q val); 1819 1820 /** 1821 * Subtract the corresponding pixel value from the given image from the 1822 * pixel values in this image. Returns a new image. 1823 * 1824 * @param im 1825 * The image to subtract from this image. 1826 * @return A new image containing the result. 1827 */ 1828 public I subtract(Image<?, ?> im) { 1829 final I newImage = this.clone(); 1830 newImage.subtractInplace(im); 1831 return newImage; 1832 } 1833 1834 /** 1835 * Subtract a scalar from every pixel value in this image and return new 1836 * image. 1837 * 1838 * @param num 1839 * A value to subtract from each pixel. 1840 * @return A new image containing the result. 1841 */ 1842 public I subtract(Q num) { 1843 final I newImage = this.clone(); 1844 newImage.subtractInplace(num); 1845 return newImage; 1846 } 1847 1848 /** 1849 * Subtract the corresponding pixel value from the given image from the 1850 * pixel values in this image. Side-affects this image. 1851 * 1852 * @param im 1853 * The image to subtract from this image. 1854 * @return A reference to this containing the result. 1855 */ 1856 public abstract I subtractInplace(Image<?, ?> im); 1857 1858 /** 1859 * Subtract a scalar from every pixel value in this image. Side-affects this 1860 * image. 1861 * 1862 * @param num 1863 * A value to subtract from each pixel. 1864 * @return A reference to this image containing the result. 1865 */ 1866 public abstract I subtractInplace(Q num); 1867 1868 /** 1869 * Set all values less than the given threshold to 0 and all others to 1. 1870 * Side-affects this image. 1871 * 1872 * @param thresh 1873 * The threshold value 1874 * @return A reference to this image containing the result. 1875 */ 1876 public abstract I threshold(Q thresh); 1877 1878 /** 1879 * Convert the image to a byte representation suitable for writing to a pnm 1880 * type format. Each byte should represent a single pixel. Multiband images 1881 * should interleave the data; e.g. [R1,G1,B1,R2,G2,B2...etc.] 1882 * 1883 * @return This image as a byte array 1884 */ 1885 public abstract byte[] toByteImage(); 1886 1887 /** 1888 * Returns a 1D array representation of this image with each pixel 1889 * represented as a packed ARGB integer. 1890 * 1891 * @return An array of ARGB pixels. 1892 */ 1893 public abstract int[] toPackedARGBPixels(); 1894 1895 /** 1896 * Apply a transform matrix to the image and returns the result as a new 1897 * image. 1898 * 1899 * @param transform 1900 * The transform matrix to apply. 1901 * @return A new image containing the result. 1902 */ 1903 public I transform(Matrix transform) { 1904 boolean unset = true; 1905 double minX = 0, minY = 0, maxX = 0, maxY = 0; 1906 final double[][][] extrema = new double[][][] { 1907 { { 0 }, { 0 }, { 1 } }, 1908 { { 0 }, { this.getHeight() }, { 1 } }, 1909 { { this.getWidth() }, { 0 }, { 1 } }, 1910 { { this.getWidth() }, { this.getHeight() }, { 1 } }, 1911 }; 1912 for (final double[][] ext : extrema) { 1913 final Matrix tmp = transform.times(Matrix.constructWithCopy(ext)); 1914 if (unset) { 1915 minX = maxX = tmp.get(0, 0); 1916 maxY = minY = tmp.get(1, 0); 1917 unset = false; 1918 } else { 1919 if (tmp.get(0, 0) > maxX) 1920 maxX = tmp.get(0, 0); 1921 if (tmp.get(1, 0) > maxY) 1922 maxY = tmp.get(1, 0); 1923 if (tmp.get(0, 0) < minX) 1924 minX = tmp.get(0, 0); 1925 if (tmp.get(1, 0) < minY) 1926 minY = tmp.get(1, 0); 1927 } 1928 } 1929 final I output = this.newInstance((int) (Math.abs(maxX - minX)), (int) (Math.abs(maxY - minY))); 1930 final Matrix invTrans = transform.inverse(); 1931 final double[][] invTransData = invTrans.getArray(); 1932 1933 for (int x = 0; x < output.getWidth(); x++) { 1934 for (int y = 0; y < output.getHeight(); y++) { 1935 double oldx = invTransData[0][0] * x + invTransData[0][1] * y + invTransData[0][2]; 1936 double oldy = invTransData[1][0] * x + invTransData[1][1] * y + invTransData[1][2]; 1937 final double norm = invTransData[2][0] * x + invTransData[2][1] * y + invTransData[2][2]; 1938 1939 oldx /= norm; 1940 oldy /= norm; 1941 1942 if (oldx < 0 || oldx >= this.getWidth() || oldy < 0 || oldy >= this.getHeight()) 1943 continue; 1944 1945 output.setPixel(x, y, this.getPixelInterp(oldx, oldy)); 1946 } 1947 } 1948 return output; 1949 } 1950 1951 /** 1952 * Removes zero-valued pixels from around the outside of the image. 1953 * Analagous to {@link String#trim()}. 1954 * 1955 * @return A new image containing the trimmed image. 1956 */ 1957 public I trim() { 1958 final Rectangle rect = this.getContentArea(); 1959 return this.extractROI((int) rect.minX(), (int) rect.minY(), (int) (rect.getWidth()), (int) (rect.getHeight())); 1960 } 1961 1962 /** 1963 * Set all pixels in the image to zero. Side-affects this image. 1964 * 1965 * @return A reference to this image containing the result. 1966 */ 1967 public abstract I zero(); 1968 1969 /** 1970 * Shifts all the pixels to the left by one pixel 1971 * 1972 * @return A reference to this image. 1973 */ 1974 public I shiftLeftInplace() { 1975 return shiftLeftInplace(1); 1976 } 1977 1978 /** 1979 * Shifts all the pixels to the right by one pixel 1980 * 1981 * @return A reference to this image. 1982 */ 1983 public I shiftRightInplace() { 1984 return shiftRightInplace(1); 1985 } 1986 1987 /** 1988 * Shifts all the pixels to the left by count pixel 1989 * 1990 * @param count 1991 * The number of pixels 1992 * 1993 * @return A reference to this image. 1994 */ 1995 public I shiftLeftInplace(int count) { 1996 return this.internalAssign(this.shiftLeft(count)); 1997 } 1998 1999 /** 2000 * Shifts all the pixels to the right by count pixel 2001 * 2002 * @param count 2003 * The number of pixels 2004 * 2005 * @return A reference to this image. 2006 */ 2007 public I shiftRightInplace(int count) { 2008 return this.internalAssign(this.shiftRight(count)); 2009 } 2010 2011 /** 2012 * Returns a new image that is it shifted around the x-ordinates by one 2013 * pixel 2014 * 2015 * @return A new image shifted around to the left by one pixel 2016 */ 2017 public I shiftLeft() { 2018 return shiftLeft(1); 2019 } 2020 2021 /** 2022 * Returns a new image that is it shifted around the x-ordinates by the 2023 * number of pixels given. 2024 * 2025 * @param nPixels 2026 * The number of pixels 2027 * 2028 * @return A new image shifted around to the left by the number of pixels 2029 */ 2030 public I shiftLeft(int nPixels) { 2031 final I output = this.newInstance(getWidth(), getHeight()); 2032 final I img = this.extractROI(0, 0, nPixels, getHeight()); 2033 output.createRenderer().drawImage( 2034 this.extractROI(nPixels, 0, getWidth() - nPixels, getHeight()), 0, 0); 2035 output.createRenderer().drawImage(img, getWidth() - nPixels, 0); 2036 return output; 2037 } 2038 2039 /** 2040 * Returns a new image that is it shifted around the x-ordinates by one 2041 * pixel 2042 * 2043 * @return A new image shifted around to the right by one pixel 2044 */ 2045 public I shiftRight() { 2046 return shiftRight(1); 2047 } 2048 2049 /** 2050 * Returns a new image that is it shifted around the x-ordinates by the 2051 * number of pixels given. 2052 * 2053 * @param nPixels 2054 * the number of pixels 2055 * 2056 * @return A new image shifted around to the right by the number of pixels 2057 */ 2058 public I shiftRight(int nPixels) { 2059 final I output = this.newInstance(getWidth(), getHeight()); 2060 final I img = this.extractROI(getWidth() - nPixels, 0, nPixels, getHeight()); 2061 output.createRenderer().drawImage( 2062 this.extractROI(0, 0, getWidth() - nPixels, getHeight()), nPixels, 0); 2063 output.createRenderer().drawImage(img, 0, 0); 2064 return output; 2065 } 2066 2067 /** 2068 * Shifts all the pixels up by one pixel 2069 * 2070 * @return A reference to this image. 2071 */ 2072 public I shiftUpInplace() { 2073 return shiftUpInplace(1); 2074 } 2075 2076 /** 2077 * Shifts all the pixels down by one pixels 2078 * 2079 * @return A reference to this image. 2080 */ 2081 public I shiftDownInplace() { 2082 return shiftDownInplace(1); 2083 } 2084 2085 /** 2086 * Shifts all the pixels up by count pixels 2087 * 2088 * @param count 2089 * The number of pixels 2090 * 2091 * @return A reference to this image. 2092 */ 2093 public I shiftUpInplace(int count) { 2094 return this.internalAssign(this.shiftUp(count)); 2095 } 2096 2097 /** 2098 * Shifts all the pixels down by count pixels 2099 * 2100 * @param count 2101 * The number of pixels 2102 * 2103 * @return A reference to this image. 2104 */ 2105 public I shiftDownInplace(int count) { 2106 return this.internalAssign(this.shiftDown(count)); 2107 } 2108 2109 /** 2110 * Returns a new image that is it shifted around the x-ordinates by one 2111 * pixel 2112 * 2113 * @return A new image shifted around up by one pixel 2114 */ 2115 public I shiftUp() { 2116 return shiftUp(1); 2117 } 2118 2119 /** 2120 * Returns a new image that is it shifted around the x-ordinates by the 2121 * number of pixels given. 2122 * 2123 * @param nPixels 2124 * The number of pixels 2125 * 2126 * @return A new image shifted around up by the number of pixels 2127 */ 2128 public I shiftUp(int nPixels) { 2129 final I output = this.newInstance(getWidth(), getHeight()); 2130 final I img = this.extractROI(0, 0, getWidth(), nPixels); 2131 output.createRenderer().drawImage( 2132 this.extractROI(0, nPixels, getWidth(), getHeight() - nPixels), 0, 0); 2133 output.createRenderer().drawImage(img, 0, getHeight() - nPixels); 2134 return output; 2135 } 2136 2137 /** 2138 * Returns a new image that is it shifted around the x-ordinates by one 2139 * pixel 2140 * 2141 * @return A new image shifted around down by one pixel 2142 */ 2143 public I shiftDown() { 2144 return shiftDown(1); 2145 } 2146 2147 /** 2148 * Returns a new image that is it shifted around the x-ordinates by the 2149 * number of pixels given. 2150 * 2151 * @param nPixels 2152 * the number of pixels 2153 * 2154 * @return A new image shifted around down by the number of pixels 2155 */ 2156 public I shiftDown(int nPixels) { 2157 final I output = this.newInstance(getWidth(), getHeight()); 2158 final I img = this.extractROI(0, getHeight() - nPixels, getWidth(), nPixels); 2159 output.createRenderer().drawImage( 2160 this.extractROI(0, 0, getWidth(), getHeight() - nPixels), 0, nPixels); 2161 output.createRenderer().drawImage(img, 0, 0); 2162 return output; 2163 } 2164 2165 /** 2166 * Overlays the given image on this image and returns a new image containing 2167 * the result. 2168 * 2169 * @param image 2170 * The image to overlay on this image. 2171 * @param x 2172 * The location at which to overlay the image 2173 * @param y 2174 * The location at which to overlay the image 2175 * @return A new image containing the result 2176 */ 2177 public I overlay(I image, int x, int y) { 2178 final I img = this.clone(); 2179 img.overlayInplace(image, x, y); 2180 return img; 2181 } 2182 2183 /** 2184 * Overlays the given image on this image directly. The method returns this 2185 * image for chaining. 2186 * 2187 * @param image 2188 * The image to overlay 2189 * @param x 2190 * The location at which to overlay the image 2191 * @param y 2192 * The location at which to overlay the image 2193 * @return Returns this image 2194 */ 2195 public abstract I overlayInplace(I image, int x, int y); 2196 2197 @SuppressWarnings("unchecked") 2198 @Override 2199 public I getImage() { 2200 return (I) this; 2201 } 2202 2203 /** 2204 * Replace pixels of a certain colour with another colour. Side-affects this 2205 * image. 2206 * 2207 * @param target 2208 * the colour to fill the image with 2209 * @param replacement 2210 * the colour to fill the image with 2211 * @return A reference to this image. 2212 */ 2213 public abstract I replace(Q target, Q replacement); 2214 2215 /** 2216 * Sub-pixel sampling of a centred rectangular region such that 2217 * <code>dst(x, y) = src(x + center.x (width(dst) 1) ⇤ 0.5, y + center.y (height(dst) 1) ⇤ 0.5)</code> 2218 * . Sub-pixels values are estimated using bilinear interpolation. 2219 * 2220 * @see #getPixelInterp(double, double) 2221 * 2222 * @param centre 2223 * the centre 2224 * @param width 2225 * the region width 2226 * @param height 2227 * the region height 2228 * @return the extracted sub-pixel region 2229 */ 2230 public I extractCentreSubPix(Point2d centre, int width, int height) { 2231 return extractCentreSubPix(centre.getX(), centre.getY(), width, height); 2232 } 2233 2234 /** 2235 * Sub-pixel sampling of a centred rectangular region such that 2236 * <code>dst(x, y) = src(x + center.x (width(dst) 1) ⇤ 0.5, y + center.y (height(dst) 1) ⇤ 0.5)</code> 2237 * . Sub-pixels values are estimated using bilinear interpolation. 2238 * 2239 * @see #getPixelInterp(double, double) 2240 * @param cx 2241 * the x-ordinate of the centre 2242 * @param cy 2243 * the y-ordinate of the centre 2244 * @param width 2245 * the region width 2246 * @param height 2247 * the region height 2248 * @return the extracted sub-pixel region 2249 */ 2250 public I extractCentreSubPix(float cx, float cy, int width, int height) { 2251 final I out = newInstance(width, height); 2252 return extractCentreSubPix(cx, cy, out); 2253 } 2254 2255 /** 2256 * Sub-pixel sampling of a centred rectangular region such that 2257 * <code>dst(x, y) = src(x + center.x (width(dst) 1) ⇤ 0.5, y + center.y (height(dst) 1) ⇤ 0.5)</code> 2258 * . Sub-pixels values are estimated using bilinear interpolation. 2259 * 2260 * @see #getPixelInterp(double, double) 2261 * 2262 * @param centre 2263 * the centre 2264 * @param out 2265 * the output image (also defines the size of the extracted 2266 * region) 2267 * @return <code>out</code> 2268 */ 2269 public I extractCentreSubPix(Point2d centre, I out) { 2270 return extractCentreSubPix(centre.getX(), centre.getY(), out); 2271 } 2272 2273 /** 2274 * Sub-pixel sampling of a centred rectangular region such that 2275 * <code>dst(x, y) = src(x + center.x (width(dst) 1) ⇤ 0.5, y + center.y (height(dst) 1) ⇤ 0.5)</code> 2276 * . Sub-pixels values are estimated using bilinear interpolation. 2277 * 2278 * @see #getPixelInterp(double, double) 2279 * @param cx 2280 * the x-ordinate of the centre 2281 * @param cy 2282 * the y-ordinate of the centre 2283 * @param out 2284 * the output image (also defines the size of the extracted 2285 * region) 2286 * @return <code>out</code> 2287 */ 2288 public abstract I extractCentreSubPix(float cx, float cy, I out); 2289}