Link to home
Start Free TrialLog in
Avatar of marre111397
marre111397

asked on

Reducing colors of an Image?

What is the simplest way to reduce an 32-bit Image object to 256 colors so it can be transformed into a gif with the Acme gifencoder?
Avatar of Jim Cakalic
Jim Cakalic
Flag of United States of America image

A tool I have used for some time on Unix is ImageMagick. Among many other capabilities, it will perform color reduction. Versions are available for most Unix systems and Linux as well as Windows (NT and 95), Macintosh, VMS, and OS/2. The package is free and can be downloaded (source and/or binary) from http://www.imagemagick.org/.

Best regards,
Jim Cakalic
Avatar of marre111397
marre111397

ASKER

But I don't want to use an external software to convert my images. The 32-bit image is generated with Java and should then be converted to a gif with 256-colors...
One possible solution based on image producers and consumers - there are others but this is perhaps the easiest to grasp at first.

This is some background on how it works...

Java's producer-consumer model makes it simple to create filters that provide many interesting image effects. Just to refresh your memory, an image producer provides the data for an image. An image consumer takes the image data and displays it. When you create an image from an URL, the data read from that URL serves as the image producer. When you create an image from an in-memory array, the MemoryImageSource is the image producer. To display an image, you connect an image producer to an image consumer and the image consumer displays the image. An image filter works like both a producer and a consumer. It acts like a consumer when it receives pixel data from the producer; then it acts like a producer when it sends the pixel data on to the consumer.

[In other words, a filter sits between a producer and a consumer and does something to the image when the producer passes it the data before the consumer gets the data to display it]

Have you tried using an RGBImageFilter? The following example is giving in the JDK docs:


FilteredImageSource is an implementation of the ImageProducer interface which takes an existing image and a filter object and uses them to produce image data for a new filtered version of the original image. Here is an example which filters an image by swapping the red and blue compents:

 
Image src = getImage("doc:///demo/images/duke/T1.gif");
ImageFilter colorfilter = new RedBlueSwapFilter();
Image img = createImage(new FilteredImageSource(src.getSource(), colorfilter));

where RedBlueSwapFilter is:

class RedBlueSwapFilter extends RGBImageFilter {
    public RedBlueSwapFilter() {
  // The filter's operation does not depend on the
  // pixel's location, so IndexColorModels can be
  // filtered directly.
      canFilterIndexColorModel = true;
    }

    public int filterRGB(int x, int y, int rgb) {
      return ((rgb & 0xff00ff00)
           | ((rgb & 0xff0000) >> 16)
           | ((rgb & 0xff) << 16));
    }
}

As you can see, the int RGB variable consists of 4 parts:

0x means use hex notation.
Then the next two digits are the Alpha (giving 256 possible intensities for the Alpha channel)
Then the next two are the Red value of the pixel (giving 256 possible intensities) and the next two are the Green and the final two are the Blue.

So you extract from the int rgb variable the parts of the colour you are interested in and process them separately. This is how the RedBlueSwapFilter works...


If you understand that then you are nearly there.

What you need is a filter that maps all the image colours down to only 256. This is done quite simply by making each colour map down from a complete possible range of 0 - FF in hex (which is 24 bit colour - 8 bits for each colour giving 16 million colours) down to only a possible 256 colours in total.

I'd give you the answer now, but to be honest I'm actually just going to lunch :) so I'll have a look at writing you a complete solution later.

I also get the feeling that you could do this by manipulating a colormodel object or perhaps there is a relevant BufferedImageOp available but I have not got time to look into this in any great detail at the moment.

This should you get you started anyway,

Let me know if you have any further questions...

NOTE: You could probably use an external program from within your Java app to process the file if it supports working from the command line. Just save your 32bit image, run the external program from with in java, wait for it to finish and then pick up the converted file but this is not particularly neat - it depends on how you are using the program.

HI JOD,

 I am seeing Your comments after a long time

:)
ASKER CERTIFIED SOLUTION
Avatar of Jod
Jod

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I haven't actually tried this yet, but it looks reasonable. But I guess I this "color reduction algorithm" won't be high-quality, but it guess it will work for my application...

What I really wanted to know was whether Java had some standard-class that could do the color-reduction for me. Maybe with error-difussion or some other color-reduction-algorithm...
I have could not see an off the shelf solution to do what you want in Java but you would think there was woudn't you.

I guess the problem is not simple and the theory of colour conversion quite complex so I'm not sure if there is an easier way though there are certainly other ways. The problem of quality in the reduced colour image is always an issue and a dithering algorithm may be far more accurate. The above will not dither the image at all - you would need to extend the code considerably to do this effectively.

For logos and business style graphics the above will be fine. For pictures and photographic material the quality will vary wildly.

Anyway, some Colour conversion theory is here, for example:

http://java.sun.com/j2se/1.3/docs/guide/2d/spec/j2d-color.fm2.html

Not as straightforward as you might hope...

The code above is fairly basic but should do the job and it is pretty easy to do as well - only about twenty lines of code in total.

There is another even simpler way using a LookUpOp


      BufferedImage destImg = new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);

      byte reverse[] = new byte[256];
         for (int j=0; j<200; j++){
                   reverse[j]=(byte)(256-j);
      }

         ByteLookupTable blut=new ByteLookupTable(0, reverse);
         LookupOp lop = new LookupOp(blut, null);
         lop.filter(sourceImg,destImg);  

This works by creatiung an array of 256 bytes each with the relevant colour value in for your new colour mapping. So the above example will copy sourceImg to destImg but reverse all the colours in the image by using a look up table of colours that is the wrong way round (goes from high to low instead of low to high).

Perhaps look up the source of imagemagick from the link given above if you are feeling really brave and see what algorithms they use....