Package net.sourceforge.jiu.color.reduction

Source Code of net.sourceforge.jiu.color.reduction.AutoDetectColorType

/*
* AutoDetectColorType
*
* Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Marco Schmidt.
* All rights reserved.
*/

package net.sourceforge.jiu.color.reduction;

import net.sourceforge.jiu.color.data.Histogram3D;
import net.sourceforge.jiu.color.data.OnDemandHistogram3D;
import net.sourceforge.jiu.data.BilevelImage;
import net.sourceforge.jiu.data.Gray16Image;
import net.sourceforge.jiu.data.Gray8Image;
import net.sourceforge.jiu.data.IntegerImage;
import net.sourceforge.jiu.data.MemoryBilevelImage;
import net.sourceforge.jiu.data.MemoryGray8Image;
import net.sourceforge.jiu.data.MemoryPaletted8Image;
import net.sourceforge.jiu.data.MemoryRGB24Image;
import net.sourceforge.jiu.data.Palette;
import net.sourceforge.jiu.data.Paletted8Image;
import net.sourceforge.jiu.data.PixelImage;
import net.sourceforge.jiu.data.RGB24Image;
import net.sourceforge.jiu.data.RGB48Image;
import net.sourceforge.jiu.data.RGBIndex;
import net.sourceforge.jiu.data.RGBIntegerImage;
import net.sourceforge.jiu.ops.MissingParameterException;
import net.sourceforge.jiu.ops.Operation;
import net.sourceforge.jiu.ops.WrongParameterException;

/**
* Detects the minimum (in terms of memory) color type of an image.
* Can convert the original image to that new input type on demand.
* <p>
* Input parameters: image to be examined, boolean that specifies whether
* conversion will be performed (default is true, conversion is performed).
* Output parameters: converted image, boolean that expresses whether
* a conversion was possible.
* <p>
* Supported types for input image: RGB24Image, Gray8Image, Paletted8Image.
* <p>
* BilevelImage is not supported because there is no smaller image type,
* so bilevel images cannot be reduced.
* <p>
* This operation is not a {@link net.sourceforge.jiu.ops.ImageToImageOperation} because this
* class need not necessarily produce a new image
* (with {@link #setConversion}(false)).
* <h3>Usage example</h3>
* This code snippet loads an image and attempts to reduce it to the
* minimum color type that will hold it.
* <pre>
* PixelImage image = ImageLoader.load("test.bmp");
* AutoDetectColorType op = new AutoDetectColorType();
* op.setInputImage(image);
* op.process();
* if (op.isReducible())
* {
*   image = op.getOutputImage();
* }
* </pre>
*
* @author Marco Schmidt
*/
public class AutoDetectColorType extends Operation
{
  public static final int TYPE_UNKNOWN = -1;
  public static final int TYPE_BILEVEL = 0;
  public static final int TYPE_GRAY16 = 4;
  public static final int TYPE_GRAY8 = 1;
  public static final int TYPE_PALETTED8 = 2;
  public static final int TYPE_RGB24 = 3;
  public static final int TYPE_RGB48 = 5;

  private PixelImage inputImage;
  private PixelImage outputImage;
  private boolean doConvert;
  private int type;
  private Histogram3D hist;

  public AutoDetectColorType()
  {
    doConvert = true;
    type = TYPE_UNKNOWN;
  }

  /**
   * Creates a bilevel image from any grayscale (or RGB) image
   * that has been checked to be bilevel.
   */
  private void createBilevelFromGrayOrRgb(IntegerImage in)
  {
    MemoryBilevelImage out = new MemoryBilevelImage(in.getWidth(), in.getHeight());
    out.clear(BilevelImage.BLACK);
    for (int y = 0; y < in.getHeight(); y++)
    {
      for (int x = 0; x < in.getWidth(); x++)
      {
        if (in.getSample(0, x, y) != 0)
        {
          out.putWhite(x, y);
        }
      }
      setProgress(y, in.getHeight());
    }
    outputImage = out;
  }

  private void createBilevelFromPaletted(Paletted8Image in)
  {
    Palette palette = in.getPalette();
    MemoryBilevelImage out = new MemoryBilevelImage(in.getWidth(), in.getHeight());
    out.clear(BilevelImage.BLACK);
    for (int y = 0; y < in.getHeight(); y++)
    {
      for (int x = 0; x < in.getWidth(); x++)
      {
        if (palette.getSample(RGBIndex.INDEX_RED, in.getSample(0, x, y)) != 0)
        {
          out.putWhite(x, y);
        }
      }
      setProgress(y, in.getHeight());
    }
    outputImage = out;
  }

  // works for RGB24 and RGB48 input image, assumed that
  // a matching output image type was chosen (Gray8 for RGB24, Gray16 for
  // RGB48)
  private void createGrayFromRgb(IntegerImage in, IntegerImage out)
  {
    for (int y = 0; y < in.getHeight(); y++)
    {
      for (int x = 0; x < in.getWidth(); x++)
      {
        out.putSample(0, x, y, in.getSample(0, x, y));
      }
      setProgress(y, in.getHeight());
    }
    outputImage = out;
  }

  private void createGray8FromGray16(Gray16Image in)
  {
    Gray8Image out = new MemoryGray8Image(in.getWidth(), in.getHeight());
    for (int y = 0; y < in.getHeight(); y++)
    {
      for (int x = 0; x < in.getWidth(); x++)
      {
        out.putSample(0, x, y, in.getSample(0, x, y) & 0xff);
      }
      setProgress(y, in.getHeight());
    }
    outputImage = out;
  }

  // assumes that in fact has a palette with gray colors only
  private void createGray8FromPaletted8(Paletted8Image in, Gray8Image out)
  {
    Palette palette = in.getPalette();
    for (int y = 0; y < in.getHeight(); y++)
    {
      for (int x = 0; x < in.getWidth(); x++)
      {
        out.putSample(0, x, y, palette.getSample(0, in.getSample(0, x, y)));
      }
      setProgress(y, in.getHeight());
    }
    outputImage = out;
  }

  private void createPaletted8FromRgb24(RGB24Image in)
  {
    // create palette from histogram
    int uniqueColors = hist.getNumUsedEntries();
    Palette palette = new Palette(uniqueColors, 255);
    int index = 0;
    for (int r = 0; r < 256; r++)
    {
      for (int g = 0; g < 256; g++)
      {
        for (int b = 0; b < 256; b++)
        {
          if (hist.getEntry(r, g, b) != 0)
          {
            hist.setEntry(r, g, b, index);
            palette.putSample(RGBIndex.INDEX_RED, index, r);
            palette.putSample(RGBIndex.INDEX_GREEN, index, g);
            palette.putSample(RGBIndex.INDEX_BLUE, index, b);
            index++;
          }
        }
      }
    }
    Paletted8Image out = new MemoryPaletted8Image(in.getWidth(), in.getHeight(), palette);
    for (int y = 0; y < in.getHeight(); y++)
    {
      for (int x = 0; x < in.getWidth(); x++)
      {
        int red = in.getSample(RGBIndex.INDEX_RED, x, y);
        int green = in.getSample(RGBIndex.INDEX_GREEN, x, y);
        int blue = in.getSample(RGBIndex.INDEX_BLUE, x, y);
        out.putSample(0, x, y, hist.getEntry(red, green, blue));
      }
      setProgress(y, in.getHeight());
    }
    outputImage = out;
  }

  private void createPaletted8FromRgb48(RGB48Image in)
  {
    // create palette from histogram
    int uniqueColors = hist.getNumUsedEntries();
    Palette palette = new Palette(uniqueColors, 255);
    int index = 0;
    for (int r = 0; r < 256; r++)
    {
      for (int g = 0; g < 256; g++)
      {
        for (int b = 0; b < 256; b++)
        {
          if (hist.getEntry(r, g, b) != 0)
          {
            hist.setEntry(r, g, b, index);
            palette.putSample(RGBIndex.INDEX_RED, index, r);
            palette.putSample(RGBIndex.INDEX_GREEN, index, g);
            palette.putSample(RGBIndex.INDEX_BLUE, index, b);
            index++;
          }
        }
      }
    }
    Paletted8Image out = new MemoryPaletted8Image(in.getWidth(), in.getHeight(), palette);
    for (int y = 0; y < in.getHeight(); y++)
    {
      for (int x = 0; x < in.getWidth(); x++)
      {
        int red = in.getSample(RGBIndex.INDEX_RED, x, y) >> 8;
        int green = in.getSample(RGBIndex.INDEX_GREEN, x, y) >> 8;
        int blue = in.getSample(RGBIndex.INDEX_BLUE, x, y) >> 8;
        out.putSample(0, x, y, hist.getEntry(red, green, blue));
      }
      setProgress(y, in.getHeight());
    }
    outputImage = out;
  }

  private void createRgb24FromRgb48(RGB48Image in, RGB24Image out)
  {
    for (int y = 0; y < in.getHeight(); y++)
    {
      for (int x = 0; x < in.getWidth(); x++)
      {
        out.putSample(RGBIndex.INDEX_RED, x, y, in.getSample(RGBIndex.INDEX_RED, x, y) >> 8);
        out.putSample(RGBIndex.INDEX_GREEN, x, y, in.getSample(RGBIndex.INDEX_GREEN, x, y) >> 8);
        out.putSample(RGBIndex.INDEX_BLUE, x, y, in.getSample(RGBIndex.INDEX_BLUE, x, y) >> 8);
      }
      setProgress(y, in.getHeight());
    }
    outputImage = out;
  }


  /**
   * Returns the reduced output image if one was created in {@link #process()}.
   * @return newly-created output image
   */
  public PixelImage getOutputImage()
  {
    return outputImage;
  }

  /**
   * Returns the type of the minimum image type found (one of the TYPE_xyz constants
   * of this class).
   * Can only be called after a successful call to process.
   */
  public int getType()
  {
    return type;
  }

  /**
   * This method can be called after {@link #process()} to find out if the input
   * image in fact can be reduced to a &quot;smaller&quot; image type.
   * If this method returns <code>true</code> and if conversion was desired by the
   * user (can be specified via {@link #setConversion}), the reduced image can
   * be retrieved via {@link #getOutputImage()}.
   * @return if image was found to be reducible in process()
   */
  public boolean isReducible()
  {
    return type != TYPE_UNKNOWN;
  }

  // works for Gray8 and Gray16
  private boolean isGrayBilevel(IntegerImage in)
  {
    final int HEIGHT = in.getHeight();
    final int MAX = in.getMaxSample(0);
    for (int y = 0; y < HEIGHT; y++)
    {
      for (int x = 0; x < in.getWidth(); x++)
      {
        int value = in.getSample(0, x, y);
        if (value != 0 && value != MAX)
        {
          return false; // not a grayscale image
        }
      }
    }
    return true;
  }

  private boolean isGray16Gray8(Gray16Image in)
  {
    final int HEIGHT = in.getHeight();
    final int WIDTH = in.getWidth();
    for (int y = 0; y < HEIGHT; y++)
    {
      for (int x = 0; x < WIDTH; x++)
      {
        int value = in.getSample(0, x, y);
        int lsb = value & 0xff;
        int msb = (value >> 8) & 0xff;
        if (lsb != msb)
        {
          return false;
        }
      }
    }
    return true;
  }

  private boolean isRgb48Gray8(RGB48Image in)
  {
    final int HEIGHT = in.getHeight();
    for (int y = 0; y < HEIGHT; y++)
    {
      for (int x = 0; x < in.getWidth(); x++)
      {
        int red = in.getSample(RGBIndex.INDEX_RED, x, y);
        int green = in.getSample(RGBIndex.INDEX_GREEN, x, y);
        int blue = in.getSample(RGBIndex.INDEX_BLUE, x, y);
        if (red != green || green != blue)
        {
          return false;
        }
        int lsb = red & 0xff;
        int msb = red >> 8;
        if (lsb != msb)
        {
          return false;
        }
      }
    }
    return true;
  }

  /**
   * Assumes that it has already been verified that the input 48 bpp
   * RGB image is also a 24 bpp RGB image.
   * @param in input image to be checked
   * @return if this image can be losslessly converted to a Paletted8Image
   */
  private boolean isRgb48Paletted8(RGB48Image in)
  {
    int uniqueColors = 0;
    hist = new OnDemandHistogram3D(255, 255, 255);
    for (int y = 0; y < in.getHeight(); y++)
    {
      for (int x = 0; x < in.getWidth(); x++)
      {
        int red = in.getSample(RGBIndex.INDEX_RED, x, y) >> 8;
        int green = in.getSample(RGBIndex.INDEX_GREEN, x, y) >> 8;
        int blue = in.getSample(RGBIndex.INDEX_BLUE, x, y) >> 8;
        if (hist.getEntry(red, green, blue) == 0)
        {
          hist.increaseEntry(red, green, blue);
          uniqueColors++;
          if (uniqueColors > 256)
          {
            return false;
          }
        }
      }
    }
    return true;
  }

  private boolean isRgb48Rgb24(RGB48Image in)
  {
    final int HEIGHT = in.getHeight();
    for (int y = 0; y < HEIGHT; y++)
    {
      for (int x = 0; x < in.getWidth(); x++)
      {
        for (int channel = 0; channel < 3; channel++)
        {
          int sample = in.getSample(channel, x, y);
          if ((sample & 0xff) != ((sample & 0xff00) >> 8))
          {
            return false;
          }
        }
      }
    }
    return true;
  }

  // works for RGB24 and RGB48
  private boolean isRgbBilevel(IntegerImage in)
  {
    final int HEIGHT = in.getHeight();
    final int MAX = in.getMaxSample(0);
    for (int y = 0; y < HEIGHT; y++)
    {
      for (int x = 0; x < in.getWidth(); x++)
      {
        int red = in.getSample(RGBIndex.INDEX_RED, x, y);
        int green = in.getSample(RGBIndex.INDEX_GREEN, x, y);
        int blue = in.getSample(RGBIndex.INDEX_BLUE, x, y);
        if (red != green || green != blue || (blue != && blue != MAX))
        {
          return false;
        }
      }
    }
    return true;
  }

  /**
   * Returns if the input RGB image can be losslessly converted to
   * a grayscale image.
   * @param in RGB image to be checked
   * @return true if input is gray, false otherwise
   */
  private boolean isRgbGray(RGBIntegerImage in)
  {
    final int HEIGHT = in.getHeight();
    for (int y = 0; y < HEIGHT; y++)
    {
      for (int x = 0; x < in.getWidth(); x++)
      {
        int red = in.getSample(RGBIndex.INDEX_RED, x, y);
        int green = in.getSample(RGBIndex.INDEX_GREEN, x, y);
        int blue = in.getSample(RGBIndex.INDEX_BLUE, x, y);
        if (red != green || green != blue)
        {
          return false;
        }
      }
    }
    return true;
  }

  private boolean isRgb24Paletted8(RGB24Image in)
  {
    int uniqueColors = 0;
    hist = new OnDemandHistogram3D(255, 255, 255);
    for (int y = 0; y < in.getHeight(); y++)
    {
      for (int x = 0; x < in.getWidth(); x++)
      {
        int red = in.getSample(RGBIndex.INDEX_RED, x, y);
        int green = in.getSample(RGBIndex.INDEX_GREEN, x, y);
        int blue = in.getSample(RGBIndex.INDEX_BLUE, x, y);
        if (hist.getEntry(red, green, blue) == 0)
        {
          hist.increaseEntry(red, green, blue);
          uniqueColors++;
          if (uniqueColors > 256)
          {
            return false;
          }
        }
      }
    }
    return true;
  }

  public void process() throws
    MissingParameterException,
    WrongParameterException
  {
    if (inputImage == null)
    {
      throw new MissingParameterException("No input image available");
    }
    // GRAY8
    if (inputImage instanceof Gray8Image)
    {
      if (isGrayBilevel((Gray8Image)inputImage))
      {
        type = TYPE_BILEVEL;
        if (doConvert)
        {
          createBilevelFromGrayOrRgb((Gray8Image)inputImage);
        }
      }
    }
    else
    // GRAY16
    if (inputImage instanceof Gray16Image)
    {
      if (isGrayBilevel((Gray16Image)inputImage))
      {
        type = TYPE_BILEVEL;
        if (doConvert)
        {
          createBilevelFromGrayOrRgb((Gray16Image)inputImage);
        }
      }
      else
      if (isGray16Gray8((Gray16Image)inputImage))
      {
        type = TYPE_GRAY16;
        if (doConvert)
        {
          createGray8FromGray16((Gray16Image)inputImage);
        }
      }
    }
    else
    // RGB24
    if (inputImage instanceof RGB24Image)
    {
      if (isRgbBilevel((RGB24Image)inputImage))
      {
        type = TYPE_BILEVEL;
        if (doConvert)
        {
          createBilevelFromGrayOrRgb((RGB24Image)inputImage);
        }
      }
      else
      if (isRgbGray((RGB24Image)inputImage))
      {
        type = TYPE_GRAY8;
        if (doConvert)
        {
          outputImage = new MemoryGray8Image(inputImage.getWidth(), inputImage.getHeight());
          createGrayFromRgb((RGB24Image)inputImage, (Gray8Image)outputImage);
        }
      }
      else
      if (isRgb24Paletted8((RGB24Image)inputImage))
      {
        type = TYPE_PALETTED8;
        if (doConvert)
        {
          createPaletted8FromRgb24((RGB24Image)inputImage);
        }
      }
    }
    else
    // RGB48
    if (inputImage instanceof RGB48Image)
    {
      if (isRgbBilevel((RGB48Image)inputImage))
      {
        type = TYPE_BILEVEL;
        if (doConvert)
        {
          createBilevelFromGrayOrRgb((RGB48Image)inputImage);
        }
      }
      else
      if (isRgb48Gray8((RGB48Image)inputImage))
      {
        type = TYPE_GRAY8;
        if (doConvert)
        {
          outputImage = new MemoryGray8Image(inputImage.getWidth(), inputImage.getHeight());
          // this create method works because it works with int and the least significant 8
          // bits are equal to the most significant 8 bits if isRgb48Gray8 returned true
          createGrayFromRgb((RGB48Image)inputImage, (Gray8Image)outputImage);
        }
      }
      else
      if (isRgbGray((RGB48Image)inputImage))
      {
        type = TYPE_GRAY16;
        if (doConvert)
        {
          outputImage = new MemoryGray8Image(inputImage.getWidth(), inputImage.getHeight());
          createGrayFromRgb((RGB24Image)inputImage, (Gray8Image)outputImage);
        }
      }
      else
      if (isRgb48Rgb24((RGB48Image)inputImage))
      {
        // RGB48 input is RGB24; is it also Paletted8?
        if (isRgb48Paletted8((RGB48Image)inputImage))
        {
          type = TYPE_PALETTED8;
          if (doConvert)
          {
            createPaletted8FromRgb48((RGB48Image)inputImage);
          }
        }
        else
        {
          type = TYPE_RGB24;
          if (doConvert)
          {
            outputImage = new MemoryRGB24Image(inputImage.getWidth(), inputImage.getHeight());
            createRgb24FromRgb48((RGB48Image)inputImage, (RGB24Image)outputImage);
          }
        }
      }
    }
    else
    // PALETTED8
    if (inputImage instanceof Paletted8Image)
    {
      Paletted8Image in = (Paletted8Image)inputImage;
      Palette palette = in.getPalette();
      if (palette.isBlackAndWhite())
      {
        type = TYPE_BILEVEL;
        if (doConvert)
        {
          createBilevelFromPaletted(in);
        }
      }
      else
      if (palette.isGray())
      {
        type = TYPE_GRAY8;
        if (doConvert)
        {
          Gray8Image out = new MemoryGray8Image(in.getWidth(), in.getHeight());
          createGray8FromPaletted8(in, out);
        }
      }
    }
    else
    {
      throw new WrongParameterException("Not a supported or reducible image type: " + inputImage.toString());
    }
  }

  /**
   * This method can be used to specify whether the input image is to be converted
   * to the minimum image type if it is clear that such a conversion is possible.
   * The default value is <code>true</code>.
   * If this is set to <code>false</code>, it can still be
   * @param convert if true, the conversion will be performed
   */
  public void setConversion(boolean convert)
  {
    doConvert = convert;
  }

  /**
   * This method must be used to specify the mandatory input image.
   * @param image PixelImage object to be examined
   */
  public void setInputImage(PixelImage image)
  {
    inputImage = image;
  }
}
TOP

Related Classes of net.sourceforge.jiu.color.reduction.AutoDetectColorType

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.