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