001 /* 002 * GammaCorrection 003 * 004 * Copyright (c) 2001, 2002, 2003 Marco Schmidt. 005 * All rights reserved. 006 */ 007 008 package net.sourceforge.jiu.color.adjustment; 009 010 import net.sourceforge.jiu.data.GrayIntegerImage; 011 import net.sourceforge.jiu.data.IntegerImage; 012 import net.sourceforge.jiu.data.Palette; 013 import net.sourceforge.jiu.data.Paletted8Image; 014 import net.sourceforge.jiu.data.PixelImage; 015 import net.sourceforge.jiu.data.RGBIntegerImage; 016 import net.sourceforge.jiu.ops.LookupTableOperation; 017 import net.sourceforge.jiu.ops.MissingParameterException; 018 import net.sourceforge.jiu.ops.WrongParameterException; 019 020 /** 021 * Corrects the gamma of an image. 022 * Works with {@link net.sourceforge.jiu.data.GrayIntegerImage}, 023 * {@link net.sourceforge.jiu.data.RGBIntegerImage} and 024 * {@link net.sourceforge.jiu.data.Paletted8Image}. 025 * Only the palette is manipulated for paletted images. 026 * <p> 027 * Changes intensity values by applying the formula 028 * <em>f(x) = MAX * (x / MAX)<sup>(1 / gamma)</sup></em> to each 029 * <em>x</em> from <em>[0 ; MAX]</em> to them. 030 * The <em>MAX</em> value is the maximum value allowed for an intensity value of the 031 * corresponding channel. 032 * It is determined by calling {@link net.sourceforge.jiu.data.IntegerImage#getMaxSample} on 033 * the input image. 034 * The <em>gamma</em> parameter must be given to a <code>GammaCorrection</code> operation 035 * before the call to {@link #process} is made. 036 * The valid interval for <em>gamma</em> is (0.0 ; {@link #MAX_GAMMA}] 037 * (so 0.0 is not a valid value). 038 * Gamma values smaller than 1 will make the image darker, values 039 * larger than 1 will make it brighter. 040 * <h3>Usage example</h3> 041 * <pre> 042 * GammaCorrection gamma = new GammaCorrection(); 043 * gamma.setInputImage(image); 044 * gamma.setGamma(2.2); 045 * gamma.process(); 046 * PixelImage correctedImage = gamma.getOutputImage(); 047 * </pre> 048 * @author Marco Schmidt 049 */ 050 public class GammaCorrection extends LookupTableOperation 051 { 052 /** 053 * The maximum allowed value for gamma. 054 */ 055 public static final double MAX_GAMMA = 10.0; 056 private double gamma; 057 058 /** 059 * Creates a lookup table that holds all new values for samples 0 to 060 * numSamples - 1. 061 */ 062 private final int[] createLookupTable(int numSamples) 063 { 064 if (numSamples < 1) 065 { 066 throw new IllegalArgumentException("Number of samples argument must be one or larger."); 067 } 068 double g = 1.0 / gamma; 069 int[] result = new int[numSamples]; 070 final int MAX_SAMPLE = numSamples - 1; 071 final double MAX = MAX_SAMPLE; 072 for (int i = 0; i < numSamples; i++) 073 { 074 result[i] = (int)Math.round((MAX * Math.pow((i / MAX), g))); 075 if (result[i] < 0) 076 { 077 result[i] = 0; 078 } 079 if (result[i] > MAX_SAMPLE) 080 { 081 result[i] = MAX_SAMPLE; 082 } 083 } 084 return result; 085 } 086 087 /** 088 * Returns the gamma value to be used for this operation. 089 * @return gamma value between 0 (not included) and {@link #MAX_GAMMA} 090 */ 091 public double getGamma() 092 { 093 return gamma; 094 } 095 096 private void process(Paletted8Image in, Paletted8Image out) 097 { 098 if (out == null) 099 { 100 out = (Paletted8Image)in.createCompatibleImage(in.getWidth(), in.getHeight()); 101 setOutputImage(out); 102 } 103 Palette palette = out.getPalette(); 104 int numSamples = palette.getMaxValue() + 1; 105 final int[] LUT = createLookupTable(numSamples); 106 for (int c = 0; c < 3; c++) 107 { 108 for (int i = 0; i < palette.getNumEntries(); i++) 109 { 110 palette.putSample(c, i, LUT[palette.getSample(c, i)]); 111 } 112 } 113 for (int y = 0; y < in.getHeight(); y++) 114 { 115 for (int x = 0; x < in.getWidth(); x++) 116 { 117 out.putSample(x, y, in.getSample(x, y)); 118 } 119 setProgress(y, in.getHeight()); 120 } 121 } 122 123 public void process() throws 124 MissingParameterException, 125 WrongParameterException 126 { 127 ensureInputImageIsAvailable(); 128 PixelImage in = getInputImage(); 129 if (in instanceof Paletted8Image) 130 { 131 process((Paletted8Image)getInputImage(), (Paletted8Image)getOutputImage()); 132 } 133 else 134 if (in instanceof GrayIntegerImage || in instanceof RGBIntegerImage) 135 { 136 setNumTables(in.getNumChannels()); 137 IntegerImage ii = (IntegerImage)in; 138 for (int channelIndex = 0; channelIndex < in.getNumChannels(); channelIndex++) 139 { 140 int numSamples = ii.getMaxSample(channelIndex) + 1; 141 int[] table = createLookupTable(numSamples); 142 setTable(channelIndex, table); 143 } 144 super.process(); 145 } 146 else 147 { 148 throw new WrongParameterException("Unsupported image type: " + in.getClass().getName()); 149 } 150 } 151 152 /** 153 * Sets a new gamma value to be used in this operation. 154 * @param newGamma the new gamma value must be > 0.0 and <= MAX_GAMMA 155 * @throws IllegalArgumentException if the argument is not in the described interval 156 */ 157 public void setGamma(double newGamma) 158 { 159 if (newGamma <= 0.0) 160 { 161 throw new IllegalArgumentException("Gamma must be larger than 0.0; got " + newGamma); 162 } 163 if (newGamma > MAX_GAMMA) 164 { 165 throw new IllegalArgumentException("Gamma must be at most " + MAX_GAMMA + "; got " + newGamma); 166 } 167 gamma = newGamma; 168 } 169 }