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.Comparator; 033 034import org.apache.log4j.Logger; 035import org.openimaj.image.analyser.PixelAnalyser; 036import org.openimaj.image.colour.ColourSpace; 037import org.openimaj.image.pixel.FValuePixel; 038import org.openimaj.image.pixel.Pixel; 039import org.openimaj.image.processor.KernelProcessor; 040import org.openimaj.image.processor.PixelProcessor; 041import org.openimaj.image.renderer.FImageRenderer; 042import org.openimaj.image.renderer.RenderHints; 043import org.openimaj.math.geometry.shape.Rectangle; 044import org.openimaj.math.util.Interpolation; 045 046import Jama.Matrix; 047 048/** 049 * Class representing a single-band floating-point image; that is an image where 050 * each pixel is represented by a floating-point number. 051 * <p> 052 * {@link FImage}s can be created from PGM files or from pixel arrays. If you 053 * wish to read other types of files then use the {@link ImageUtilities} class 054 * that provides read/write functions for {@link Image} objects. 055 * 056 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 057 */ 058public class FImage extends SingleBandImage<Float, FImage> 059{ 060 private static final long serialVersionUID = 1L; 061 062 /** The logging class */ 063 protected static Logger logger = Logger.getLogger(FImage.class); 064 065 /** 066 * The default number of sigmas at which the Gaussian function is truncated 067 * when building a kernel 068 */ 069 protected static final float DEFAULT_GAUSS_TRUNCATE = 4.0f; 070 071 /** The underlying pixels */ 072 public float pixels[][]; 073 074 /** 075 * Create an {@link FImage} from an array of floating point values with the 076 * given width and height. The length of the array must equal the width 077 * multiplied by the height. 078 * 079 * @param array 080 * An array of floating point values. 081 * @param width 082 * The width of the resulting image. 083 * @param height 084 * The height of th resulting image. 085 */ 086 public FImage(final float[] array, final int width, final int height) 087 { 088 assert (array.length == width * height); 089 090 this.pixels = new float[height][width]; 091 this.height = height; 092 this.width = width; 093 094 for (int y = 0; y < height; y++) 095 for (int x = 0; x < width; x++) 096 this.pixels[y][x] = array[y * width + x]; 097 } 098 099 /** 100 * Create an {@link FImage} from an array of double values with the given 101 * width and height. The length of the array must equal the width multiplied 102 * by the height. The values will be downcast to floats. 103 * 104 * @param array 105 * An array of floating point values. 106 * @param width 107 * The width of the resulting image. 108 * @param height 109 * The height of th resulting image. 110 */ 111 public FImage(final double[] array, final int width, final int height) 112 { 113 assert (array.length == width * height); 114 115 this.pixels = new float[height][width]; 116 this.height = height; 117 this.width = width; 118 119 for (int y = 0; y < height; y++) 120 for (int x = 0; x < width; x++) 121 this.pixels[y][x] = (float) array[y * width + x]; 122 } 123 124 /** 125 * Create an {@link FImage} from an array of double values with the given 126 * width and height. The length of the array must equal the width multiplied 127 * by the height. The values will be downcast to floats. 128 * 129 * @param array 130 * An array of floating point values. 131 * @param width 132 * The width of the resulting image. 133 * @param height 134 * The height of the resulting image. 135 * @param offset 136 * The offset in the array to begin reading from 137 */ 138 public FImage(final double[] array, final int width, final int height, int offset) 139 { 140 assert (array.length == width * height); 141 142 this.pixels = new float[height][width]; 143 this.height = height; 144 this.width = width; 145 146 for (int y = 0; y < height; y++) 147 for (int x = 0; x < width; x++) 148 this.pixels[y][x] = (float) array[offset + y * width + x]; 149 } 150 151 /** 152 * Create an {@link FImage} from an array of floating point values. 153 * 154 * @param array 155 * the array representing pixel values to copy data from. 156 */ 157 public FImage(final float[][] array) 158 { 159 this.pixels = array; 160 this.height = array.length; 161 this.width = array[0].length; 162 } 163 164 /** 165 * Create an empty {@link FImage} of the given size. 166 * 167 * @param width 168 * image width (number of columns) 169 * @param height 170 * image height (number of rows) 171 */ 172 public FImage(final int width, final int height) { 173 this.pixels = new float[height][width]; 174 175 this.height = height; 176 this.width = width; 177 } 178 179 /** 180 * Construct an {@link FImage} from an array of packed ARGB integers. 181 * 182 * @param data 183 * array of packed ARGB pixels 184 * @param width 185 * the image width 186 * @param height 187 * the image height 188 */ 189 public FImage(final int[] data, final int width, final int height) { 190 this.internalAssign(data, width, height); 191 } 192 193 /** 194 * Construct an {@link FImage} from an array of packed ARGB integers using 195 * the specified plane. 196 * 197 * @param data 198 * array of packed ARGB pixels 199 * @param width 200 * the image width 201 * @param height 202 * the image height 203 * @param plane 204 * The {@link ARGBPlane} to copy data from 205 */ 206 public FImage(final int[] data, final int width, final int height, final ARGBPlane plane) { 207 this.width = width; 208 this.height = height; 209 this.pixels = new float[height][width]; 210 211 for (int y = 0; y < height; y++) { 212 for (int x = 0; x < width; x++) { 213 final int rgb = data[x + y * width]; 214 215 int colour = 0; 216 switch (plane) 217 { 218 case RED: 219 colour = ((rgb >> 16) & 0xff); 220 break; 221 case GREEN: 222 colour = ((rgb >> 8) & 0xff); 223 break; 224 case BLUE: 225 colour = ((rgb) & 0xff); 226 break; 227 default: 228 break; 229 } 230 231 this.pixels[y][x] = colour; 232 } 233 } 234 } 235 236 /** 237 * {@inheritDoc} 238 * 239 * @see org.openimaj.image.Image#abs() 240 */ 241 @Override 242 public FImage abs() { 243 for (int r = 0; r < this.height; r++) 244 for (int c = 0; c < this.width; c++) 245 this.pixels[r][c] = Math.abs(this.pixels[r][c]); 246 return this; 247 } 248 249 /** 250 * Adds the pixel values of the given {@link FImage} to the pixels of this 251 * image. Returns a new {@link FImage} and does not affect this image or the 252 * given image. This is a version of {@link Image#add(Image)} which takes an 253 * {@link FImage}. This method directly accesses the underlying float[][] 254 * and is therefore fast. This function returns a new {@link FImage}. 255 * 256 * @see org.openimaj.image.Image#add(Image) 257 * @param im 258 * {@link FImage} to add into this one. 259 * @return A new {@link FImage} 260 */ 261 public FImage add(final FImage im) 262 { 263 if (!ImageUtilities.checkSameSize(this, im)) 264 throw new AssertionError("images must be the same size"); 265 266 final FImage newImage = new FImage(im.width, im.height); 267 268 for (int r = 0; r < im.height; r++) 269 for (int c = 0; c < im.width; c++) 270 newImage.pixels[r][c] = this.pixels[r][c] + im.pixels[r][c]; 271 272 return newImage; 273 } 274 275 /** 276 * Returns a new {@link FImage} that contains the pixels of this image 277 * increased by the given value. {@inheritDoc} 278 * 279 * @see org.openimaj.image.Image#add(java.lang.Object) 280 */ 281 @Override 282 public FImage add(final Float num) 283 { 284 final FImage newImage = new FImage(this.width, this.height); 285 final float fnum = num; 286 287 for (int r = 0; r < this.height; r++) 288 for (int c = 0; c < this.width; c++) 289 newImage.pixels[r][c] = this.pixels[r][c] + fnum; 290 291 return newImage; 292 } 293 294 /** 295 * {@inheritDoc} This method throws an {@link UnsupportedOperationException} 296 * if the given image is not an {@link FImage}. 297 * 298 * @see org.openimaj.image.Image#add(org.openimaj.image.Image) 299 * @exception UnsupportedOperationException 300 * if an unsupported type is added 301 * @return a reference to this {@link FImage} 302 */ 303 @Override 304 public FImage add(final Image<?, ?> im) 305 { 306 if (im instanceof FImage) 307 return this.add((FImage) im); 308 else 309 throw new UnsupportedOperationException("Unsupported Type"); 310 } 311 312 /*** 313 * Adds the given image pixel values to the pixel values of this image. 314 * Version of {@link Image#addInplace(Image)} which takes an {@link FImage}. 315 * This directly accesses the underlying float[][] and is therefore fast. 316 * This function side-affects the pixels in this {@link FImage}. 317 * 318 * @see Image#addInplace(Image) 319 * @param im 320 * the FImage to add 321 * @return a reference to this 322 */ 323 public FImage addInplace(final FImage im) 324 { 325 if (!ImageUtilities.checkSameSize(this, im)) 326 throw new AssertionError("images must be the same size"); 327 328 for (int r = 0; r < im.height; r++) 329 for (int c = 0; c < im.width; c++) 330 this.pixels[r][c] += im.pixels[r][c]; 331 332 return this; 333 } 334 335 /** 336 * {@inheritDoc} 337 * 338 * @see org.openimaj.image.Image#addInplace(java.lang.Object) 339 */ 340 @Override 341 public FImage addInplace(final Float num) 342 { 343 final float fnum = num; 344 for (int r = 0; r < this.height; r++) 345 for (int c = 0; c < this.width; c++) 346 this.pixels[r][c] += fnum; 347 348 return this; 349 } 350 351 /** 352 * {@inheritDoc} This method throws an {@link UnsupportedOperationException} 353 * if the given image is not an {@link FImage}. 354 * 355 * @see org.openimaj.image.Image#addInplace(org.openimaj.image.Image) 356 * @exception UnsupportedOperationException 357 * if an unsupported type is added 358 * @return a reference to this {@link FImage} 359 */ 360 @Override 361 public FImage addInplace(final Image<?, ?> im) 362 { 363 if (im instanceof FImage) 364 return this.addInplace((FImage) im); 365 else 366 throw new UnsupportedOperationException("Unsupported Type"); 367 } 368 369 /** 370 * {@inheritDoc} 371 * 372 * @see org.openimaj.image.Image#clip(java.lang.Object, java.lang.Object) 373 */ 374 @Override 375 public FImage clip(final Float min, final Float max) 376 { 377 int r, c; 378 379 for (r = 0; r < this.height; r++) 380 { 381 for (c = 0; c < this.width; c++) 382 { 383 if (this.pixels[r][c] < min) 384 this.pixels[r][c] = 0; 385 if (this.pixels[r][c] > max) 386 this.pixels[r][c] = 1; 387 } 388 } 389 390 return this; 391 } 392 393 /** 394 * {@inheritDoc} 395 * 396 * @see org.openimaj.image.Image#clipMax(java.lang.Object) 397 */ 398 @Override 399 public FImage clipMax(final Float thresh) 400 { 401 final float fthresh = thresh; 402 for (int r = 0; r < this.height; r++) 403 { 404 for (int c = 0; c < this.width; c++) 405 { 406 if (this.pixels[r][c] > fthresh) 407 this.pixels[r][c] = 1; 408 } 409 } 410 return this; 411 } 412 413 /** 414 * {@inheritDoc} 415 * 416 * @see org.openimaj.image.Image#clipMin(java.lang.Object) 417 */ 418 @Override 419 public FImage clipMin(final Float thresh) 420 { 421 final float fthresh = thresh; 422 for (int r = 0; r < this.height; r++) 423 { 424 for (int c = 0; c < this.width; c++) 425 { 426 if (this.pixels[r][c] < fthresh) 427 this.pixels[r][c] = 0; 428 } 429 } 430 return this; 431 } 432 433 /** 434 * {@inheritDoc} 435 * 436 * @see org.openimaj.image.SingleBandImage#clone() 437 */ 438 @Override 439 public FImage clone() 440 { 441 final FImage cpy = new FImage(this.width, this.height); 442 int r; 443 444 for (r = 0; r < this.height; r++) 445 System.arraycopy(this.pixels[r], 0, cpy.pixels[r], 0, this.width); 446 447 return cpy; 448 } 449 450 @Override 451 public FImageRenderer createRenderer() { 452 return new FImageRenderer(this); 453 } 454 455 @Override 456 public FImageRenderer createRenderer(final RenderHints options) { 457 return new FImageRenderer(this, options); 458 } 459 460 /** 461 * Divides the pixels values of this image with the values from the given 462 * image. This is a version of {@link Image#divide(Image)} which takes an 463 * {@link FImage}. This directly accesses the underlying float[][] and is 464 * therefore fast. This function returns a new {@link FImage}. 465 * 466 * @see Image#divide(Image) 467 * @param im 468 * the {@link FImage} to be the denominator. 469 * @return A new {@link FImage} 470 */ 471 public FImage divide(final FImage im) 472 { 473 if (!ImageUtilities.checkSameSize(this, im)) 474 throw new AssertionError("images must be the same size"); 475 476 final FImage newImage = new FImage(im.width, im.height); 477 int r, c; 478 479 for (r = 0; r < im.height; r++) 480 for (c = 0; c < im.width; c++) 481 newImage.pixels[r][c] = this.pixels[r][c] / im.pixels[r][c]; 482 483 return newImage; 484 } 485 486 /** 487 * Divides the pixel values of this image with the values from the given 488 * image. This is a version of {@link Image#divideInplace(Image)} which 489 * takes an {@link FImage}. This directly accesses the underlying float[][] 490 * and is therefore fast. This function side-affects this image. 491 * 492 * @see Image#divideInplace(Image) 493 * @param im 494 * the {@link FImage} to be the denominator 495 * @return a reference to this {@link FImage} 496 */ 497 public FImage divideInplace(final FImage im) 498 { 499 if (!ImageUtilities.checkSameSize(this, im)) 500 throw new AssertionError("images must be the same size"); 501 502 for (int y = 0; y < this.height; y++) 503 { 504 for (int x = 0; x < this.width; x++) 505 { 506 this.pixels[y][x] /= im.pixels[y][x]; 507 } 508 } 509 510 return this; 511 } 512 513 /** 514 * {@inheritDoc} 515 * 516 * @see org.openimaj.image.Image#divideInplace(java.lang.Object) 517 */ 518 @Override 519 public FImage divideInplace(final Float val) 520 { 521 final float fval = val; 522 523 for (int y = 0; y < this.height; y++) 524 for (int x = 0; x < this.width; x++) 525 this.pixels[y][x] /= fval; 526 527 return this; 528 } 529 530 /** 531 * Divide all pixels by a given value 532 * 533 * @param fval 534 * the value 535 * @return this image 536 * @see org.openimaj.image.Image#divideInplace(java.lang.Object) 537 */ 538 public FImage divideInplace(final float fval) 539 { 540 for (int y = 0; y < this.height; y++) 541 for (int x = 0; x < this.width; x++) 542 this.pixels[y][x] /= fval; 543 544 return this; 545 } 546 547 /** 548 * {@inheritDoc} 549 * 550 * @see org.openimaj.image.Image#divideInplace(org.openimaj.image.Image) 551 */ 552 @Override 553 public FImage divideInplace(final Image<?, ?> im) 554 { 555 if (im instanceof FImage) 556 return this.divideInplace((FImage) im); 557 else 558 throw new UnsupportedOperationException("Unsupported Type"); 559 } 560 561 /** 562 * {@inheritDoc} 563 * 564 * @see org.openimaj.image.Image#extractROI(int, int, 565 * org.openimaj.image.Image) 566 */ 567 @Override 568 public FImage extractROI(final int x, final int y, final FImage out) 569 { 570 for (int r = y, rr = 0; rr < out.height; r++, rr++) 571 { 572 for (int c = x, cc = 0; cc < out.width; c++, cc++) 573 { 574 if (r < 0 || r >= this.height || c < 0 || c >= this.width) 575 (out).pixels[rr][cc] = 0; 576 else 577 (out).pixels[rr][cc] = this.pixels[r][c]; 578 } 579 } 580 581 return out; 582 } 583 584 /** 585 * {@inheritDoc} 586 * 587 * @see org.openimaj.image.Image#extractROI(int, int, int, int) 588 */ 589 @Override 590 public FImage extractROI(final int x, final int y, final int w, final int h) 591 { 592 final FImage out = new FImage(w, h); 593 594 for (int r = y, rr = 0; rr < h; r++, rr++) 595 { 596 for (int c = x, cc = 0; cc < w; c++, cc++) 597 { 598 if (r < 0 || r >= this.height || c < 0 || c >= this.width) 599 out.pixels[rr][cc] = 0; 600 else 601 out.pixels[rr][cc] = this.pixels[r][c]; 602 } 603 } 604 605 return out; 606 } 607 608 /** 609 * {@inheritDoc} 610 * 611 * @see org.openimaj.image.SingleBandImage#fill(java.lang.Comparable) 612 */ 613 @Override 614 public FImage fill(final Float colour) 615 { 616 for (int r = 0; r < this.height; r++) 617 for (int c = 0; c < this.width; c++) 618 this.pixels[r][c] = colour; 619 620 return this; 621 } 622 623 /** 624 * Fill an image with the given colour 625 * 626 * @param colour 627 * the colour 628 * @return the image 629 * @see org.openimaj.image.SingleBandImage#fill(java.lang.Comparable) 630 */ 631 public FImage fill(final float colour) 632 { 633 for (int r = 0; r < this.height; r++) 634 for (int c = 0; c < this.width; c++) 635 this.pixels[r][c] = colour; 636 637 return this; 638 } 639 640 /** 641 * {@inheritDoc} 642 * 643 * @see org.openimaj.image.Image#getContentArea() 644 */ 645 @Override 646 public Rectangle getContentArea() { 647 int minc = this.width, maxc = 0, minr = this.height, maxr = 0; 648 649 for (int r = 0; r < this.height; r++) { 650 for (int c = 0; c < this.width; c++) { 651 if (this.pixels[r][c] > 0) { 652 if (c < minc) 653 minc = c; 654 if (c > maxc) 655 maxc = c; 656 if (r < minr) 657 minr = r; 658 if (r > maxr) 659 maxr = r; 660 } 661 } 662 } 663 664 return new Rectangle(minc, minr, maxc - minc + 1, maxr - minr + 1); 665 } 666 667 /** 668 * Returns the pixels of the image as a vector (array) of doubles. 669 * 670 * @return the pixels of the image as a vector (array) of doubles. 671 */ 672 public double[] getDoublePixelVector() 673 { 674 final double f[] = new double[this.height * this.width]; 675 for (int y = 0; y < this.height; y++) 676 for (int x = 0; x < this.width; x++) 677 f[x + y * this.width] = this.pixels[y][x]; 678 679 return f; 680 } 681 682 /** 683 * {@inheritDoc} 684 * 685 * @see org.openimaj.image.Image#getField(org.openimaj.image.Image.Field) 686 */ 687 @Override 688 public FImage getField(final Field f) 689 { 690 final FImage img = new FImage(this.width, this.height / 2); 691 692 int r, r2, c; 693 final int init = (f.equals(Field.ODD) ? 1 : 0); 694 for (r = init, r2 = 0; r < this.height && r2 < this.height / 2; r += 2, r2++) 695 { 696 for (c = 0; c < this.width; c++) 697 { 698 img.pixels[r2][c] = this.pixels[r][c]; 699 } 700 } 701 702 return img; 703 } 704 705 /** 706 * {@inheritDoc} 707 * 708 * @see org.openimaj.image.Image#getFieldCopy(org.openimaj.image.Image.Field) 709 */ 710 @Override 711 public FImage getFieldCopy(final Field f) 712 { 713 final FImage img = new FImage(this.width, this.height); 714 715 int r, c; 716 for (r = 0; r < this.height; r += 2) 717 { 718 for (c = 0; c < this.width; c++) 719 { 720 if (f.equals(Field.EVEN)) 721 { 722 img.pixels[r][c] = this.pixels[r][c]; 723 img.pixels[r + 1][c] = this.pixels[r][c]; 724 } 725 else 726 { 727 img.pixels[r][c] = this.pixels[r + 1][c]; 728 img.pixels[r + 1][c] = this.pixels[r + 1][c]; 729 } 730 } 731 } 732 733 return img; 734 } 735 736 /** 737 * {@inheritDoc} 738 * 739 * @see org.openimaj.image.Image#getFieldInterpolate(org.openimaj.image.Image.Field) 740 */ 741 @Override 742 public FImage getFieldInterpolate(final Field f) 743 { 744 final FImage img = new FImage(this.width, this.height); 745 746 int r, c; 747 for (r = 0; r < this.height; r += 2) 748 { 749 for (c = 0; c < this.width; c++) 750 { 751 if (f.equals(Field.EVEN)) 752 { 753 img.pixels[r][c] = this.pixels[r][c]; 754 755 if (r + 2 == this.height) 756 { 757 img.pixels[r + 1][c] = this.pixels[r][c]; 758 } 759 else 760 { 761 img.pixels[r + 1][c] = 0.5F * (this.pixels[r][c] + this.pixels[r + 2][c]); 762 } 763 } 764 else 765 { 766 img.pixels[r + 1][c] = this.pixels[r + 1][c]; 767 768 if (r == 0) 769 { 770 img.pixels[r][c] = this.pixels[r + 1][c]; 771 } 772 else 773 { 774 img.pixels[r][c] = 0.5F * (this.pixels[r - 1][c] + this.pixels[r + 1][c]); 775 } 776 } 777 } 778 } 779 780 return img; 781 } 782 783 /** 784 * Returns the pixels of the image as a vector (array) of floats. 785 * 786 * @return the pixels of the image as a vector (array) of floats. 787 */ 788 public float[] getFloatPixelVector() 789 { 790 final float f[] = new float[this.height * this.width]; 791 for (int y = 0; y < this.height; y++) 792 for (int x = 0; x < this.width; x++) 793 f[x + y * this.width] = this.pixels[y][x]; 794 795 return f; 796 } 797 798 /** 799 * {@inheritDoc} 800 * 801 * @see org.openimaj.image.Image#getPixel(int, int) 802 */ 803 @Override 804 public Float getPixel(final int x, final int y) 805 { 806 return this.pixels[y][x]; 807 } 808 809 /** 810 * {@inheritDoc} 811 * 812 * @see org.openimaj.image.Image#getPixelComparator() 813 */ 814 @Override 815 public Comparator<? super Float> getPixelComparator() { 816 return new Comparator<Float>() { 817 818 @Override 819 public int compare(final Float o1, final Float o2) { 820 return o1.compareTo(o2); 821 } 822 823 }; 824 } 825 826 /** 827 * {@inheritDoc} 828 * 829 * @see org.openimaj.image.Image#getPixelInterp(double, double) 830 * @see Interpolation#bilerp(double, double, double, double, double, double) 831 */ 832 @Override 833 public Float getPixelInterp(final double x, final double y) 834 { 835 int x0 = (int) Math.floor(x); 836 int x1 = x0 + 1; 837 int y0 = (int) Math.floor(y); 838 int y1 = y0 + 1; 839 840 if (x0 < 0) 841 x0 = 0; 842 if (x0 >= this.width) 843 x0 = this.width - 1; 844 if (y0 < 0) 845 y0 = 0; 846 if (y0 >= this.height) 847 y0 = this.height - 1; 848 849 if (x1 < 0) 850 x1 = 0; 851 if (x1 >= this.width) 852 x1 = this.width - 1; 853 if (y1 < 0) 854 y1 = 0; 855 if (y1 >= this.height) 856 y1 = this.height - 1; 857 858 final float f00 = this.pixels[y0][x0]; 859 final float f01 = this.pixels[y1][x0]; 860 final float f10 = this.pixels[y0][x1]; 861 final float f11 = this.pixels[y1][x1]; 862 float dx = (float) (x - x0); 863 float dy = (float) (y - y0); 864 if (dx < 0) 865 dx = 1 + dx; 866 if (dy < 0) 867 dy = 1 + dy; 868 869 return Interpolation.bilerp(dx, dy, f00, f01, f10, f11); 870 } 871 872 /** 873 * {@inheritDoc} 874 * 875 * @see org.openimaj.image.Image#getPixelInterp(double, double) 876 * @see Interpolation#bilerp(double, double, double, double, double, double) 877 */ 878 @Override 879 public Float getPixelInterp(final double x, final double y, final Float background) 880 { 881 final int x0 = (int) Math.floor(x); 882 final int x1 = x0 + 1; 883 final int y0 = (int) Math.floor(y); 884 final int y1 = y0 + 1; 885 886 boolean tx0, tx1, ty0, ty1; 887 tx0 = ty0 = tx1 = ty1 = true; 888 if (x0 < 0) 889 tx0 = false; 890 if (x0 >= this.width) 891 tx0 = false; 892 if (y0 < 0) 893 ty0 = false; 894 if (y0 >= this.height) 895 ty0 = false; 896 897 if (x1 < 0) 898 tx1 = false; 899 if (x1 >= this.width) 900 tx1 = false; 901 if (y1 < 0) 902 ty1 = false; 903 if (y1 >= this.height) 904 ty1 = false; 905 906 final double f00 = (ty0 && tx0 ? this.pixels[y0][x0] : background.floatValue()); // this.pixels[y0][x0]; 907 final double f01 = (ty1 && tx0 ? this.pixels[y1][x0] : background.floatValue()); // this.pixels[y1][x0]; 908 final double f10 = (ty0 && tx1 ? this.pixels[y0][x1] : background.floatValue()); // this.pixels[y0][x1]; 909 final double f11 = (ty1 && tx1 ? this.pixels[y1][x1] : background.floatValue()); // this.pixels[y1][x1]; 910 911 double dx = x - x0; 912 double dy = y - y0; 913 if (dx < 0) 914 dx = 1 + dx; 915 if (dy < 0) 916 dy = 1 + dy; 917 918 final double interpVal = Interpolation.bilerp(dx, dy, f00, f01, f10, f11); 919 return (float) interpVal; 920 } 921 922 /** 923 * Interpolate the value of a pixel at the given coordinates 924 * 925 * @param x 926 * the x-ordinate 927 * @param y 928 * the y-ordinate 929 * @param background 930 * the background colour 931 * @return the interpolated pixel value 932 * @see org.openimaj.image.Image#getPixelInterp(double, double) 933 * @see Interpolation#bilerp(double, double, double, double, double, double) 934 */ 935 public float getPixelInterpNative(final float x, final float y, final float background) 936 { 937 final int x0 = (int) Math.floor(x); 938 final int x1 = x0 + 1; 939 final int y0 = (int) Math.floor(y); 940 final int y1 = y0 + 1; 941 942 boolean tx0, tx1, ty0, ty1; 943 tx0 = ty0 = tx1 = ty1 = true; 944 if (x0 < 0) 945 tx0 = false; 946 if (x0 >= this.width) 947 tx0 = false; 948 if (y0 < 0) 949 ty0 = false; 950 if (y0 >= this.height) 951 ty0 = false; 952 953 if (x1 < 0) 954 tx1 = false; 955 if (x1 >= this.width) 956 tx1 = false; 957 if (y1 < 0) 958 ty1 = false; 959 if (y1 >= this.height) 960 ty1 = false; 961 962 final float f00 = (ty0 && tx0 ? this.pixels[y0][x0] : background); // this.pixels[y0][x0]; 963 final float f01 = (ty1 && tx0 ? this.pixels[y1][x0] : background); // this.pixels[y1][x0]; 964 final float f10 = (ty0 && tx1 ? this.pixels[y0][x1] : background); // this.pixels[y0][x1]; 965 final float f11 = (ty1 && tx1 ? this.pixels[y1][x1] : background); // this.pixels[y1][x1]; 966 967 float dx = x - x0; 968 float dy = y - y0; 969 if (dx < 0) 970 dx = 1 + dx; 971 if (dy < 0) 972 dy = 1 + dy; 973 974 final float interpVal = Interpolation.bilerpf(dx, dy, f00, f01, f10, f11); 975 return interpVal; 976 } 977 978 /** 979 * {@inheritDoc} 980 * 981 * @see org.openimaj.image.Image#internalAssign(org.openimaj.image.Image) 982 */ 983 @Override 984 public FImage internalCopy(final FImage im) 985 { 986 final int h = im.height; 987 final int w = im.width; 988 final float[][] impixels = im.pixels; 989 990 for (int r = 0; r < h; r++) 991 System.arraycopy(impixels[r], 0, this.pixels[r], 0, w); 992 993 return this; 994 } 995 996 /** 997 * {@inheritDoc} 998 * 999 * @see org.openimaj.image.Image#internalAssign(org.openimaj.image.Image) 1000 */ 1001 @Override 1002 public FImage internalAssign(final FImage im) 1003 { 1004 this.pixels = im.pixels; 1005 this.height = im.height; 1006 this.width = im.width; 1007 1008 return this; 1009 } 1010 1011 /** 1012 * {@inheritDoc} 1013 * 1014 * @see org.openimaj.image.Image#internalAssign(int [] data, int width, int 1015 * height) 1016 */ 1017 @Override 1018 public FImage internalAssign(final int[] data, final int width, final int height) { 1019 if (this.height != height || this.width != width) { 1020 this.height = height; 1021 this.width = width; 1022 this.pixels = new float[height][width]; 1023 } 1024 1025 for (int y = 0; y < height; y++) { 1026 for (int x = 0; x < width; x++) { 1027 final int rgb = data[x + width * y]; 1028 1029 final int red = ((rgb >> 16) & 0xff); 1030 final int green = ((rgb >> 8) & 0xff); 1031 final int blue = ((rgb) & 0xff); 1032 1033 // NTSC colour conversion: 1034 // This improves keypoint detection for some reason! 1035 final float fpix = 0.299f * red + 0.587f * green + 0.114f * blue; 1036 1037 this.pixels[y][x] = ImageUtilities.BYTE_TO_FLOAT_LUT[(int) fpix]; 1038 } 1039 } 1040 return this; 1041 } 1042 1043 /** 1044 * {@inheritDoc} 1045 * 1046 * @see org.openimaj.image.Image#inverse() 1047 */ 1048 @Override 1049 public FImage inverse() 1050 { 1051 int r, c; 1052 final float max = this.max(); 1053 1054 for (r = 0; r < this.height; r++) 1055 for (c = 0; c < this.width; c++) 1056 this.pixels[r][c] = max - this.pixels[r][c]; 1057 1058 return this; 1059 } 1060 1061 /** 1062 * {@inheritDoc} 1063 * 1064 * @see org.openimaj.image.Image#max() 1065 */ 1066 @Override 1067 public Float max() 1068 { 1069 int r, c; 1070 float max = Float.MIN_VALUE; 1071 1072 for (r = 0; r < this.height; r++) 1073 for (c = 0; c < this.width; c++) 1074 if (max < this.pixels[r][c]) 1075 max = this.pixels[r][c]; 1076 1077 return max; 1078 } 1079 1080 /** 1081 * Get the pixel with the maximum value. Returns an {@link FValuePixel} 1082 * which contains the location and value of the pixel. If there are multiple 1083 * pixels with the same value then the first is returned. Note that this 1084 * method assumes all pixel values are greater than 0. 1085 * 1086 * @return the maximum pixel as an {@link FValuePixel}. 1087 */ 1088 public FValuePixel maxPixel() 1089 { 1090 final FValuePixel max = new FValuePixel(-1, -1); 1091 max.value = -Float.MAX_VALUE; 1092 1093 for (int y = 0; y < this.height; y++) { 1094 for (int x = 0; x < this.width; x++) { 1095 if (max.value < this.pixels[y][x]) { 1096 max.value = this.pixels[y][x]; 1097 max.x = x; 1098 max.y = y; 1099 } 1100 } 1101 } 1102 1103 return max; 1104 } 1105 1106 /** 1107 * {@inheritDoc} 1108 * 1109 * @see org.openimaj.image.Image#min() 1110 */ 1111 @Override 1112 public Float min() 1113 { 1114 int r, c; 1115 float min = Float.MAX_VALUE; 1116 1117 for (r = 0; r < this.height; r++) 1118 for (c = 0; c < this.width; c++) 1119 if (min > this.pixels[r][c]) 1120 min = this.pixels[r][c]; 1121 1122 return min; 1123 } 1124 1125 /** 1126 * Get the pixel with the minimum value. Returns an {@link FValuePixel} 1127 * which contains the location and value of the pixel. If there are multiple 1128 * pixels with the same value then the first is returned. Note that this 1129 * method assumes all pixel values are greater than 0. 1130 * 1131 * @return The minimum pixel as an {@link FValuePixel}. 1132 */ 1133 public FValuePixel minPixel() 1134 { 1135 final FValuePixel min = new FValuePixel(-1, -1); 1136 min.value = Float.MAX_VALUE; 1137 1138 for (int y = 0; y < this.height; y++) 1139 for (int x = 0; x < this.width; x++) 1140 if (min.value > this.pixels[y][x]) { 1141 min.value = this.pixels[y][x]; 1142 min.x = x; 1143 min.y = y; 1144 } 1145 1146 return min; 1147 } 1148 1149 /** 1150 * {@inheritDoc} 1151 * 1152 * @see org.openimaj.image.Image#multiply(java.lang.Object) 1153 */ 1154 @Override 1155 public FImage multiply(final Float num) 1156 { 1157 return super.multiply(num); 1158 } 1159 1160 /** 1161 * Multiplies this image's pixel values by the corresponding pixel values in 1162 * the given image side-affecting this image. This is a version of 1163 * {@link Image#multiplyInplace(Image)} which takes an {@link FImage}. This 1164 * directly accesses the underlying float[][] and is therefore fast. This 1165 * function works inplace. 1166 * 1167 * @see Image#multiplyInplace(Image) 1168 * @param im 1169 * the {@link FImage} to multiply with this image 1170 * @return a reference to this image 1171 */ 1172 public FImage multiplyInplace(final FImage im) 1173 { 1174 if (!ImageUtilities.checkSameSize(this, im)) 1175 throw new AssertionError("images must be the same size"); 1176 1177 for (int r = 0; r < this.height; r++) 1178 { 1179 for (int c = 0; c < this.width; c++) 1180 { 1181 this.pixels[r][c] *= im.pixels[r][c]; 1182 } 1183 } 1184 1185 return this; 1186 } 1187 1188 /** 1189 * {@inheritDoc} 1190 * 1191 * @see org.openimaj.image.Image#multiplyInplace(java.lang.Object) 1192 */ 1193 @Override 1194 public FImage multiplyInplace(final Float num) 1195 { 1196 final float fnum = num; 1197 for (int r = 0; r < this.height; r++) 1198 { 1199 for (int c = 0; c < this.width; c++) 1200 { 1201 this.pixels[r][c] *= fnum; 1202 } 1203 } 1204 1205 return this; 1206 } 1207 1208 /** 1209 * Multiply all pixel values by the given value 1210 * 1211 * @param fnum 1212 * the value 1213 * @return this image 1214 * @see org.openimaj.image.Image#multiplyInplace(java.lang.Object) 1215 */ 1216 public FImage multiplyInplace(final float fnum) 1217 { 1218 for (int r = 0; r < this.height; r++) 1219 { 1220 for (int c = 0; c < this.width; c++) 1221 { 1222 this.pixels[r][c] *= fnum; 1223 } 1224 } 1225 1226 return this; 1227 } 1228 1229 /** 1230 * {@inheritDoc} This method will throw an 1231 * {@link UnsupportedOperationException} if the input input is not an 1232 * {@link FImage}. 1233 * 1234 * @see org.openimaj.image.Image#multiplyInplace(org.openimaj.image.Image) 1235 * @throws UnsupportedOperationException 1236 * if the given image is not an {@link FImage} 1237 */ 1238 @Override 1239 public FImage multiplyInplace(final Image<?, ?> im) 1240 { 1241 if (im instanceof FImage) 1242 return this.multiplyInplace((FImage) im); 1243 else 1244 throw new UnsupportedOperationException("Unsupported Type"); 1245 } 1246 1247 /** 1248 * {@inheritDoc} 1249 * 1250 * @return A new {@link FImage} 1251 * @see org.openimaj.image.Image#newInstance(int, int) 1252 */ 1253 @Override 1254 public FImage newInstance(final int width, final int height) 1255 { 1256 return new FImage(width, height); 1257 } 1258 1259 /** 1260 * {@inheritDoc} 1261 * 1262 * @see org.openimaj.image.Image#normalise() 1263 */ 1264 @Override 1265 public FImage normalise() 1266 { 1267 final float min = this.min(); 1268 final float max = this.max(); 1269 1270 if (max == min) 1271 return this; 1272 1273 for (int r = 0; r < this.height; r++) 1274 { 1275 for (int c = 0; c < this.width; c++) 1276 { 1277 this.pixels[r][c] = (this.pixels[r][c] - min) / (max - min); 1278 } 1279 } 1280 1281 return this; 1282 } 1283 1284 /** 1285 * {@inheritDoc} 1286 * 1287 * @see org.openimaj.image.SingleBandImage#process(org.openimaj.image.processor.KernelProcessor) 1288 */ 1289 @Override 1290 public FImage process(final KernelProcessor<Float, FImage> p) { 1291 return this.process(p, false); 1292 } 1293 1294 /** 1295 * {@inheritDoc} This method has been overridden in {@link FImage} for 1296 * performance. 1297 * 1298 * @see org.openimaj.image.SingleBandImage#process(org.openimaj.image.processor.KernelProcessor, 1299 * boolean) 1300 */ 1301 @Override 1302 public FImage process(final KernelProcessor<Float, FImage> p, final boolean pad) 1303 { 1304 final FImage newImage = new FImage(this.width, this.height); 1305 final int kh = p.getKernelHeight(); 1306 final int kw = p.getKernelWidth(); 1307 1308 final FImage tmp = new FImage(kw, kh); 1309 1310 final int hh = kh / 2; 1311 final int hw = kw / 2; 1312 1313 if (!pad) { 1314 for (int y = hh; y < this.height - (kh - hh); y++) { 1315 for (int x = hw; x < this.width - (kw - hw); x++) { 1316 newImage.pixels[y][x] = p.processKernel(this.extractROI(x - hw, y - hh, tmp)); 1317 } 1318 } 1319 } else { 1320 for (int y = 0; y < this.height; y++) { 1321 for (int x = 0; x < this.width; x++) { 1322 newImage.pixels[y][x] = p.processKernel(this.extractROI(x - hw, y - hh, tmp)); 1323 } 1324 } 1325 } 1326 1327 return newImage; 1328 } 1329 1330 /** 1331 * {@inheritDoc} This method has been overridden in {@link FImage} for 1332 * performance. 1333 * 1334 * @see org.openimaj.image.Image#processInplace(org.openimaj.image.processor.PixelProcessor) 1335 */ 1336 @Override 1337 public FImage processInplace(final PixelProcessor<Float> p) 1338 { 1339 for (int y = 0; y < this.height; y++) 1340 { 1341 for (int x = 0; x < this.width; x++) 1342 { 1343 this.pixels[y][x] = p.processPixel(this.pixels[y][x]); 1344 } 1345 } 1346 1347 return this; 1348 } 1349 1350 /** 1351 * {@inheritDoc} This method has been overridden in {@link FImage} for 1352 * performance. 1353 * 1354 * @see org.openimaj.image.Image#analyseWith(org.openimaj.image.analyser.PixelAnalyser) 1355 */ 1356 @Override 1357 public void analyseWith(final PixelAnalyser<Float> p) 1358 { 1359 p.reset(); 1360 1361 for (int y = 0; y < this.height; y++) 1362 { 1363 for (int x = 0; x < this.width; x++) 1364 { 1365 p.analysePixel(this.pixels[y][x]); 1366 } 1367 } 1368 } 1369 1370 /** 1371 * {@inheritDoc} 1372 * 1373 * @see org.openimaj.image.Image#setPixel(int, int, java.lang.Object) 1374 */ 1375 @Override 1376 public void setPixel(final int x, final int y, final Float val) { 1377 if (x >= 0 && x < this.width && y >= 0 && y < this.height) 1378 this.pixels[y][x] = val; 1379 } 1380 1381 /** 1382 * Subtracts the given {@link FImage} from this image returning a new image 1383 * containing the result. 1384 * 1385 * @param im 1386 * The image to subtract from this image. 1387 * @return A new image containing the result. 1388 */ 1389 public FImage subtract(final FImage im) 1390 { 1391 if (!ImageUtilities.checkSameSize(this, im)) 1392 throw new AssertionError("images must be the same size"); 1393 1394 final FImage newImage = new FImage(im.width, im.height); 1395 int r, c; 1396 1397 for (r = 0; r < im.height; r++) 1398 for (c = 0; c < im.width; c++) 1399 newImage.pixels[r][c] = this.pixels[r][c] - im.pixels[r][c]; 1400 return newImage; 1401 } 1402 1403 /** 1404 * {@inheritDoc} 1405 * 1406 * @see org.openimaj.image.Image#subtract(java.lang.Object) 1407 */ 1408 @Override 1409 public FImage subtract(final Float num) 1410 { 1411 final FImage newImage = new FImage(this.width, this.height); 1412 1413 for (int r = 0; r < this.height; r++) 1414 { 1415 for (int c = 0; c < this.width; c++) 1416 { 1417 newImage.pixels[r][c] = this.pixels[r][c] - num; 1418 } 1419 } 1420 return newImage; 1421 } 1422 1423 /** 1424 * {@inheritDoc} Throws an {@link UnsupportedOperationException} if the 1425 * given image is not an {@link FImage}. 1426 * 1427 * @see org.openimaj.image.Image#subtract(org.openimaj.image.Image) 1428 * @throws UnsupportedOperationException 1429 * if the given image is not an {@link FImage}. 1430 */ 1431 @Override 1432 public FImage subtract(final Image<?, ?> input) 1433 { 1434 if (input instanceof FImage) 1435 return this.subtract((FImage) input); 1436 else 1437 throw new UnsupportedOperationException("Unsupported Type"); 1438 } 1439 1440 /** 1441 * Subtracts (pixel-by-pixel) the given {@link FImage} from this image. 1442 * Side-affects this image. 1443 * 1444 * @param im 1445 * The {@link FImage} to subtract from this image. 1446 * @return A reference to this image containing the result. 1447 */ 1448 public FImage subtractInplace(final FImage im) 1449 { 1450 if (!ImageUtilities.checkSameSize(this, im)) 1451 throw new AssertionError("images must be the same size"); 1452 1453 float pix1[][], pix2[][]; 1454 int r, c; 1455 1456 pix1 = this.pixels; 1457 pix2 = im.pixels; 1458 1459 for (r = 0; r < this.height; r++) 1460 for (c = 0; c < this.width; c++) 1461 pix1[r][c] -= pix2[r][c]; 1462 1463 return this; 1464 } 1465 1466 /** 1467 * {@inheritDoc} 1468 * 1469 * @see org.openimaj.image.Image#subtractInplace(java.lang.Object) 1470 */ 1471 @Override 1472 public FImage subtractInplace(final Float num) 1473 { 1474 final float fnum = num; 1475 for (int r = 0; r < this.height; r++) 1476 { 1477 for (int c = 0; c < this.width; c++) 1478 { 1479 this.pixels[r][c] -= fnum; 1480 } 1481 } 1482 1483 return this; 1484 } 1485 1486 /** 1487 * {@inheritDoc} 1488 * 1489 * @see org.openimaj.image.Image#subtractInplace(org.openimaj.image.Image) 1490 */ 1491 @Override 1492 public FImage subtractInplace(final Image<?, ?> im) 1493 { 1494 if (im instanceof FImage) 1495 return this.subtractInplace((FImage) im); 1496 else 1497 throw new UnsupportedOperationException("Unsupported Type"); 1498 } 1499 1500 /** 1501 * {@inheritDoc} 1502 * 1503 * @see org.openimaj.image.Image#threshold(java.lang.Object) 1504 */ 1505 @Override 1506 public FImage threshold(final Float thresh) 1507 { 1508 final float fthresh = thresh; 1509 for (int r = 0; r < this.height; r++) 1510 { 1511 for (int c = 0; c < this.width; c++) 1512 { 1513 if (this.pixels[r][c] <= fthresh) 1514 this.pixels[r][c] = 0; 1515 else 1516 this.pixels[r][c] = 1; 1517 } 1518 } 1519 return this; 1520 } 1521 1522 /** 1523 * {@inheritDoc} 1524 * 1525 * @see org.openimaj.image.Image#toByteImage() 1526 */ 1527 @Override 1528 public byte[] toByteImage() 1529 { 1530 final byte[] pgmData = new byte[this.height * this.width]; 1531 1532 for (int j = 0; j < this.height; j++) 1533 { 1534 for (int i = 0; i < this.width; i++) 1535 { 1536 int v = (int) (255.0f * this.pixels[j][i]); 1537 1538 v = Math.max(0, Math.min(255, v)); 1539 1540 pgmData[i + j * this.width] = (byte) (v & 0xFF); 1541 } 1542 } 1543 return pgmData; 1544 } 1545 1546 /** 1547 * {@inheritDoc} 1548 * 1549 * @see org.openimaj.image.Image#toPackedARGBPixels() 1550 */ 1551 @Override 1552 public int[] toPackedARGBPixels() 1553 { 1554 final int[] bimg = new int[this.width * this.height]; 1555 1556 for (int r = 0; r < this.height; r++) { 1557 for (int c = 0; c < this.width; c++) { 1558 final int v = (Math.max(0, Math.min(255, (int) (this.pixels[r][c] * 255)))); 1559 1560 final int rgb = 0xff << 24 | v << 16 | v << 8 | v; 1561 bimg[c + this.width * r] = rgb; 1562 } 1563 } 1564 1565 return bimg; 1566 } 1567 1568 /** 1569 * {@inheritDoc} 1570 * 1571 * @see java.lang.Object#toString() 1572 */ 1573 @Override 1574 public String toString() { 1575 String imageString = ""; 1576 for (int y = 0; y < this.height; y++) { 1577 for (int x = 0; x < this.width; x++) { 1578 imageString += String.format("%+.3f ", this.pixels[y][x]); 1579 if (x == 16) { 1580 if (this.width - 16 <= x) 1581 continue; 1582 imageString += "... "; 1583 x = this.width - 16; 1584 } 1585 } 1586 imageString += "\n"; 1587 if (y == 16) { 1588 if (this.height - 16 <= y) 1589 continue; 1590 y = this.height - 16; 1591 imageString += "... \n"; 1592 } 1593 1594 } 1595 return imageString; 1596 } 1597 1598 /** 1599 * Returns a string representation of every pixel in this image using the 1600 * format string (see {@link String#format(String, Object...)}) to format 1601 * each pixel value. 1602 * 1603 * @param format 1604 * The format string to use for each pixel output 1605 * @return A string representation of the image 1606 * @see String#format(String, Object...) 1607 */ 1608 public String toString(final String format) { 1609 String imageString = ""; 1610 for (int y = 0; y < this.height; y++) { 1611 for (int x = 0; x < this.width; x++) { 1612 imageString += String.format(format, this.pixels[y][x]); 1613 } 1614 imageString += "\n"; 1615 } 1616 return imageString; 1617 } 1618 1619 /** 1620 * {@inheritDoc} 1621 * 1622 * @see org.openimaj.image.Image#transform(Jama.Matrix) 1623 */ 1624 @Override 1625 public FImage transform(final Matrix transform) { 1626 return super.transform(transform); 1627 } 1628 1629 /** 1630 * {@inheritDoc} 1631 * 1632 * @see org.openimaj.image.Image#zero() 1633 */ 1634 @Override 1635 public FImage zero() 1636 { 1637 for (int r = 0; r < this.height; r++) 1638 { 1639 for (int c = 0; c < this.width; c++) 1640 { 1641 this.pixels[r][c] = 0; 1642 } 1643 } 1644 return this; 1645 } 1646 1647 @Override 1648 public boolean equals(final Object o) { 1649 if (!(o instanceof FImage)) { 1650 return false; 1651 } 1652 return this.equalsThresh((FImage) o, 0); 1653 } 1654 1655 /** 1656 * Compare this image against another using a threshold on the absolute 1657 * difference between pixel values in order to determine equality. 1658 * 1659 * @param o 1660 * the image to compare against 1661 * @param thresh 1662 * the threshold for determining equality 1663 * @return true images are the same size and if all pixel values have a 1664 * difference less than threshold; false otherwise. 1665 */ 1666 public boolean equalsThresh(final FImage o, final float thresh) { 1667 final FImage that = o; 1668 if (that.height != this.height || that.width != this.width) 1669 return false; 1670 for (int i = 0; i < this.height; i++) { 1671 for (int j = 0; j < this.width; j++) { 1672 if (Math.abs(that.pixels[i][j] - this.pixels[i][j]) > thresh) { 1673 return false; 1674 } 1675 } 1676 } 1677 return true; 1678 } 1679 1680 /** 1681 * Get the value of the pixel at coordinate p 1682 * 1683 * @param p 1684 * The coordinate to get 1685 * 1686 * @return The pixel value at (x, y) 1687 */ 1688 public float getPixelNative(final Pixel p) { 1689 return this.getPixelNative(p.x, p.y); 1690 } 1691 1692 /** 1693 * Get the value of the pixel at coordinate <code>(x, y)</code>. 1694 * 1695 * @param x 1696 * The x-coordinate to get 1697 * @param y 1698 * The y-coordinate to get 1699 * 1700 * @return The pixel value at (x, y) 1701 */ 1702 public float getPixelNative(final int x, final int y) { 1703 return this.pixels[y][x]; 1704 } 1705 1706 /** 1707 * Returns the pixels in this image as a vector (an array of the pixel 1708 * type). 1709 * 1710 * @param f 1711 * The array into which to place the data 1712 * @return The pixels in the image as a vector (a reference to the given 1713 * array). 1714 */ 1715 public float[] getPixelVectorNative(final float[] f) 1716 { 1717 for (int y = 0; y < this.getHeight(); y++) 1718 for (int x = 0; x < this.getWidth(); x++) 1719 f[x + y * this.getWidth()] = this.pixels[y][x]; 1720 1721 return f; 1722 } 1723 1724 /** 1725 * Sets the pixel at <code>(x,y)</code> to the given value. Side-affects 1726 * this image. 1727 * 1728 * @param x 1729 * The x-coordinate of the pixel to set 1730 * @param y 1731 * The y-coordinate of the pixel to set 1732 * @param val 1733 * The value to set the pixel to. 1734 */ 1735 public void setPixelNative(final int x, final int y, final float val) { 1736 this.pixels[y][x] = val; 1737 } 1738 1739 /** 1740 * Convenience method to initialise an array of FImages 1741 * 1742 * @param num 1743 * array length 1744 * @param width 1745 * width of images 1746 * @param height 1747 * height of images 1748 * @return array of newly initialised images 1749 */ 1750 public static FImage[] createArray(final int num, final int width, final int height) { 1751 final FImage[] array = new FImage[num]; 1752 1753 for (int i = 0; i < num; i++) { 1754 array[i] = new FImage(width, height); 1755 } 1756 1757 return array; 1758 } 1759 1760 /** 1761 * @return The sum of all the pixels in the image 1762 */ 1763 public float sum() { 1764 float sum = 0; 1765 for (final float[] row : this.pixels) { 1766 for (int i = 0; i < row.length; i++) { 1767 sum += row[i]; 1768 } 1769 } 1770 return sum; 1771 } 1772 1773 /** 1774 * Convert this {@link FImage} to an RGB {@link MBFImage}. 1775 * 1776 * @return a new RGB colour image. 1777 */ 1778 public MBFImage toRGB() { 1779 return new MBFImage(ColourSpace.RGB, this.clone(), this.clone(), this.clone()); 1780 } 1781 1782 @Override 1783 public FImage flipX() { 1784 final int hwidth = this.width / 2; 1785 1786 for (int y = 0; y < this.height; y++) { 1787 for (int x = 0; x < hwidth; x++) { 1788 final int xx = this.width - x - 1; 1789 1790 final float tmp = this.pixels[y][x]; 1791 1792 this.pixels[y][x] = this.pixels[y][xx]; 1793 this.pixels[y][xx] = tmp; 1794 } 1795 } 1796 return this; 1797 } 1798 1799 @Override 1800 public FImage flipY() { 1801 final int hheight = this.height / 2; 1802 1803 for (int y = 0; y < hheight; y++) { 1804 final int yy = this.height - y - 1; 1805 1806 for (int x = 0; x < this.width; x++) { 1807 final float tmp = this.pixels[y][x]; 1808 1809 this.pixels[y][x] = this.pixels[yy][x]; 1810 this.pixels[yy][x] = tmp; 1811 } 1812 } 1813 1814 return this; 1815 } 1816 1817 /** 1818 * Overlay the given image on this image with the given alpha channel at the 1819 * given location. 1820 * 1821 * @param img 1822 * The image to overlay 1823 * @param alpha 1824 * The alpha channel to use 1825 * @param x 1826 * The location to draw the image 1827 * @param y 1828 * The location to draw the image 1829 * @return This image with the overlay on it 1830 */ 1831 public FImage overlayInplace(final FImage img, final FImage alpha, final int x, final int y) 1832 { 1833 final int sx = Math.max(x, 0); 1834 final int sy = Math.max(y, 0); 1835 final int ex = Math.min(this.width, x + img.getWidth()); 1836 final int ey = Math.min(this.height, y + img.getHeight()); 1837 1838 for (int yc = sy; yc < ey; yc++) 1839 { 1840 for (int xc = sx; xc < ex; xc++) 1841 { 1842 final float a = alpha.pixels[yc - sy][xc - sx]; 1843 this.pixels[yc][xc] = (a * img.pixels[yc - sy][xc - sx] + 1844 (1 - a) * this.pixels[yc][xc]); 1845 } 1846 } 1847 1848 return this; 1849 } 1850 1851 /** 1852 * {@inheritDoc} 1853 * <p> 1854 * This method will overlay the given image at the given location with full 1855 * opacity. 1856 * 1857 * @see org.openimaj.image.Image#overlayInplace(org.openimaj.image.Image, 1858 * int, int) 1859 */ 1860 @Override 1861 public FImage overlayInplace(final FImage image, final int x, final int y) 1862 { 1863 return this.overlayInplace(image, this.clone().fill(1f), x, y); 1864 } 1865 1866 /** 1867 * Create a random image of the given size. 1868 * 1869 * @param width 1870 * the width 1871 * @param height 1872 * the height 1873 * @return the image 1874 */ 1875 public static FImage randomImage(final int width, final int height) { 1876 final FImage img = new FImage(width, height); 1877 1878 for (int y = 0; y < height; y++) 1879 for (int x = 0; x < width; x++) 1880 img.pixels[y][x] = (float) Math.random(); 1881 1882 return img; 1883 } 1884 1885 @Override 1886 public FImage replace(Float target, Float replacement) { 1887 return replace((float) target, (float) replacement); 1888 } 1889 1890 /** 1891 * Replace pixels of a certain colour with another colour. Side-affects this 1892 * image. 1893 * 1894 * @param target 1895 * the colour to fill the image with 1896 * @param replacement 1897 * the colour to fill the image with 1898 * @return A reference to this image. 1899 */ 1900 public FImage replace(float target, float replacement) { 1901 for (int r = 0; r < this.height; r++) 1902 for (int c = 0; c < this.width; c++) 1903 if (this.pixels[r][c] == target) 1904 this.pixels[r][c] = replacement; 1905 1906 return this; 1907 } 1908 1909 @Override 1910 public FImage extractCentreSubPix(float cx, float cy, FImage out) { 1911 final int width = out.width; 1912 final int height = out.height; 1913 for (int y = 0; y < height; y++) { 1914 for (int x = 0; x < width; x++) { 1915 final float ix = (float) (x + cx - (width - 1) * 0.5); 1916 final float iy = (float) (y + cy - (height - 1) * 0.5); 1917 out.pixels[y][x] = this.getPixelInterpNative(ix, iy, 0f); 1918 } 1919 } 1920 return out; 1921 } 1922}