001/******************************************************************************* 002 * This software is provided as a supplement to the authors' textbooks on digital 003 * image processing published by Springer-Verlag in various languages and editions. 004 * Permission to use and distribute this software is granted under the BSD 2-Clause 005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause). 006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved. 007 * Visit https://imagingbook.com for additional details. 008 ******************************************************************************/ 009package imagingbook.common.image.access; 010 011import ij.process.ByteProcessor; 012import ij.process.FloatProcessor; 013import ij.process.ImageProcessor; 014import ij.process.ShortProcessor; 015import imagingbook.common.image.OutOfBoundsStrategy; 016import imagingbook.common.image.interpolation.InterpolationMethod; 017import imagingbook.common.image.interpolation.PixelInterpolator; 018 019/** 020 * The common (abstract) super-class for all image accessors to scalar-valued images. It inherits all methods from 021 * {@link ImageAccessor} but adds the methods {@link #getVal(int, int)}, {@link #getVal(double, double)} and 022 * {@link #setVal(int, int, float)} for reading and writing scalar-valued pixel data. 023 */ 024public abstract class ScalarAccessor extends ImageAccessor { 025 026 private final PixelInterpolator interpolator; // performs interpolation 027 028 ScalarAccessor(ImageProcessor ip, OutOfBoundsStrategy obs, InterpolationMethod ipm) { 029 super(ip, obs, ipm); 030 this.interpolator = PixelInterpolator.create(interpolationMethod); 031 } 032 033 /** 034 * Creates a new image accessor of general type {@link ScalarAccessor}. The conrete type of the returned instance 035 * depends on the specified image, i.e., {@link ByteAccessor} for {@link ByteProcessor}, {@link ShortAccessor} for 036 * {@link ShortProcessor}, {@link FloatAccessor} for {@link FloatProcessor}. 037 * 038 * @param ip the image to be accessed 039 * @param obs the out-of-bounds strategy to be used (use {@code null} for default settings) 040 * @param ipm the interpolation method to be used (use {@code null} for default settings) 041 * @return a new image accessor 042 */ 043 public static ScalarAccessor create(ImageProcessor ip, OutOfBoundsStrategy obs, InterpolationMethod ipm) { 044 if (ip instanceof ByteProcessor) 045 return new ByteAccessor((ByteProcessor) ip, obs, ipm); 046 if (ip instanceof ShortProcessor) 047 return new ShortAccessor((ShortProcessor) ip, obs, ipm); 048 if (ip instanceof FloatProcessor) 049 return new FloatAccessor((FloatProcessor) ip, obs, ipm); 050 throw new IllegalArgumentException( 051 "cannot create " + ScalarAccessor.class.getSimpleName() + " for " + ip.getClass().getSimpleName()); 052 } 053 054 @Override 055 public int getDepth() { 056 return 1; 057 } 058 059 @Override 060 public ScalarAccessor getComponentAccessor(int k) { 061 checkComponentIndex(k); 062 return this; 063 } 064 065 /** 066 * Reads and returns the scalar pixel value for the given image position. The value returned for coordinates outside 067 * the image boundaries depends on the {@link OutOfBoundsStrategy} specified for this {@link ImageAccessor}. 068 * 069 * @param u the x-coordinate 070 * @param v the y-coordinate 071 * @return the pixel value ({@code float}) 072 */ 073 public abstract float getVal(int u, int v); // returns pixel value at integer position (u, v) 074 075 @Override 076 public float getVal(int u, int v, int k) { 077 checkComponentIndex(k); 078 return this.getVal(u, v); 079 } 080 081 @Override 082 public float getVal(double x, double y, int k) { 083 checkComponentIndex(k); 084 return this.getVal(x, y); 085 } 086 087 /** 088 * Reads and returns the interpolated scalar pixel value for the given image position. The value returned for 089 * coordinates outside the image boundaries depends on the {@link OutOfBoundsStrategy} specified for this 090 * {@link ImageAccessor}. 091 * 092 * @param x the x-coordinate 093 * @param y the y-coordinate 094 * @return the pixel value ({@code float}) 095 */ 096 public float getVal(double x, double y) { // interpolating version 097 return interpolator.getInterpolatedValue(this, x, y); 098 } 099 100 /** 101 * Writes a scalar pixel value to the given image position. An exception is thrown if u, v coordinates are outside 102 * the image. 103 * 104 * @param u the x-coordinate 105 * @param v the y-coordinate 106 * @param val the new pixel value ({@code float}) 107 */ 108 public abstract void setVal(int u, int v, float val); 109 110 public void setVal(int u, int v, int k, float val) { 111 if (k == 0) { 112 this.setVal(u, v, val); 113 } 114 else { 115 throw new IllegalArgumentException("invalid component index " + k); 116 } 117 } 118 119 @Override 120 public float[] getPix(int u, int v) { 121 return new float[] { this.getVal(u, v) }; 122 } 123 124 @Override 125 public float[] getPix(double x, double y) { 126 return new float[] { this.getVal(x, y) }; 127 } 128 129 @Override 130 public void setPix(int u, int v, float[] pix) { 131 this.setVal(u, v, pix[0]); 132 } 133 134 // --------------------------------------------------------------------- 135 136// @Override 137// public void setDefaultValue(float val) { 138// this.defaultValue = val; 139// } 140 141// public void setDefaultValue(float[] vals) { 142// if (vals.length != 1) { 143// throw new IllegalArgumentException("default values must be of length " + 1); 144// } 145// this.setDefaultValue(vals[0]); 146// } 147 148 @Override 149 void checkComponentIndex(int k) { 150 if (k != 0) { 151 throw new IllegalArgumentException("invalid component index " + k); 152 } 153 } 154}