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-2025 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 concrete 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 * An exception is thrown if the supplied image is not scalar-valued (i.e., a color image). 038 * 039 * @param ip the image to be accessed 040 * @param obs the out-of-bounds strategy to be used (use {@code null} for default settings) 041 * @param ipm the interpolation method to be used (use {@code null} for default settings) 042 * @return a new image accessor 043 */ 044 public static ScalarAccessor create(ImageProcessor ip, OutOfBoundsStrategy obs, InterpolationMethod ipm) { 045 if (ip instanceof ByteProcessor) 046 return new ByteAccessor((ByteProcessor) ip, obs, ipm); 047 if (ip instanceof ShortProcessor) 048 return new ShortAccessor((ShortProcessor) ip, obs, ipm); 049 if (ip instanceof FloatProcessor) 050 return new FloatAccessor((FloatProcessor) ip, obs, ipm); 051 throw new IllegalArgumentException( 052 "cannot create " + ScalarAccessor.class.getSimpleName() + " for " + ip.getClass().getSimpleName()); 053 } 054 055 // /** 056 // * Creates a new scalar accessor (subtype of {@link ScalarAccessor}) for the specified image. 057 // * 058 // * @param ip the image to be accessed 059 // * @param obs the out-of-bounds strategy to be used (use {@code null} for default settings) 060 // * @param ipm the interpolation method to be used (use {@code null} for default settings) 061 // * @return a new {@link ByteAccessor} 062 // */ 063 // public static ByteAccessor create(ByteProcessor ip, OutOfBoundsStrategy obs, InterpolationMethod ipm) { 064 // return new ByteAccessor(ip, obs, ipm); 065 // } 066 // 067 // /** 068 // * Creates a new scalar accessor (subtype of {@link ScalarAccessor}) for the specified image. 069 // * 070 // * @param ip the image to be accessed 071 // * @param obs the out-of-bounds strategy to be used (use {@code null} for default settings) 072 // * @param ipm the interpolation method to be used (use {@code null} for default settings) 073 // * @return a new {@link ShortAccessor} 074 // */ 075 // public static ShortAccessor create(ShortProcessor ip, OutOfBoundsStrategy obs, InterpolationMethod ipm) { 076 // return new ShortAccessor(ip, obs, ipm); 077 // } 078 // 079 // /** 080 // * Creates a new scalar accessor (subtype of {@link ScalarAccessor}) for the specified image. 081 // * 082 // * @param ip the image to be accessed 083 // * @param obs the out-of-bounds strategy to be used (use {@code null} for default settings) 084 // * @param ipm the interpolation method to be used (use {@code null} for default settings) 085 // * @return a new {@link FloatAccessor} 086 // */ 087 // public static FloatAccessor create(FloatProcessor ip, OutOfBoundsStrategy obs, InterpolationMethod ipm) { 088 // return new FloatAccessor(ip, obs, ipm); 089 // } 090 091 @Override 092 public int getDepth() { 093 return 1; 094 } 095 096 @Override 097 public ScalarAccessor getComponentAccessor(int k) { 098 checkComponentIndex(k); 099 return this; 100 } 101 102 /** 103 * Reads and returns the scalar pixel value for the given image position. The value returned for coordinates outside 104 * the image boundaries depends on the {@link OutOfBoundsStrategy} specified for this {@link ImageAccessor}. 105 * 106 * @param u the x-coordinate 107 * @param v the y-coordinate 108 * @return the pixel value ({@code float}) 109 */ 110 public abstract float getVal(int u, int v); // returns pixel value at integer position (u, v) 111 112 @Override 113 public float getVal(int u, int v, int k) { 114 checkComponentIndex(k); 115 return this.getVal(u, v); 116 } 117 118 @Override 119 public float getVal(double x, double y, int k) { 120 checkComponentIndex(k); 121 return this.getVal(x, y); 122 } 123 124 /** 125 * Reads and returns the interpolated scalar pixel value for the given image position. The value returned for 126 * coordinates outside the image boundaries depends on the {@link OutOfBoundsStrategy} specified for this 127 * {@link ImageAccessor}. 128 * 129 * @param x the x-coordinate 130 * @param y the y-coordinate 131 * @return the pixel value ({@code float}) 132 */ 133 public float getVal(double x, double y) { // interpolating version 134 return interpolator.getInterpolatedValue(this, x, y); 135 } 136 137 /** 138 * Writes a scalar pixel value to the given image position. An exception is thrown if u, v coordinates are outside 139 * the image. 140 * 141 * @param u the x-coordinate 142 * @param v the y-coordinate 143 * @param val the new pixel value ({@code float}) 144 */ 145 public abstract void setVal(int u, int v, float val); 146 147 public void setVal(int u, int v, int k, float val) { 148 if (k == 0) { 149 this.setVal(u, v, val); 150 } 151 else { 152 throw new IllegalArgumentException("invalid component index " + k); 153 } 154 } 155 156 @Override 157 public float[] getPix(int u, int v) { 158 return new float[] { this.getVal(u, v) }; 159 } 160 161 @Override 162 public float[] getPix(double x, double y) { 163 return new float[] { this.getVal(x, y) }; 164 } 165 166 @Override 167 public void setPix(int u, int v, float[] pix) { 168 this.setVal(u, v, pix[0]); 169 } 170 171 // --------------------------------------------------------------------- 172 173// @Override 174// public void setDefaultValue(float val) { 175// this.defaultValue = val; 176// } 177 178// public void setDefaultValue(float[] vals) { 179// if (vals.length != 1) { 180// throw new IllegalArgumentException("default values must be of length " + 1); 181// } 182// this.setDefaultValue(vals[0]); 183// } 184 185 @Override 186 void checkComponentIndex(int k) { 187 if (k != 0) { 188 throw new IllegalArgumentException("invalid component index " + k); 189 } 190 } 191}