Solved

Reducing colors of an Image?

Posted on 2000-05-14
8
430 Views
Last Modified: 2008-02-01
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?
0
Comment
Question by:marre111397
8 Comments
 
LVL 19

Expert Comment

by:Jim Cakalic
Comment Utility
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
0
 

Author Comment

by:marre111397
Comment Utility
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...
0
 
LVL 5

Expert Comment

by:Jod
Comment Utility
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.

0
 
LVL 7

Expert Comment

by:Ravindra76
Comment Utility
HI JOD,

 I am seeing Your comments after a long time

:)
0
What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 5

Accepted Solution

by:
Jod earned 50 total points
Comment Utility
Hi. Yep, been very busy.

But I now have a database update running that will take a while so can spare some time...

The hardest question is, qhich colours to use and choose. What colours of RGB to map to.

Well, if you are using these gifs on the web, the answer is done for you. You must extract each part of the RGB values and map them to those shown on this page

http://www.lynda.com/hexv.html

because this is the safe palette that is used for most browsers.

There are only 216 colours in the pallete due to system variations, but you can be sure that if you map a gif to these colours then it will remain the same on most or all platforms.

So every red, green or blue value can be mapped to one of the following six values:

255, 204, 153, 102, 51 or 0.

Does not sound like many colours but then 6*6*6 = 216. Any more than this and you go over the 256 limit.

There is some more info here

http://www.lynda.com/hex.html

So, extract each colour component:

RED = rgb & 0xff0000
GREEN = rgb & 0x00ff00
BLUE = rgb & 0x0000ff

Map it to the nearest relevant value above.

ff = 255 so ff becomes ff and so does eveything else above about 230.

I say about 230 as you may need to fiddle with the range boundaries to get the best results.

Then continue for all the remaining 5 ranges so that all values between 229 and 170 become 204 and so on through all the colours.

Try this filter in the above code:

class ColourReduceFilter extends RGBImageFilter {
    public ColourReduceFilter() {
      // 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 ( ( mapColour(rgb & 0xff000000) ) | // retain alpha transperency
               ( mapColour(rgb & 0xff0000) ) |
               ( mapColour(rgb & 0x00ff00) ) |
               ( mapColour(rgb & 0x0000ff) ) );
    }

    public int mapColour (int c) {
      int mappedCol = 0;
     
      if (c > 230)
        mappedCol = 255;
      else if (c > 170)
        mappedCol = 204;
      else if (c > 127)
        mappedCol = 153;
      else if (c > 75)
        mappedCol = 102;
      else if (c > 20)
        mappedCol = 51;
      else
        mappedCol = 0;

      return mappedCol;
    }
}

I haven't compiled this so it may have some typos, but the general idea is sound.
0
 

Author Comment

by:marre111397
Comment Utility
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...
0
 
LVL 5

Expert Comment

by:Jod
Comment Utility
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).

0
 
LVL 5

Expert Comment

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

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
SPLUNK REST  API call to Splunk to create and index? 2 66
bunnyEars2 challenge 6 66
array6 challenfge 6 62
mapAB Challlenge 35 84
An old method to applying the Singleton pattern in your Java code is to check if a static instance, defined in the same class that needs to be instantiated once and only once, is null and then create a new instance; otherwise, the pre-existing insta…
Java functions are among the best things for programmers to work with as Java sites can be very easy to read and prepare. Java especially simplifies many processes in the coding industry as it helps integrate many forms of technology and different d…
Viewers will learn one way to get user input in Java. Introduce the Scanner object: Declare the variable that stores the user input: An example prompting the user for input: Methods you need to invoke in order to properly get  user input:
Viewers will learn about basic arrays, how to declare them, and how to use them. Introduction and definition: Declare an array and cover the syntax of declaring them: Initialize every index in the created array: Example/Features of a basic arr…

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now