001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.image; 031 032import java.util.ArrayList; 033import java.util.Arrays; 034import java.util.Iterator; 035import java.util.List; 036 037import org.openimaj.image.colour.ColourSpace; 038import org.openimaj.image.processor.SinglebandImageProcessor; 039import org.openimaj.image.processor.SinglebandKernelProcessor; 040import org.openimaj.image.processor.SinglebandPixelProcessor; 041import org.openimaj.math.geometry.shape.Rectangle; 042 043/** 044 * A base class for multi-band images. 045 * 046 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 047 * 048 * @param <T> 049 * The pixel type 050 * @param <I> 051 * The concrete subclass type 052 * @param <S> 053 * The concrete subclass type of each band 054 */ 055public abstract class MultiBandImage<T extends Comparable<T>, I extends MultiBandImage<T, I, S>, S extends SingleBandImage<T, S>> 056 extends 057 Image<T[], I> 058 implements 059 Iterable<S>, 060 SinglebandImageProcessor.Processable<T, S, I>, 061 SinglebandKernelProcessor.Processable<T, S, I> 062 063{ 064 private static final long serialVersionUID = 1L; 065 066 /** The images for each band in a list */ 067 public List<S> bands; 068 069 /** The colour-space of this image */ 070 public ColourSpace colourSpace = ColourSpace.CUSTOM; 071 072 /** 073 * Default constructor for a multiband image. 074 */ 075 public MultiBandImage() { 076 this.bands = new ArrayList<S>(); 077 } 078 079 /** 080 * Default constructor for a multiband image. 081 * 082 * @param colourSpace 083 * the colour space 084 */ 085 public MultiBandImage(final ColourSpace colourSpace) { 086 this(); 087 this.colourSpace = colourSpace; 088 } 089 090 /** 091 * Construct a multiband image using each of the given images as the bands 092 * (in order). 093 * 094 * @param colourSpace 095 * the colour space 096 * @param images 097 * A set of images to use as the bands in the image. 098 */ 099 public MultiBandImage(final ColourSpace colourSpace, final S... images) { 100 this(colourSpace); 101 102 if (!ImageUtilities.checkSameSize(images)) { 103 throw new IllegalArgumentException("images are not the same size"); 104 } 105 106 this.bands.addAll(Arrays.asList(images)); 107 } 108 109 /** 110 * {@inheritDoc} 111 * 112 * @see org.openimaj.image.Image#abs() 113 */ 114 @SuppressWarnings("unchecked") 115 @Override 116 public I abs() { 117 for (final S i : this.bands) 118 i.abs(); 119 return (I) this; 120 } 121 122 /** 123 * Add the given scalar to each pixel of each band and return result as a 124 * new image. 125 * 126 * @param num 127 * The value to add to each pixel in every band. 128 * @return A new image containing the result. 129 */ 130 public I add(final T num) { 131 final I newImage = this.clone(); 132 newImage.add(num); 133 return newImage; 134 } 135 136 /** 137 * Adds a new band image to the multiband image. The given image must be the 138 * same size as the images already in this image. 139 * 140 * @param img 141 * The image to add as a new band. 142 */ 143 public void addBand(final S img) { 144 if (this.bands.size() > 0) { 145 if (!ImageUtilities.checkSize(this.getHeight(), this.getWidth(), img)) { 146 throw new IllegalArgumentException("images are not the same size"); 147 } 148 } 149 this.bands.add(img); 150 } 151 152 /** 153 * {@inheritDoc} The input image must be a {@link MultiBandImage} or a 154 * {@link SingleBandImage}. 155 * 156 * @see org.openimaj.image.Image#addInplace(org.openimaj.image.Image) 157 * @throws UnsupportedOperationException 158 * if the given image is neither a {@link MultiBandImage} nor a 159 * {@link SingleBandImage}. 160 */ 161 @Override 162 public I addInplace(final Image<?, ?> im) { 163 if (im instanceof MultiBandImage<?, ?, ?>) { 164 return this.addInplace((MultiBandImage<?, ?, ?>) im); 165 } else if (im instanceof SingleBandImage<?, ?>) { 166 return this.addInplace((SingleBandImage<?, ?>) im); 167 } else { 168 throw new UnsupportedOperationException("Unsupported Type"); 169 } 170 } 171 172 /** 173 * Adds to each pixel the value of the corresponding pixel in the 174 * corresponding band in the given image. Side-affects this image. 175 * 176 * @param im 177 * The image to add to this image. 178 * @return A reference to this image containing the result. 179 */ 180 @SuppressWarnings("unchecked") 181 public I addInplace(final MultiBandImage<?, ?, ?> im) { 182 assert (ImageUtilities.checkSameSize(this, im)); 183 184 final int np = this.bands.size(); 185 186 for (int i = 0; i < np; i++) 187 this.bands.get(i).addInplace(((MultiBandImage<?, ?, ?>) im).bands.get(i)); 188 189 return (I) this; 190 } 191 192 /** 193 * Adds to each pixel (in all bandS) the value of corresponding pixel in the 194 * given image. Side-affects this image. 195 * 196 * @param im 197 * The image to add to this image. 198 * @return A reference to this image containing the result. 199 */ 200 @SuppressWarnings("unchecked") 201 public I addInplace(final SingleBandImage<?, ?> im) { 202 assert (ImageUtilities.checkSameSize(this, im)); 203 204 final int np = this.bands.size(); 205 206 for (int i = 0; i < np; i++) 207 this.bands.get(i).addInplace(im); 208 209 return (I) this; 210 } 211 212 /** 213 * Add the given value to each pixel in every band. Side-affects this image. 214 * 215 * @param num 216 * The value to add to each pixel 217 * @return A reference to this image containing the result. 218 */ 219 @SuppressWarnings("unchecked") 220 public I addInplace(final T num) { 221 for (final S sbi : this) { 222 sbi.addInplace(num); 223 } 224 225 return (I) this; 226 } 227 228 /** 229 * {@inheritDoc} 230 * 231 * @see org.openimaj.image.Image#addInplace(java.lang.Object) 232 */ 233 @SuppressWarnings("unchecked") 234 @Override 235 public I addInplace(final T[] num) { 236 final int np = this.bands.size(); 237 238 assert (num.length == np); 239 240 for (int i = 0; i < np; i++) 241 this.bands.get(i).addInplace(num[i]); 242 243 return (I) this; 244 } 245 246 /** 247 * Sets any pixels that are below min to zero or above max to the highest 248 * normal value that the image allows (usually 1 for floating-point images). 249 * This method may side-affect this image. 250 * 251 * @param min 252 * The minimum value to clip to 253 * @param max 254 * The maximum value to clip to. 255 * @return this 256 * @see Image#clip(Object, Object) 257 */ 258 @SuppressWarnings("unchecked") 259 public I clip(final T min, final T max) { 260 for (final S sbi : this) { 261 sbi.clip(min, max); 262 } 263 264 return (I) this; 265 } 266 267 /** 268 * {@inheritDoc} 269 * 270 * @see org.openimaj.image.Image#clip(java.lang.Object, java.lang.Object) 271 */ 272 @SuppressWarnings("unchecked") 273 @Override 274 public I clip(final T[] min, final T[] max) { 275 final int np = this.bands.size(); 276 277 assert (min.length == np); 278 assert (max.length == np); 279 280 for (int i = 0; i < np; i++) 281 this.bands.get(i).clip(min[i], max[i]); 282 283 return (I) this; 284 } 285 286 /** 287 * For all bands, sets any values above the given threshold to zero. 288 * Side-affects this image. 289 * 290 * @param thresh 291 * The threshold above which values are clipped 292 * @return A reference to this image containing the result. 293 */ 294 @SuppressWarnings("unchecked") 295 public I clipMax(final T thresh) { 296 for (final S sbm : this) 297 sbm.clipMax(thresh); 298 299 return (I) this; 300 } 301 302 /** 303 * {@inheritDoc} 304 * 305 * @see org.openimaj.image.Image#clipMax(java.lang.Object) 306 */ 307 @SuppressWarnings("unchecked") 308 @Override 309 public I clipMax(final T[] thresh) { 310 final int np = this.bands.size(); 311 312 assert (thresh.length == np); 313 314 for (int i = 0; i < np; i++) 315 this.bands.get(i).clipMax(thresh[i]); 316 317 return (I) this; 318 } 319 320 /** 321 * Sets all pixels in all bands that have a value below the given threshold 322 * to zero. Side-affects this image. 323 * 324 * @param thresh 325 * The threshold below which pixels will be set to zero. 326 * @return A reference to this image containing the result. 327 */ 328 @SuppressWarnings("unchecked") 329 public I clipMin(final T thresh) { 330 for (final S sbm : this) 331 sbm.clipMin(thresh); 332 333 return (I) this; 334 } 335 336 /** 337 * {@inheritDoc} 338 * 339 * @see org.openimaj.image.Image#clipMin(java.lang.Object) 340 */ 341 @SuppressWarnings("unchecked") 342 @Override 343 public I clipMin(final T[] thresh) { 344 final int np = this.bands.size(); 345 346 assert (thresh.length == np); 347 348 for (int i = 0; i < np; i++) 349 this.bands.get(i).clipMin(thresh[i]); 350 351 return (I) this; 352 } 353 354 /** 355 * {@inheritDoc} 356 * 357 * @see org.openimaj.image.Image#clone() 358 */ 359 @Override 360 public I clone() { 361 final I newImage = this.newInstance(); 362 363 for (final S sbi : this) { 364 newImage.bands.add(sbi.clone()); 365 } 366 newImage.colourSpace = this.colourSpace; 367 368 return newImage; 369 } 370 371 /** 372 * Delete the band at the given index. 373 * 374 * @param index 375 * The index of the band to remove. 376 */ 377 public void deleteBand(final int index) { 378 this.bands.remove(index); 379 } 380 381 /** 382 * Divides all pixels of each band by the given value and returns result as 383 * a new image. 384 * 385 * @param val 386 * The value to divide every pixel by. 387 * @return A new image containing the result. 388 */ 389 public I divide(final T val) { 390 final I newImage = this.clone(); 391 newImage.divide(val); 392 return newImage; 393 } 394 395 /** 396 * {@inheritDoc} 397 * 398 * @see org.openimaj.image.Image#divideInplace(org.openimaj.image.Image) 399 */ 400 @Override 401 public I divideInplace(final Image<?, ?> im) { 402 if (im instanceof MultiBandImage<?, ?, ?>) { 403 return this.divideInplace((MultiBandImage<?, ?, ?>) im); 404 } else if (im instanceof SingleBandImage<?, ?>) { 405 return this.divideInplace((SingleBandImage<?, ?>) im); 406 } else { 407 throw new UnsupportedOperationException("Unsupported Type"); 408 } 409 } 410 411 /** 412 * Divides the pixels in every band of this image by the corresponding pixel 413 * in the corresponding band of the given image. Side-affects this image. 414 * 415 * @param im 416 * The image to divide into this image. 417 * @return A reference to this image containing the result. 418 */ 419 @SuppressWarnings("unchecked") 420 public I divideInplace(final MultiBandImage<?, ?, ?> im) { 421 assert (ImageUtilities.checkSameSize(this, im)); 422 423 final int np = this.bands.size(); 424 425 for (int i = 0; i < np; i++) 426 this.bands.get(i).divideInplace(((MultiBandImage<?, ?, ?>) im).bands.get(i)); 427 428 return (I) this; 429 } 430 431 /** 432 * Divides the pixels in every band of this image by the corresponding pixel 433 * in the given image. Side-affects this image. 434 * 435 * @param im 436 * The image to divide into this image. 437 * @return A reference to this image containing the result. 438 */ 439 @SuppressWarnings("unchecked") 440 public I divideInplace(final SingleBandImage<?, ?> im) { 441 assert (ImageUtilities.checkSameSize(this, im)); 442 443 final int np = this.bands.size(); 444 445 for (int i = 0; i < np; i++) 446 this.bands.get(i).divideInplace(im); 447 448 return (I) this; 449 } 450 451 /** 452 * Divide all pixels of every band by the given value. Side-affects this 453 * image. 454 * 455 * @param val 456 * The value to divide by 457 * @return A reference to this image containing the result. 458 */ 459 @SuppressWarnings("unchecked") 460 public I divideInplace(final T val) { 461 for (final S sbm : this) { 462 sbm.divideInplace(val); 463 } 464 return (I) this; 465 } 466 467 /** 468 * {@inheritDoc} 469 * 470 * @see org.openimaj.image.Image#divideInplace(java.lang.Object) 471 */ 472 @SuppressWarnings("unchecked") 473 @Override 474 public I divideInplace(final T[] val) { 475 final int np = this.bands.size(); 476 477 assert (val.length == np); 478 479 for (int i = 0; i < np; i++) 480 this.bands.get(i).divideInplace(val[i]); 481 482 return (I) this; 483 } 484 485 /** 486 * {@inheritDoc} 487 * 488 * @see org.openimaj.image.Image#extractROI(int, int, 489 * org.openimaj.image.Image) 490 */ 491 @Override 492 public I extractROI(final int x, final int y, final I out) { 493 for (int i = 0; i < this.bands.size(); i++) { 494 final S img = this.bands.get(i); 495 img.extractROI(x, y, out.bands.get(i)); 496 } 497 498 return out; 499 } 500 501 /** 502 * {@inheritDoc} 503 * 504 * @see org.openimaj.image.Image#extractROI(int, int, int, int) 505 */ 506 @Override 507 public I extractROI(final int x, final int y, final int w, final int h) { 508 final I newImage = this.newInstance(); 509 510 for (final S sbm : this) { 511 newImage.addBand(sbm.extractROI(x, y, w, h)); 512 } 513 return newImage; 514 } 515 516 /** 517 * {@inheritDoc} 518 * 519 * @see org.openimaj.image.Image#fill(java.lang.Object) 520 */ 521 @SuppressWarnings("unchecked") 522 @Override 523 public I fill(final T[] colour) { 524 for (int b = 0; b < this.bands.size(); b++) 525 this.bands.get(b).fill(colour[b]); 526 return (I) this; 527 } 528 529 /** 530 * Flatten the bands into a single band using the average value of the 531 * pixels at each location. 532 * 533 * @return A new single-band image containing the result. 534 */ 535 public S flatten() { 536 if (this.bands.size() == 1) 537 return bands.get(0).clone(); 538 539 final S out = this.newBandInstance(this.getWidth(), this.getHeight()); 540 541 for (final S sbm : this) 542 out.addInplace(sbm); 543 544 return out.divideInplace(this.intToT(this.numBands())); 545 } 546 547 /** 548 * Flatten the bands into a single band by selecting the maximum value pixel 549 * from each band. 550 * 551 * @return A new flattened image 552 */ 553 public abstract S flattenMax(); 554 555 /** 556 * Get the band at index i. 557 * 558 * @param i 559 * the index 560 * @return the specified colour band 561 */ 562 public S getBand(final int i) { 563 return this.bands.get(i); 564 } 565 566 /** 567 * Get the colour space of this image 568 * 569 * @return the colour space 570 */ 571 public ColourSpace getColourSpace() { 572 return this.colourSpace; 573 } 574 575 /** 576 * {@inheritDoc} 577 * 578 * @see org.openimaj.image.Image#getContentArea() 579 */ 580 @Override 581 public Rectangle getContentArea() { 582 int minx = this.getWidth(), maxx = 0, miny = this.getHeight(), maxy = 0; 583 for (int i = 0; i < this.numBands(); i++) { 584 final Rectangle box = this.getBand(i).getContentArea(); 585 if (box.minX() < minx) 586 minx = (int) box.minX(); 587 if (box.maxX() > maxx) 588 maxx = (int) box.maxX(); 589 if (box.minY() < miny) 590 miny = (int) box.minY(); 591 if (box.maxY() > maxy) 592 maxy = (int) box.maxY(); 593 } 594 595 return new Rectangle(minx, miny, maxx - minx, maxy - miny); 596 } 597 598 /** 599 * {@inheritDoc} 600 * 601 * @see org.openimaj.image.Image#getField(org.openimaj.image.Image.Field) 602 */ 603 @Override 604 public I getField(final Field f) { 605 final I newImage = this.newInstance(); 606 607 for (final S sbm : this) { 608 newImage.bands.add(sbm.getField(f)); 609 } 610 return newImage; 611 } 612 613 /** 614 * {@inheritDoc} 615 * 616 * @see org.openimaj.image.Image#getFieldCopy(org.openimaj.image.Image.Field) 617 */ 618 @Override 619 public I getFieldCopy(final Field f) { 620 final I newImage = this.newInstance(); 621 622 for (final S sbm : this) { 623 newImage.bands.add(sbm.getFieldCopy(f)); 624 } 625 return newImage; 626 } 627 628 /** 629 * {@inheritDoc} 630 * 631 * @see org.openimaj.image.Image#getFieldInterpolate(org.openimaj.image.Image.Field) 632 */ 633 @Override 634 public I getFieldInterpolate(final Field f) { 635 final I newImage = this.newInstance(); 636 637 for (final S sbm : this) { 638 newImage.bands.add(sbm.getFieldInterpolate(f)); 639 } 640 641 return newImage; 642 } 643 644 /** 645 * {@inheritDoc} 646 * 647 * @see org.openimaj.image.Image#getHeight() 648 */ 649 @Override 650 public int getHeight() { 651 if (this.bands.size() > 0) 652 return this.bands.get(0).getHeight(); 653 return 0; 654 } 655 656 /** 657 * {@inheritDoc} 658 * 659 * @see org.openimaj.image.Image#getWidth() 660 */ 661 @Override 662 public int getWidth() { 663 if (this.bands.size() > 0) 664 return this.bands.get(0).getWidth(); 665 return 0; 666 } 667 668 /** 669 * {@inheritDoc} 670 * 671 * @see org.openimaj.image.Image#internalAssign(org.openimaj.image.Image) 672 */ 673 @SuppressWarnings("unchecked") 674 @Override 675 public I internalCopy(final I im) 676 { 677 final int nb = this.bands.size(); 678 for (int i = 0; i < nb; i++) 679 this.bands.get(i).internalCopy(im.getBand(i)); 680 681 return (I) this; 682 } 683 684 /** 685 * {@inheritDoc} 686 * 687 * @see org.openimaj.image.Image#internalAssign(org.openimaj.image.Image) 688 */ 689 @SuppressWarnings("unchecked") 690 @Override 691 public I internalAssign(final I im) { 692 this.bands = im.bands; 693 return (I) this; 694 } 695 696 /** 697 * Converts the given integer to a value that can be used as a pixel value. 698 * 699 * @param n 700 * The integer to convert. 701 * @return A value that can be used as a pixel value. 702 */ 703 protected abstract T intToT(int n); 704 705 /** 706 * {@inheritDoc} 707 * 708 * @see org.openimaj.image.Image#inverse() 709 */ 710 @SuppressWarnings("unchecked") 711 @Override 712 public I inverse() { 713 for (final S sbm : this) { 714 sbm.inverse(); 715 } 716 return (I) this; 717 } 718 719 /** 720 * {@inheritDoc} 721 * 722 * @see java.lang.Iterable#iterator() 723 */ 724 @Override 725 public Iterator<S> iterator() { 726 return this.bands.iterator(); 727 } 728 729 /** 730 * {@inheritDoc} 731 * 732 * @see org.openimaj.image.Image#max() 733 */ 734 @Override 735 public T[] max() { 736 final List<T> pixels = new ArrayList<T>(); 737 738 for (final S sbm : this) { 739 pixels.add(sbm.max()); 740 } 741 742 return pixels.toArray(createPixelArray(this.numBands())); 743 } 744 745 /** 746 * {@inheritDoc} 747 * 748 * @see org.openimaj.image.Image#min() 749 */ 750 @Override 751 public T[] min() { 752 final List<T> pixels = new ArrayList<T>(); 753 754 for (final S sbm : this) { 755 pixels.add(sbm.min()); 756 } 757 758 return pixels.toArray(createPixelArray(this.numBands())); 759 } 760 761 /** 762 * Create an array of n pixels 763 * 764 * @param n 765 * number of pixels 766 * @return the array 767 */ 768 protected abstract T[] createPixelArray(int n); 769 770 /** 771 * Multiplies each pixel of every band by the given value and returns the 772 * result as a new image. 773 * 774 * @param num 775 * The value to multiply by. 776 * @return A new image containing the result. 777 */ 778 public I multiply(final T num) { 779 final I newImage = this.clone(); 780 newImage.multiplyInplace(num); 781 return newImage; 782 } 783 784 /** 785 * {@inheritDoc} 786 * 787 * @see org.openimaj.image.Image#multiplyInplace(org.openimaj.image.Image) 788 */ 789 @Override 790 public I multiplyInplace(final Image<?, ?> im) { 791 if (im instanceof MultiBandImage<?, ?, ?>) { 792 return this.multiplyInplace((MultiBandImage<?, ?, ?>) im); 793 } else if (im instanceof SingleBandImage<?, ?>) { 794 return this.multiplyInplace((SingleBandImage<?, ?>) im); 795 } else { 796 throw new UnsupportedOperationException("Unsupported Type"); 797 } 798 } 799 800 /** 801 * Multiplies every pixel in this image by the corresponding pixel in the 802 * corresponding band in the given image. Side-affects this image. 803 * 804 * @param im 805 * The image to multiply with this image. 806 * @return A reference to this image containing the result. 807 */ 808 @SuppressWarnings("unchecked") 809 public I multiplyInplace(final MultiBandImage<?, ?, ?> im) { 810 assert (ImageUtilities.checkSameSize(this, im)); 811 812 final int np = this.bands.size(); 813 814 for (int i = 0; i < np; i++) 815 this.bands.get(i).multiplyInplace(((MultiBandImage<?, ?, ?>) im).bands.get(i)); 816 817 return (I) this; 818 } 819 820 /** 821 * Multiplies every pixel in this image by the corresponding pixel in the 822 * given image. Side-affects this image. 823 * 824 * @param im 825 * The image to multiply with this image. 826 * @return A reference to this image containing the result. 827 */ 828 @SuppressWarnings("unchecked") 829 public I multiplyInplace(final SingleBandImage<?, ?> im) { 830 assert (ImageUtilities.checkSameSize(this, im)); 831 832 final int np = this.bands.size(); 833 834 for (int i = 0; i < np; i++) 835 this.bands.get(i).multiplyInplace(im); 836 837 return (I) this; 838 } 839 840 /** 841 * Multiplies each pixel of every band by the given value. Side-affects this 842 * image. 843 * 844 * @param num 845 * The value to multiply this image by 846 * @return A reference to this image containing the result. 847 */ 848 @SuppressWarnings("unchecked") 849 public I multiplyInplace(final T num) { 850 for (final S sbm : this) 851 sbm.multiplyInplace(num); 852 853 return (I) this; 854 } 855 856 /** 857 * {@inheritDoc} 858 * 859 * @see org.openimaj.image.Image#multiplyInplace(java.lang.Object) 860 */ 861 @SuppressWarnings("unchecked") 862 @Override 863 public I multiplyInplace(final T[] num) { 864 final int np = this.bands.size(); 865 866 assert (num.length == np); 867 868 for (int i = 0; i < np; i++) 869 this.bands.get(i).multiplyInplace(num[i]); 870 871 return (I) this; 872 } 873 874 /** 875 * Returns a new instance of an image that represents each band. 876 * 877 * @param width 878 * The width of the image 879 * @param height 880 * The height of the image 881 * @return A new {@link SingleBandImage} of the appropriate type. 882 */ 883 public abstract S newBandInstance(int width, int height); 884 885 /** 886 * Returns a new instance of a this image type. 887 * 888 * @return A new {@link MBFImage} subclass type. 889 */ 890 public abstract I newInstance(); 891 892 /** 893 * {@inheritDoc} 894 * 895 * @see org.openimaj.image.Image#newInstance(int, int) 896 */ 897 @Override 898 public abstract I newInstance(int width, int height); 899 900 /** 901 * {@inheritDoc} 902 * 903 * @see org.openimaj.image.Image#normalise() 904 */ 905 @SuppressWarnings("unchecked") 906 @Override 907 public I normalise() { 908 for (final S sbm : this) 909 sbm.normalise(); 910 911 return (I) this; 912 } 913 914 /** 915 * Returns the number of bands in this image. 916 * 917 * @return the number of bands in this image. 918 */ 919 public int numBands() { 920 return this.bands.size(); 921 } 922 923 /** 924 * {@inheritDoc} 925 * 926 * @see org.openimaj.image.processor.SinglebandImageProcessor.Processable#process(org.openimaj.image.processor.SinglebandImageProcessor) 927 */ 928 @Override 929 public I process(final SinglebandImageProcessor<T, S> p) { 930 final I out = this.newInstance(); 931 for (final S sbm : this) 932 out.bands.add(sbm.process(p)); 933 934 return out; 935 } 936 937 /** 938 * {@inheritDoc} 939 * 940 * @see org.openimaj.image.processor.SinglebandKernelProcessor.Processable#process(org.openimaj.image.processor.SinglebandKernelProcessor) 941 */ 942 @Override 943 public I process(final SinglebandKernelProcessor<T, S> kernel) { 944 return this.process(kernel, false); 945 } 946 947 /** 948 * {@inheritDoc} 949 * 950 * @see org.openimaj.image.processor.SinglebandKernelProcessor.Processable#process(org.openimaj.image.processor.SinglebandKernelProcessor, 951 * boolean) 952 */ 953 @Override 954 public I process(final SinglebandKernelProcessor<T, S> kernel, final boolean pad) { 955 final I out = this.newInstance(); 956 for (final S sbm : this) 957 out.bands.add(sbm.process(kernel, pad)); 958 959 return out; 960 } 961 962 /** 963 * Processes this image with the given {@link SinglebandImageProcessor} for 964 * every band. 965 * 966 * @param pp 967 * The pixel process to apply to each band in turn. 968 * @return A new image containing the result. 969 */ 970 public I process(final SinglebandPixelProcessor<T> pp) { 971 final I out = this.newInstance(); 972 for (final S sbm : this) 973 out.bands.add(sbm.process(pp)); 974 975 return out; 976 } 977 978 /** 979 * {@inheritDoc} 980 * 981 * @see org.openimaj.image.processor.SinglebandImageProcessor.Processable#processInplace(org.openimaj.image.processor.SinglebandImageProcessor) 982 */ 983 @Override 984 @SuppressWarnings("unchecked") 985 public I processInplace(final SinglebandImageProcessor<T, S> p) { 986 for (final S sbm : this) 987 sbm.processInplace(p); 988 989 return (I) this; 990 } 991 992 /** 993 * {@inheritDoc} 994 * 995 * @see org.openimaj.image.processor.SinglebandKernelProcessor.Processable#processInplace(org.openimaj.image.processor.SinglebandKernelProcessor) 996 */ 997 @Override 998 public I processInplace(final SinglebandKernelProcessor<T, S> kernel) { 999 return this.processInplace(kernel, false); 1000 } 1001 1002 /** 1003 * {@inheritDoc} 1004 * 1005 * @see org.openimaj.image.processor.SinglebandKernelProcessor.Processable#processInplace(org.openimaj.image.processor.SinglebandKernelProcessor, 1006 * boolean) 1007 */ 1008 @Override 1009 @SuppressWarnings("unchecked") 1010 public I processInplace(final SinglebandKernelProcessor<T, S> kernel, final boolean pad) { 1011 for (final S sbm : this) 1012 sbm.processInplace(kernel, pad); 1013 1014 return (I) this; 1015 } 1016 1017 /** 1018 * Process this image with the given {@link SinglebandImageProcessor} for 1019 * every band. Side-affects this image. 1020 * 1021 * @param pp 1022 * The pixel processor to apply to each band in turn. 1023 * @return A reference to this image containing the result. 1024 */ 1025 @SuppressWarnings("unchecked") 1026 public I processInplace(final SinglebandPixelProcessor<T> pp) { 1027 for (final S sbm : this) 1028 sbm.processInplace(pp); 1029 1030 return (I) this; 1031 } 1032 1033 /** 1034 * {@inheritDoc} 1035 * 1036 * @see org.openimaj.image.Image#setPixel(int, int, java.lang.Object) 1037 */ 1038 @Override 1039 public void setPixel(final int x, final int y, final T[] val) { 1040 final int np = this.bands.size(); 1041 if (np == val.length) 1042 for (int i = 0; i < np; i++) 1043 this.bands.get(i).setPixel(x, y, val[i]); 1044 else { 1045 final int offset = val.length - np; 1046 for (int i = 0; i < np; i++) 1047 if (i + offset >= 0) 1048 this.bands.get(i).setPixel(x, y, val[i + offset]); 1049 } 1050 } 1051 1052 /** 1053 * Subtracts the given value from every pixel in every band and returns the 1054 * result as a new image. 1055 * 1056 * @param num 1057 * The value to subtract from this image. 1058 * @return A new image containing the result. 1059 */ 1060 public I subtract(final T num) { 1061 final I newImage = this.clone(); 1062 newImage.subtract(num); 1063 return newImage; 1064 } 1065 1066 /** 1067 * {@inheritDoc} 1068 * 1069 * @see org.openimaj.image.Image#subtractInplace(org.openimaj.image.Image) 1070 */ 1071 @Override 1072 public I subtractInplace(final Image<?, ?> im) { 1073 if (im instanceof MultiBandImage<?, ?, ?>) { 1074 return this.subtractInplace((MultiBandImage<?, ?, ?>) im); 1075 } else if (im instanceof SingleBandImage<?, ?>) { 1076 return this.subtractInplace((SingleBandImage<?, ?>) im); 1077 } else { 1078 throw new UnsupportedOperationException("Unsupported Type"); 1079 } 1080 } 1081 1082 /** 1083 * Subtracts from every pixel in every band the corresponding pixel value in 1084 * the corresponding band of the given image. Side-affects this image. 1085 * 1086 * @param im 1087 * The image to subtract from this image 1088 * @return A reference to this image containing the result. 1089 */ 1090 @SuppressWarnings("unchecked") 1091 public I subtractInplace(final MultiBandImage<?, ?, ?> im) { 1092 assert (ImageUtilities.checkSameSize(this, im)); 1093 1094 final int np = this.bands.size(); 1095 1096 for (int i = 0; i < np; i++) 1097 this.bands.get(i).subtractInplace(((MultiBandImage<?, ?, ?>) im).bands.get(i)); 1098 1099 return (I) this; 1100 } 1101 1102 /** 1103 * Subtracts from every pixel in every band the corresponding pixel value in 1104 * the given image. Side-affects this image. 1105 * 1106 * @param im 1107 * The image to subtract from this image. 1108 * @return A reference to this image containing the result. 1109 */ 1110 @SuppressWarnings("unchecked") 1111 public I subtractInplace(final SingleBandImage<?, ?> im) { 1112 assert (ImageUtilities.checkSameSize(this, im)); 1113 1114 final int np = this.bands.size(); 1115 1116 for (int i = 0; i < np; i++) 1117 this.bands.get(i).subtractInplace(im); 1118 1119 return (I) this; 1120 } 1121 1122 /** 1123 * Subtracts the given value from every pixel in every band. Side-affects 1124 * this image. 1125 * 1126 * @param num 1127 * The value to subtract from this image 1128 * @return A reference to this image containing the result. 1129 */ 1130 @SuppressWarnings("unchecked") 1131 public I subtractInplace(final T num) { 1132 for (final S sbm : this) 1133 sbm.subtractInplace(num); 1134 1135 return (I) this; 1136 } 1137 1138 /** 1139 * {@inheritDoc} 1140 * 1141 * @see org.openimaj.image.Image#subtractInplace(java.lang.Object) 1142 */ 1143 @SuppressWarnings("unchecked") 1144 @Override 1145 public I subtractInplace(final T[] num) { 1146 final int np = this.bands.size(); 1147 1148 assert (num.length == np); 1149 1150 for (int i = 0; i < np; i++) 1151 this.bands.get(i).subtractInplace(num[i]); 1152 1153 return (I) this; 1154 } 1155 1156 /** 1157 * Sets the value of any pixel below the given threshold to zero and all 1158 * others to 1 for all bands. Side-affects this image. 1159 * 1160 * @param thresh 1161 * The threshold above which pixels will be set to 1. 1162 * @return A reference to this image containing the result. 1163 */ 1164 @SuppressWarnings("unchecked") 1165 public I threshold(final T thresh) { 1166 for (final S sbm : this) 1167 sbm.threshold(thresh); 1168 1169 return (I) this; 1170 } 1171 1172 /** 1173 * {@inheritDoc} 1174 * 1175 * @see org.openimaj.image.Image#threshold(java.lang.Object) 1176 */ 1177 @SuppressWarnings("unchecked") 1178 @Override 1179 public I threshold(final T[] thresh) { 1180 final int np = this.bands.size(); 1181 1182 assert (thresh.length == np); 1183 1184 for (int i = 0; i < np; i++) 1185 this.bands.get(i).threshold(thresh[i]); 1186 1187 return (I) this; 1188 } 1189 1190 /** 1191 * {@inheritDoc} 1192 * 1193 * @see org.openimaj.image.Image#toByteImage() 1194 */ 1195 @Override 1196 public byte[] toByteImage() { 1197 final int width = this.getWidth(); 1198 final int height = this.getHeight(); 1199 final int nb = this.bands.size(); 1200 1201 final byte[] ppmData = new byte[nb * height * width]; 1202 1203 for (int n = 0; n < nb; n++) { 1204 final byte[] band = this.bands.get(n).toByteImage(); 1205 1206 for (int j = 0; j < height; j++) { 1207 for (int i = 0; i < width; i++) { 1208 ppmData[nb * (i + j * width) + n] = band[i + j * width]; 1209 } 1210 } 1211 } 1212 return ppmData; 1213 } 1214 1215 /** 1216 * {@inheritDoc} 1217 * 1218 * @see org.openimaj.image.Image#toPackedARGBPixels() 1219 */ 1220 @Override 1221 public int[] toPackedARGBPixels() { 1222 // TODO: deal better with color spaces 1223 if (this.bands.size() == 1) { 1224 return this.bands.get(0).toPackedARGBPixels(); 1225 } else if (this.bands.size() == 3) { 1226 final int width = this.getWidth(); 1227 final int height = this.getHeight(); 1228 1229 final byte[] rp = this.bands.get(0).toByteImage(); 1230 final byte[] gp = this.bands.get(1).toByteImage(); 1231 final byte[] bp = this.bands.get(2).toByteImage(); 1232 1233 final int[] data = new int[height * width]; 1234 1235 for (int r = 0; r < height; r++) { 1236 for (int c = 0; c < width; c++) { 1237 final int red = rp[c + r * width] & 0xff; 1238 final int green = gp[c + r * width] & 0xff; 1239 final int blue = bp[c + r * width] & 0xff; 1240 1241 final int rgb = 0xff << 24 | red << 16 | green << 8 | blue; 1242 data[c + r * width] = rgb; 1243 } 1244 } 1245 1246 return data; 1247 } else if (this.bands.size() == 4) { 1248 final int width = this.getWidth(); 1249 final int height = this.getHeight(); 1250 1251 final byte[] ap = this.bands.get(3).toByteImage(); 1252 final byte[] rp = this.bands.get(0).toByteImage(); 1253 final byte[] gp = this.bands.get(1).toByteImage(); 1254 final byte[] bp = this.bands.get(2).toByteImage(); 1255 1256 final int[] data = new int[height * width]; 1257 1258 for (int r = 0; r < height; r++) { 1259 for (int c = 0; c < width; c++) { 1260 final int alpha = ap[c + r * width] & 0xff; 1261 final int red = rp[c + r * width] & 0xff; 1262 final int green = gp[c + r * width] & 0xff; 1263 final int blue = bp[c + r * width] & 0xff; 1264 1265 final int argb = alpha << 24 | red << 16 | green << 8 | blue; 1266 data[c + r * width] = argb; 1267 } 1268 } 1269 1270 return data; 1271 } else { 1272 throw new UnsupportedOperationException( 1273 "Unable to create bufferedImage with " + this.numBands() + " bands"); 1274 } 1275 } 1276 1277 /** 1278 * {@inheritDoc} 1279 * 1280 * @see org.openimaj.image.Image#zero() 1281 */ 1282 @SuppressWarnings("unchecked") 1283 @Override 1284 public I zero() { 1285 for (final S sbm : this) 1286 sbm.zero(); 1287 1288 return (I) this; 1289 } 1290 1291 @SuppressWarnings("unchecked") 1292 @Override 1293 public I shiftLeftInplace(final int count) { 1294 for (final S b : this.bands) 1295 b.shiftLeftInplace(count); 1296 return (I) this; 1297 } 1298 1299 @SuppressWarnings("unchecked") 1300 @Override 1301 public I shiftRightInplace(final int count) { 1302 for (final S b : this.bands) 1303 b.shiftRightInplace(count); 1304 return (I) this; 1305 } 1306 1307 @SuppressWarnings("unchecked") 1308 @Override 1309 public I flipX() { 1310 for (final S b : this.bands) 1311 b.flipX(); 1312 1313 return (I) this; 1314 } 1315 1316 @SuppressWarnings("unchecked") 1317 @Override 1318 public I flipY() { 1319 for (final S b : this.bands) 1320 b.flipY(); 1321 1322 return (I) this; 1323 } 1324 1325 @Override 1326 @SuppressWarnings("unchecked") 1327 public boolean equals(final Object other) { 1328 final I that = (I) other; 1329 if (this.bands.size() != that.bands.size()) 1330 return false; 1331 int i = 0; 1332 for (final S b : this.bands) 1333 { 1334 final boolean fail = !b.equals(that.getBand(i)); 1335 if (fail) 1336 return false; 1337 1338 i++; 1339 } 1340 1341 return true; 1342 } 1343 1344 @SuppressWarnings("unchecked") 1345 @Override 1346 public I replace(final T[] target, final T[] replacement) { 1347 for (int b = 0; b < this.bands.size(); b++) 1348 this.bands.get(b).replace(target[b], replacement[b]); 1349 return (I) this; 1350 } 1351 1352 @Override 1353 public I extractCentreSubPix(float cx, float cy, I out) { 1354 for (int b = 0; b < this.bands.size(); b++) 1355 this.bands.get(b).extractCentreSubPix(cx, cy, out.bands.get(b)); 1356 return out; 1357 } 1358}