1

I have been playing with image flood fill that I found here on Stack Overflow.

I think the code is not the problem. Though if you have a better one I would be glad to see (or even better if you know a library which has this type of image manipulations).

My problem is that after I run the algorithm on this image the guy's helmet instead of being green is light grey.

I have tried it on a silly example created in Paint and it works fine. Thus, I think there must be some image setting or something of that kind which changes the rgb value I set to it in the algorithm.

Do you have any suggestions to what should be set in the code (please see below)?

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class FloodFillTest extends JPanel
{
    private static final long serialVersionUID = 1L;
    private BufferedImage bI;
    public FloodFillTest()
    {
        try
        {
            this.bI = ImageIO.read(new File("images/brother.png"));
            Color targetColor = Color.WHITE;
            Color replacementColor = Color.GREEN;
            System.out.println("targetColor="+targetColor+"; replacementColor="+replacementColor);
            floodFill(125, 90, targetColor, replacementColor, bI);      
            setPreferredSize(new Dimension(bI.getWidth(), bI.getHeight()));
            System.out.println("bI.getWidth()="+bI.getWidth()+"; bI.getHeight()="+bI.getHeight());
        }catch(IOException ex)
        {
            Logger.getLogger(FloodFillTest.class.getName()).log(Level.SEVERE, null, ex);
        }
    }   

    /**
     * Fills a color in the image with a different color.
     * @param x x coordinate of starting point.
     * @param y y coordinate of starting point.
     * @param targetColor color we want to replace.
     * @param replacementColor the color which is used as the replacement.
     * @param image the image where we fill the color.
     */
    public static void floodFill(int x, int y, Color targetColor, Color replacementColor,
            BufferedImage image)
    {
        if(image.getRGB(x, y) != targetColor.getRGB()) 
            return;
        image.setRGB(x, y, replacementColor.getRGB());
        System.out.println("after set image.getRGB(x,y)="+ new Color(image.getRGB(x,y)).toString());
        floodFill(x - 1, y, targetColor, replacementColor, image);
        floodFill(x + 1, y, targetColor, replacementColor, image);
        floodFill(x, y - 1, targetColor, replacementColor, image);
        floodFill(x, y + 1, targetColor, replacementColor, image);
    }
    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);        
        Graphics2D g2 = (Graphics2D) g;
        g2.drawImage(bI, 0,0, null);
    }   

    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {               
                System.out.println("Color.WHITE="+Color.WHITE +"; Color.BLACK="+Color.BLACK);
                JPanel p = new FloodFillTest();
                p.setBackground(Color.CYAN);
                JPanel contentPane = new JPanel();
                contentPane.add(p);
                JFrame f = new JFrame();
                f.setContentPane(contentPane);
                f.setSize(800, 600);
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.setVisible(true);
            }
        });
    }
}

This is the image I am using in tests. The image I am using in tests.

Community
  • 1
  • 1
Boro
  • 7,913
  • 4
  • 43
  • 85
  • I'm sorry, but what is the problem? Maybe I'm overlooking something, but I don't see you state it. – Bart May 23 '11 at 16:03
  • @Bart sorry for this you were completely right. Now I state it clearly. The problem is that the helmet is filled with light grey color instead with green as given in replacementColor parameter. – Boro May 23 '11 at 16:25
  • 1
    *setRGB* and *getRGB*, in addition to being amazingly slow, also do a *lot* of things under the hood, like modifying the *r,g,b* values depending on color models etc. When I work with "pixels", I use image backed by an underlying *int[]* and read/write integers from/to this *int[]*. It's not only faster but you can also be sure that you're really both reading and putting the *r,g,b* value you want. – SyntaxT3rr0r May 23 '11 at 16:27
  • @SyntaxT3rr0r thanks for that comment might I ask how would you then get and set the int array, from and to an image? – Boro May 24 '11 at 13:39

1 Answers1

4

You got a grayscale image there. You cannot use green color on a grayscale image. That's why it turns up as light gray.

You need to either:

  • convert the image to RGB beforehand
  • make sure that you read/convert the image as RGB within Java

The last option is more safe as it will not fail on future images. Here is some code I found on the web that is said to do conversion to Grayscale. A small modification and you have what you need to ensure you are working on a color image:

public static BufferedImage convertToGrayscale(BufferedImage source) { 
     BufferedImageOp op = new ColorConvertOp(
       ColorSpace.getInstance(ColorSpace.CS_GRAY), null); 
     return op.filter(source, null);
}
ypnos
  • 50,202
  • 14
  • 95
  • 141
  • This is it. I have forgotten about the obvious. All the time I was working with a grayscale image. Thanks :) – Boro May 23 '11 at 17:02
  • For now I went with changing the image to color scale. But out of curiosity how would I use the code in my example. I was trying to use it on loaded image, or image after floodfill and I am getting the same null pointer exception `Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException at java.awt.image.ComponentColorModel.getDataElements(ComponentColorModel.java:1538) at java.awt.image.BufferedImage.setRGB(BufferedImage.java:971) at test.FloodFillTest.floodFill(FloodFillTest.java:59)......` – Boro May 23 '11 at 17:17