package psychWithJava; /* * CLUT.java * * Copyright (C) 2005-2008 Huseyin Boyaci. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License version 2 for more details * (a copy is included in the LICENSE file that accompanied this code) * (also available at http://www.gnu.org) You should have received a copy of * the GNU General Public License along with this program; if not, write to * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * */ import static java.lang.Math.*; import java.io.InputStream; import java.util.NoSuchElementException; import java.util.Scanner; /** * Provides methods to perform (inverse) color lookup operations. * * @author Huseyin Boyaci */ public class CLUT { private int DIM; private double maxLum; private double[] pix2Lum; private int[] lum2Pix; private boolean monotonicIncrease = true; private boolean monotonicDecrease = true; /** * Constructs a CLUT object for an 8 bit video display system. * A file with name "filename" contains the 28=256 * luminance values, one for each pixel from 255 to 0 in descending order. * The file * may contain the absolute luminance values, but by default CLUT stores and * works with relative values between 0.0 and 255.0. * * @param filename * the name of the file containing luminance values for all pixels * from 255 to 0 in descending order. * * @throws IllegalArgumentException * if the file has different number of elements than 256 */ public CLUT(String filename){ this(filename,8); } /** * Constructs a CLUT object for an 8 bit video display system. * A double array "table" contains the 28=256 * luminance values, one for each pixel from 255 to 0 in descending order. * The table may contain the absolute luminance values, but by default CLUT * stores and * works with relative values between 0.0 and 255.0. * * @param table * the double array containing luminance values for all pixels * from 255 to 0 in descending order. * * @throws IllegalArgumentException * if the file has different number of elements than 256 */ public CLUT(double[] table){ this(table,8); } /** * Constructs a CLUT object for a system with an arbitrary number of bits. * File with name "filename" contains the 2bits lines of * luminance values for each pixel from 2bits-1 to 0 * in descending order. The file * may contain the absolute luminance values, but by default CLUT stores and * works with relative values between 0.0 and 2bits-1. * * @param filename * the name of the file containing luminance values for all pixels * from 2bits-1 to 0 in descending order. * * @param bits * number of bits of the system. */ public CLUT(String filename, int bits) throws IllegalArgumentException{ InputStream stream = CLUT.class.getResourceAsStream(filename); Scanner in = new Scanner(stream); DIM = (int)pow(2,bits); pix2Lum = new double[DIM]; lum2Pix = new int[DIM]; maxLum = 0; double[] table = new double[DIM]; for (int pix = DIM - 1; pix > -1; pix--) { try { table[pix] = in.nextDouble(); } catch (NoSuchElementException e) { throw new IllegalArgumentException( "Error in CLUT(String): wrong input file - file too short"); } } if (in.hasNext()) throw new IllegalArgumentException( "Error in CLUT(String): suspicious input file - file too long"); in.close(); setClut(table); } /** * Constructs a CLUT object for a system with an arbitrary number of bits. * Double array table contains the 2bits * luminance values for each pixel from 2bits-1 to 0 * in descending order. The table * may contain the absolute luminance values, but by default CLUT stores and * works with relative values between 0.0 and 2bits-1. * * @param table * the double array containing luminance values for all pixels * from 2bits-1 to 0 in descending order. * * @param bits * number of bits of the system. */ public CLUT(double[] table, int bits) throws IllegalArgumentException{ DIM = (int)pow(2,bits); pix2Lum = new double[DIM]; lum2Pix = new int[DIM]; if (table.length != DIM) throw new IllegalArgumentException( "Error in CLUT(double[], int): incompatible data"); else setClut(table); } /** * Sets a new color look-up table (CLUT). * * @param table * new look-up table */ public void setClut(double[] table){ if (table.length != DIM) throw new IllegalArgumentException( "Error in CLUT8.setClut(double[]): incompatible data"); maxLum = 0; pix2Lum = table; double lastLum = pix2Lum[0]; for (double lum : pix2Lum) { maxLum = max(maxLum, lum); if (monotonicIncrease && lastLum > lum) monotonicIncrease = false; else if(monotonicDecrease && lastLum < lum) monotonicDecrease = false; lastLum = lum; } if(!monotonicIncrease && !monotonicDecrease){ System.err .println("Warning in CLUT.setClut(double[]): " + "LUT is not monotonically increasing or decreasing, " + "speed may degrade"); } for (int pix = 0; pix < DIM; pix++) pix2Lum[pix] *= (double)(DIM-1) / maxLum; if(monotonicIncrease){ int lastPixel = 0; for (int lum = 0; lum < DIM; lum++) { double diff = Double.MAX_VALUE; for (int j = lastPixel; j < DIM; j++) { double diffTmp = abs(pix2Lum[j] - lum); if (diffTmp == 0) { lum2Pix[lum] = j; lastPixel = j; break; } else if (diffTmp <= diff) { lum2Pix[lum] = j; lastPixel = j; diff = diffTmp; } else break; } } } else if(monotonicDecrease){ int lastPixel = 0; for (int lum = DIM-1; lum >= 0; lum--) { double diff = Double.MAX_VALUE; for (int j = lastPixel; j < DIM; j++) { double diffTmp = abs(pix2Lum[j] - lum); if (diffTmp == 0) { System.err.println(lum); lum2Pix[lum] = j; lastPixel = j; break; } else if (diffTmp <= diff) { lum2Pix[lum] = j; lastPixel = j; diff = diffTmp; } else break; } } } else{ for (int lum = 0; lum < DIM; lum++) { double diff = Double.MAX_VALUE; for (int j = 0; j < DIM; j++) { double diffTmp = abs(pix2Lum[j] - lum); if (diffTmp == 0) { lum2Pix[lum] = j; break; } else if (diffTmp <= diff) { lum2Pix[lum] = j; diff = diffTmp; } } } } } /** * returns the maximum available luminance * * @return maximum luminance */ public double getMaxLum() { return maxLum; } /** * Returns the pixel whose luminance is CLOSEST to the required luminance. * This is a slower but more precise lookup for real (non-whole) values * between 0 and 2bits-1 * * @param lum * required Luminance (real number, i.e. double) * * @return pixel whose luminance is the closest to required Luminance */ public int lum2Pix(double lum) { int intLum = (int) round(lum); int pixel = lum2Pix[intLum]; if (lum == (double) intLum) return pixel; else { if(monotonicIncrease){ int pixelStart = lum2Pix[max(intLum - 1, 0)]; int pixelEnd = lum2Pix[min(intLum + 1, DIM - 1)]; double diff = Double.MAX_VALUE; for (int j = pixelStart; j <= pixelEnd; j++) { double diffTmp = abs(pix2Lum[j] - lum); if (diffTmp == 0) { pixel = j; break; } else if (diffTmp <= diff) { pixel = j; diff = diffTmp; } else break; } } else if(monotonicDecrease){ int pixelStart = lum2Pix[min(intLum + 1, DIM-1)]; int pixelEnd = lum2Pix[max(intLum - 1, 0)]; double diff = Double.MAX_VALUE; for (int j = pixelStart; j <= pixelEnd; j++) { double diffTmp = abs(pix2Lum[j] - lum); if (diffTmp == 0) { pixel = j; break; } else if (diffTmp <= diff) { pixel = j; diff = diffTmp; } else break; } } else { double diff = Double.MAX_VALUE; for (int j = 0; j < DIM; j++) { double diffTmp = abs(pix2Lum[j] - lum); if (diffTmp == 0) { pixel = j; break; } else if (diffTmp <= diff) { pixel = j; diff = diffTmp; } } } return pixel; } } /** * Returns the pixel whose luminance is CLOSEST to the required luminance. * This is a faster lookup for whole numbers between 0 and 2bits-1. * * @param lum * required Luminance (whole number, integer) * * @return pixel whose luminance is the closest to required Luminance */ public int lum2Pix(int lum) { return lum2Pix[lum]; } /** * Returns the RELATIVE luminance of the given pixel. The relative value is * between 0 and 2bits-1 (not 0 and 1) * * @param pixel * whose luminance is sought * @return Luminance8 of the pixel */ public double pix2Lum(int pixel) { return pix2Lum[pixel]; } }