001 /* 002 * AreaFilterOperation 003 * 004 * Copyright (c) 2002, 2003 Marco Schmidt. 005 * All rights reserved. 006 */ 007 008 package net.sourceforge.jiu.filters; 009 010 import net.sourceforge.jiu.data.GrayIntegerImage; 011 import net.sourceforge.jiu.data.IntegerImage; 012 import net.sourceforge.jiu.data.PixelImage; 013 import net.sourceforge.jiu.data.RGBIntegerImage; 014 import net.sourceforge.jiu.ops.ImageToImageOperation; 015 import net.sourceforge.jiu.ops.MissingParameterException; 016 import net.sourceforge.jiu.ops.WrongParameterException; 017 018 /** 019 * Base class for operations that convert images to images and determine 020 * an output sample by doing calculations on the input sample at the same 021 * position plus some neighboring samples. 022 * <p> 023 * Override {@link #computeSample} and the operation will work. 024 * @since 0.9.0 025 * @author Marco Schmidt 026 */ 027 public abstract class AreaFilterOperation extends ImageToImageOperation 028 { 029 private int areaWidth; 030 private int areaHeight; 031 032 /** 033 * Checks if the argument is a valid area height value. 034 * The default implementation requires the argument to be odd and larger than zero. 035 * Override this method if your extension of AreaFilterOperation requires different heights. 036 * @throws IllegalArgumentException if the argument is not valid 037 */ 038 public void checkAreaHeight(int height) 039 { 040 if (height < 1) 041 { 042 throw new IllegalArgumentException("Height must be larger than 0."); 043 } 044 if ((height & 1) == 0) 045 { 046 throw new IllegalArgumentException("Height must be odd."); 047 } 048 } 049 050 /** 051 * Checks if the argument is a valid area width value. 052 * The default implementation requires the argument to be odd and larger than zero. 053 * Override this method if your extension of AreaFilterOperation requires different widths. 054 * @throws IllegalArgumentException if the argument is not valid 055 */ 056 public void checkAreaWidth(int width) 057 { 058 if (width < 1) 059 { 060 throw new IllegalArgumentException("Width must be larger than 0."); 061 } 062 if ((width & 1) == 0) 063 { 064 throw new IllegalArgumentException("Width must be odd."); 065 } 066 } 067 068 /** 069 * Determine the resulting sample for an array with the source sample 070 * and zero or more of its neighbors. 071 * This abstract method must be implemented by classes extending this operation. 072 * The array will hold <code>numSamples</code> samples, which will be stored 073 * starting at offset <code>0</code>. 074 * <p> 075 * Normally, <code>numSamples</code> is equal to {@link #getAreaWidth} times {@link #getAreaHeight}. 076 * Near the border of the image you may get less samples. 077 * Example: the top left sample of an image has only three neighbors (east, south-east and south), 078 * so you will only get four samples (three neighbors and the sample itself). 079 * @param samples the array holding the sample(s) 080 * @param numSamples number of samples in the array 081 * @return sample to be written to the output image 082 */ 083 public abstract int computeSample(int[] samples, int numSamples); 084 085 /** 086 * Returns the current area height. 087 * @return height of area window in pixels 088 * @see #setAreaHeight(int) 089 */ 090 public int getAreaHeight() 091 { 092 return areaHeight; 093 } 094 095 /** 096 * Returns the current area width. 097 * @return width of area window in pixels 098 * @see #setAreaWidth(int) 099 */ 100 public int getAreaWidth() 101 { 102 return areaWidth; 103 } 104 105 /** 106 * Applies the filter to one of the channels of an image. 107 */ 108 private void process(int channelIndex, IntegerImage in, IntegerImage out) 109 { 110 processBorders(channelIndex, in, out); 111 processCenter(channelIndex, in, out); 112 /* 113 final int HEIGHT = in.getHeight(); 114 final int WIDTH = in.getWidth(); 115 final int H_2 = areaWidth / 2; 116 final int V_2 = areaHeight / 2; 117 int processedItems = channelIndex * HEIGHT; 118 final int TOTAL_ITEMS = in.getNumChannels() * HEIGHT; 119 int[] samples = new int[areaWidth * areaHeight]; 120 for (int y = 0; y < HEIGHT; y++) 121 { 122 for (int x = 0; x < WIDTH; x++) 123 { 124 // collect samples from area 125 int numSamples = 0; 126 for (int v = y - V_2; v <= y + V_2; v++) 127 { 128 if (v >= 0 && v < HEIGHT) 129 { 130 for (int u = x - H_2; u <= x + H_2; u++) 131 { 132 if (u >= 0 && u < WIDTH) 133 { 134 samples[numSamples++] = in.getSample(channelIndex, u, v); 135 } 136 } 137 } 138 } 139 // determine and set output sample 140 out.putSample(channelIndex, x, y, computeSample(samples, numSamples)); 141 } 142 setProgress(processedItems++, TOTAL_ITEMS); 143 } 144 */ 145 } 146 147 private void process(IntegerImage in, IntegerImage out) 148 { 149 if (out == null) 150 { 151 out = (IntegerImage)in.createCompatibleImage(in.getWidth(), in.getHeight()); 152 setOutputImage(out); 153 } 154 for (int channelIndex = 0; channelIndex < in.getNumChannels(); channelIndex++) 155 { 156 process(channelIndex, in, out); 157 } 158 } 159 160 public void process() throws 161 MissingParameterException, 162 WrongParameterException 163 { 164 if (areaWidth == 0) 165 { 166 throw new MissingParameterException("Area width has not been initialized."); 167 } 168 if (areaHeight == 0) 169 { 170 throw new MissingParameterException("Area height has not been initialized."); 171 } 172 ensureInputImageIsAvailable(); 173 ensureImagesHaveSameResolution(); 174 PixelImage in = getInputImage(); 175 PixelImage out = getOutputImage(); 176 if (in instanceof GrayIntegerImage || in instanceof RGBIntegerImage) 177 { 178 process((IntegerImage)in, (IntegerImage)out); 179 } 180 else 181 { 182 throw new WrongParameterException("Input image must implement GrayIntegerImage or RGBIntegerImage."); 183 } 184 } 185 186 private void processBorders(int channelIndex, IntegerImage in, IntegerImage out) 187 { 188 /*processBorderNorthWest(channelIndex, in, out); 189 processBorderNorth(channelIndex, in, out); 190 processBorderNorthEast(channelIndex, in, out); 191 192 processBorderEast(channelIndex, in, out); 193 194 processBorderSouthEast(channelIndex, in, out); 195 processBorderSouth(channelIndex, in, out); 196 processBorderSouthWest(channelIndex, in, out); 197 198 processBorderWest(channelIndex, in, out);*/ 199 } 200 201 private void processCenter(int channelIndex, IntegerImage in, IntegerImage out) 202 { 203 final int HEIGHT = in.getHeight(); 204 final int WIDTH = in.getWidth(); 205 final int AREA_WIDTH = getAreaWidth(); 206 final int H_2 = AREA_WIDTH / 2; 207 final int AREA_HEIGHT = getAreaHeight(); 208 final int V_2 = AREA_HEIGHT / 2; 209 if (WIDTH < AREA_WIDTH || HEIGHT < AREA_HEIGHT) 210 { 211 return; 212 } 213 final int NUM_SAMPLES = AREA_WIDTH * AREA_HEIGHT; 214 final int TOTAL_ITEMS = in.getNumChannels() * HEIGHT; 215 int processedItems = channelIndex * HEIGHT + AREA_HEIGHT / 2; 216 int[] samples = new int[AREA_WIDTH * AREA_HEIGHT]; 217 for (int y1 = 0, y2 = V_2; y2 < HEIGHT - V_2; y1++, y2++) 218 { 219 for (int x1 = 0, x2 = H_2; x2 < WIDTH - H_2; x1++, x2++) 220 { 221 in.getSamples(channelIndex, x1, y1, areaWidth, areaHeight, samples, 0); 222 out.putSample(channelIndex, x2, y2, computeSample(samples, NUM_SAMPLES)); 223 } 224 setProgress(processedItems++, TOTAL_ITEMS); 225 } 226 } 227 228 /** 229 * Sets the area of the window to be used to determine each pixel's mean to 230 * the argument width and height. 231 * @param width width of window, must be 1 or larger 232 * @param height height of window, must be 1 or larger 233 * @see #setAreaHeight 234 * @see #setAreaWidth 235 */ 236 public void setArea(int width, int height) 237 { 238 setAreaWidth(width); 239 setAreaHeight(height); 240 } 241 242 /** 243 * Sets the height of the area of the window to be used to determine each pixel's mean to 244 * the argument value. 245 * @param height height of window, must be odd and 1 or larger 246 * @see #getAreaHeight 247 * @see #setArea 248 * @see #setAreaWidth 249 */ 250 public void setAreaHeight(int height) 251 { 252 checkAreaHeight(height); 253 areaHeight = height; 254 } 255 256 /** 257 * Sets the width of the area of the window to be used to determine each pixel's mean to 258 * the argument value. 259 * @param width width of window, must be odd and 1 or larger 260 * @see #getAreaWidth 261 * @see #setArea 262 * @see #setAreaHeight 263 */ 264 public void setAreaWidth(int width) 265 { 266 checkAreaWidth(width); 267 areaWidth = width; 268 } 269 }