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-2016 Wilhelm Burger, Mark J. Burge. All rights reserved. 007 * Visit http://imagingbook.com for additional details. 008 *******************************************************************************/ 009 010package imagingbook.lib.filters; 011 012import ij.IJ; 013import ij.process.ColorProcessor; 014import ij.process.ImageProcessor; 015import imagingbook.lib.image.ImageAccessor; 016import imagingbook.lib.image.OutOfBoundsStrategy; 017 018/** 019 * This abstract class represents a generic filter that, when applied to 020 * an {@code ImageProcessor} object performs all pixel-level 021 * iterations automatically. 022 * Concrete implementations of this class need to define only two methods:<br> 023 * {@code float filterPixel(ImageAccessor.Scalar, int, int)} and<br> 024 * {@code float[] filterPixel(ImageAccessor.Rgb, int, int)}.<br> 025 * See {@link LinearFilter} for a sample implementation. 026 * 027 * <br> 028 * Note that this is experimental code! 029 * 030 * @author wilbur 031 * @version 2016/11/01 032 * 033 */ 034public abstract class GenericFilter { 035 036 /* TODO: PASS THE IMAGE PROCESSOR of the original image and 037 * set up width/height, accessors etc., then use apply without processor argument?? 038 * Allow source/target to be of different types? 039 * Implement using interfaces (for gray/color)? 040 * 041 */ 042 043 private OutOfBoundsStrategy obs = OutOfBoundsStrategy.NearestBorder; 044 045 /** 046 * Set the out-of-bounds strategy of this {@link GenericFilter}. See {@link OutOfBoundsStrategy}. 047 * @param obs the out-of-bounds strategy 048 */ 049 public void setOutOfBoundsStrategy(OutOfBoundsStrategy obs) { 050 this.obs = obs; 051 } 052 053 protected GenericFilter() { 054 } 055 056 /** 057 * Calculates and returns the filter result for a single pixel 058 * at the given position. 059 * 060 * @param source the {@link ImageAccessor.Scalar} representing the source (scalar-valued) image 061 * @param u the horizontal pixel position 062 * @param v the vertical pixel position 063 * @return the resulting (scalar) filter value for the specified pixel position 064 */ 065 public abstract float filterPixel(ImageAccessor.Scalar source, int u, int v); 066 067 /** 068 * Calculates and returns the filter result for a single pixel 069 * at the given position. 070 * 071 * @param source the {@link ImageAccessor.Rgb} representing the source (RGB) image 072 * @param u the horizontal pixel position 073 * @param v the vertical pixel position 074 * @return the resulting (RGB) filter value for the specified pixel position 075 */ 076 public abstract float[] filterPixel(ImageAccessor.Rgb source, int u, int v); 077 078 079 /** 080 * Dispatch work depending on actual (runtime) type of processor. 081 * This is ugly but we want to avoid generic types (which would 082 * not be of much help in this case anyway). 083 * 084 * @param ip the image this filter is applied to (destructively) 085 */ 086 public void applyTo(ImageProcessor ip) { // TODO: check for target == null? 087 final int w = ip.getWidth(); 088 final int h = ip.getHeight(); 089 ImageProcessor ipCopy = ip.duplicate(); 090 091 if (ip instanceof ColorProcessor) { 092 ImageAccessor.Rgb iaOrig = ImageAccessor.Rgb.create(ip, obs, null); 093 ImageAccessor.Rgb iaCopy = ImageAccessor.Rgb.create(ipCopy, obs, null); 094 for (int v = 0; v < h; v++) { 095 for (int u = 0; u < w; u++) { 096 //int p = (int) filterPixel(iaCopy, u, v); 097 float[] rgb = filterPixel(iaCopy, u, v); 098 iaOrig.setPix(u, v, rgb); 099 } 100 IJ.showProgress(v, h); 101 } 102 } 103 else { 104 ImageAccessor.Scalar iaOrig = ImageAccessor.Scalar.create(ip, obs, null); 105 ImageAccessor.Scalar iaCopy = ImageAccessor.Scalar.create(ipCopy, obs, null); 106 for (int v = 0; v < h; v++) { 107 for (int u = 0; u < w; u++) { 108 float p = filterPixel(iaCopy, u, v); 109 iaOrig.setVal(u, v, p); 110 } 111 IJ.showProgress(v, h); 112 } 113 } 114 } 115 116}