001 /* 002 * LookupTableOperation 003 * 004 * Copyright (c) 2001, 2002 Marco Schmidt <marcoschmidt@users.sourceforge.net> 005 * All rights reserved. 006 */ 007 008 package net.sourceforge.jiu.ops; 009 010 import net.sourceforge.jiu.data.PixelImage; 011 import net.sourceforge.jiu.data.IntegerImage; 012 import net.sourceforge.jiu.ops.ImageToImageOperation; 013 import net.sourceforge.jiu.ops.MissingParameterException; 014 import net.sourceforge.jiu.ops.WrongParameterException; 015 016 /** 017 * An operation that replaces samples with values taken from a lookup table. 018 * Operations where each pixel is treated independently from its neighbors 019 * and where a pixel value is always mapped to the same new pixel value 020 * can be implemented this way. 021 * 022 * @author Marco Schmidt 023 * @since 0.6.0 024 */ 025 public abstract class LookupTableOperation extends ImageToImageOperation 026 { 027 private int[][] intTables; 028 private int numTables; 029 030 /** 031 * Creates a LookupTableOperation for one lookup table. 032 */ 033 public LookupTableOperation() 034 { 035 this(1); 036 } 037 038 /** 039 * Creates an object of this class, calling the super constructor with two <code>null</code> 040 * arguments and allocates space for the argument number of lookup tables. 041 * @param numTables number of tables to be used in this operation 042 */ 043 public LookupTableOperation(int numTables) 044 { 045 super(null, null); 046 if (numTables < 1) 047 { 048 throw new IllegalArgumentException("The number of tables must be at least 1; got " + numTables); 049 } 050 intTables = new int[numTables][]; 051 this.numTables = numTables; 052 } 053 054 /** 055 * Returns the number of tables in this operation. 056 * @return number of tables 057 */ 058 public int getNumTables() 059 { 060 return numTables; 061 } 062 063 /** 064 * Returns one of the internal <code>int</code> lookup tables. 065 * @param channelIndex the zero-based index of the table to be returned; 066 * from 0 to getNumTables() - 1 067 * @return the channelIndex'th table 068 */ 069 public int[] getTable(int channelIndex) 070 { 071 return intTables[channelIndex]; 072 } 073 074 public void prepareImages() throws 075 MissingParameterException, 076 WrongParameterException 077 { 078 ensureInputImageIsAvailable(); 079 PixelImage in = getInputImage(); 080 if (!(in instanceof IntegerImage)) 081 { 082 throw new WrongParameterException("Input image must be of type IntegerImage."); 083 } 084 PixelImage out = getOutputImage(); 085 if (out == null) 086 { 087 out = in.createCompatibleImage(in.getWidth(), in.getHeight()); 088 setOutputImage(out); 089 } 090 else 091 { 092 if (in.getNumChannels() != out.getNumChannels()) 093 { 094 throw new WrongParameterException("Output image must have same number of channels as input image."); 095 } 096 ensureImagesHaveSameResolution(); 097 } 098 } 099 100 public void process() throws 101 MissingParameterException, 102 WrongParameterException 103 { 104 prepareImages(); 105 process((IntegerImage)getInputImage(), (IntegerImage)getOutputImage()); 106 } 107 108 private void process(IntegerImage in, IntegerImage out) 109 { 110 boolean useFirstTableOnly = getNumTables() < in.getNumChannels(); 111 final int TOTAL_ITEMS = in.getHeight() * in.getNumChannels(); 112 int processedItems = 0; 113 for (int channelIndex = 0; channelIndex < in.getNumChannels(); channelIndex++) 114 { 115 int tableIndex; 116 if (useFirstTableOnly) 117 { 118 tableIndex = 0; 119 } 120 else 121 { 122 tableIndex = channelIndex; 123 } 124 process(in, out, channelIndex, tableIndex, processedItems, TOTAL_ITEMS); 125 processedItems += in.getHeight(); 126 } 127 } 128 129 private void process(IntegerImage in, IntegerImage out, final int CHANNEL_INDEX, 130 int tableIndex, int processedItems, final int TOTAL_ITEMS) 131 { 132 final int[] TABLE = getTable(tableIndex); 133 final int WIDTH = in.getWidth(); 134 final int HEIGHT = in.getHeight(); 135 for (int y = 0; y < HEIGHT; y++) 136 { 137 for (int x = 0; x < WIDTH; x++) 138 { 139 out.putSample(CHANNEL_INDEX, x, y, TABLE[in.getSample(CHANNEL_INDEX, x, y)]); 140 } 141 setProgress(processedItems++, TOTAL_ITEMS); 142 } 143 } 144 145 /** 146 * Resets the number of tables to be used in this operation to the 147 * argument and drops all actual table data initialized so far. 148 * After a call to this method, {@link #getTable} will return 149 * <code>null</code> as long as no new table data is provided 150 * via {@link #setTable} or {@link #setTables}. 151 * @param numberOfTables the new number of tables for this operation, must be <code>1</code> or larger 152 * @throws IllegalArgumentException if the number is zero or smaller 153 */ 154 public void setNumTables(int numberOfTables) 155 { 156 if (numberOfTables < 1) 157 { 158 throw new IllegalArgumentException("Number of tables argument must be larger than zero."); 159 } 160 numTables = numberOfTables; 161 intTables = new int[numTables][]; 162 } 163 164 /** 165 * Provides a new lookup table for one of the channels. 166 * @param channelIndex the index of the channel for which a table is provided; must be at least <code>0</code> and smaller than {@link #getNumTables} 167 * @param tableData the actual table to be used for lookup 168 * @throws IllegalArgumentException if the channel index is not in the valid interval (see above) 169 */ 170 public void setTable(int channelIndex, int[] tableData) 171 { 172 if (channelIndex < 0) 173 { 174 throw new IllegalArgumentException("The channelIndex argument must be at least 0; got " + channelIndex); 175 } 176 if (channelIndex >= getNumTables()) 177 { 178 throw new IllegalArgumentException("The channelIndex argument must be smaller than the number of tables " + 179 getNumTables() + "; got " + channelIndex); 180 } 181 intTables[channelIndex] = tableData; 182 } 183 184 /** 185 * Sets the tables for all channels to the argument table. 186 * Useful when the same table can be used for all channels. 187 * @param tableData the data that will be used as lookup table for all channels 188 */ 189 public void setTables(int[] tableData) 190 { 191 for (int i = 0; i < getNumTables(); i++) 192 { 193 setTable(i, tableData); 194 } 195 } 196 }