Pergunta

If I have the RBG code of a number, such as -16777216 (black), how can I find other similar shades of black using this color code?

I'm trying to convert an image to monochrome by marking all pixels which are not -16777216 to be white. However, often there are varying shades of black which are found, but they are lost because they are not an exact match.

Edit: I'm having a bit of trouble. When I try to use this color to find shades of black, so I can ignore them while converting the other pixels to white, this is my result:

Source:

source

Result:

result

Code:

package test;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URL;
import javax.imageio.ImageIO;

public class Test
{       
    public static void main(String[] args)
    {
        try
        {
            BufferedImage source = ImageIO.read( new URL("http://i.imgur.com/UgdqfUY.png"));
            //-16777216 = black:
            BufferedImage dest = makeMonoChromeFast(source, -16777216);
            File result = new File("D:/result.png");
            ImageIO.write(dest, "png", result);
        }
        catch (Exception e)
        {
            e.printStackTrace();;
        }
    }

    public static BufferedImage makeMonoChromeFast(BufferedImage source, int foreground)
    {        
        int background = -1; //white;

        Color fg = new Color(foreground);

        int color = 0;
        for (int y = 0; y < source.getHeight(); y++)
        {
            for (int x = 0; x < source.getWidth(); x++)
            {
                color = source.getRGB(x, y);
                if ( color == foreground )
                    continue;
                if (! isIncluded(fg, color, 50))
                    source.setRGB(x, y, background);;
            }
        }

        return source;
    }

    public static boolean isIncluded(Color target, int pixelColor, int tolerance)
    {
        Color pixel = new Color(pixelColor);
        int rT = target.getRed();
        int gT = target.getGreen();
        int bT = target.getBlue();
        int rP = pixel.getRed();
        int gP = pixel.getGreen();
        int bP = pixel.getBlue();
        return(
            (rP-tolerance<=rT) && (rT<=rP+tolerance) &&
            (gP-tolerance<=gT) && (gT<=gP+tolerance) &&
            (bP-tolerance<=bT) && (bT<=bP+tolerance) );
    }
}
Foi útil?

Solução

You might use this 'look for color with difference tolerance' method.

public static boolean isIncluded(Color target, Color pixel, int tolerance) {
    int rT = target.getRed();
    int gT = target.getGreen();
    int bT = target.getBlue();
    int rP = pixel.getRed();
    int gP = pixel.getGreen();
    int bP = pixel.getBlue();
    return(
        (rP-tolerance<=rT) && (rT<=rP+tolerance) &&
        (gP-tolerance<=gT) && (gT<=gP+tolerance) &&
        (bP-tolerance<=bT) && (bT<=bP+tolerance) );
}

Here it is used to get the outline (motorcycle-03.jpg) of the motorcycle (motorcycle.jpg), while stripping out the 'faint gray overlay'.

motorcycle.jpg

Original Image

motorcycle-03.png

Processed Image

ImageOutline.java

This code requires some patience (when running). See Smoothing a jagged path for code that does the same thing much faster.

import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.geom.Area;
import javax.imageio.ImageIO;
import java.io.File;
import java.util.Date;
import javax.swing.*;

/* Motorcycle image courtesy of ShutterStock
http://www.shutterstock.com/pic-13585165/stock-vector-travel-motorcycle-silhouette.html */
class ImageOutline {

    public static Area getOutline(BufferedImage image, Color color, boolean include, int tolerance) {
        Area area = new Area();
        for (int x=0; x<image.getWidth(); x++) {
            for (int y=0; y<image.getHeight(); y++) {
                Color pixel = new Color(image.getRGB(x,y));
                if (include) {
                    if (isIncluded(color, pixel, tolerance)) {
                        Rectangle r = new Rectangle(x,y,1,1);
                        area.add(new Area(r));
                    }
                } else {
                    if (!isIncluded(color, pixel, tolerance)) {
                        Rectangle r = new Rectangle(x,y,1,1);
                        area.add(new Area(r));
                    }
                }
            }
        }
        return area;
    }

    public static boolean isIncluded(Color target, Color pixel, int tolerance) {
        int rT = target.getRed();
        int gT = target.getGreen();
        int bT = target.getBlue();
        int rP = pixel.getRed();
        int gP = pixel.getGreen();
        int bP = pixel.getBlue();
        return(
            (rP-tolerance<=rT) && (rT<=rP+tolerance) &&
            (gP-tolerance<=gT) && (gT<=gP+tolerance) &&
            (bP-tolerance<=bT) && (bT<=bP+tolerance) );
    }

    public static BufferedImage drawOutline(int w, int h, Area area) {
        final BufferedImage result = new BufferedImage(
            w,
            h,
            BufferedImage.TYPE_INT_RGB);
        Graphics2D g = result.createGraphics();

        g.setColor(Color.white);
        g.fillRect(0,0,w,h);

        g.setClip(area);
        g.setColor(Color.red);
        g.fillRect(0,0,w,h);

        g.setClip(null);
        g.setStroke(new BasicStroke(1));
        g.setColor(Color.blue);
        g.draw(area);

        return result;
    }

    public static BufferedImage createAndWrite(
        BufferedImage image,
        Color color,
        boolean include,
        int tolerance,
        String name)
        throws Exception {
        int w = image.getWidth();
        int h = image.getHeight();

        System.out.println("Get Area: " + new Date() + " - " + name);
        Area area = getOutline(image, color, include, tolerance);
        System.out.println("Got Area: " + new Date() + " - " + name);

        final BufferedImage result = drawOutline(w,h,area);
        displayAndWriteImage(result, name);

        return result;
    }

    public static void displayAndWriteImage(BufferedImage image, String fileName) throws Exception {
        ImageIO.write(image, "png", new File(fileName));
        JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(image)));
    }

    public static void main(String[] args) throws Exception {
        final BufferedImage outline = ImageIO.read(new File("motorcycle.jpg"));
        BufferedImage crop = outline.getSubimage(17,35,420,270);
        displayAndWriteImage(crop, "motorcycle-01.png");

        BufferedImage crude = createAndWrite(crop, Color.white, false, 60, "motorcycle-02.png");

        BufferedImage combo = createAndWrite(crude, Color.red, true, 0, "motorcycle-03.png");
    }
}

With the code seen in the question, with a tolerance of 150, I see this.

enter image description here

Outras dicas

In general, I think that the way to go is use the sRGB to Grey-scale conversion formulae described on this Wikipedia page, and then choose a particular "grey" value as being the boundary between black and white. (The choice is up to you ...)

But say that you already have RGB values that represent grey-scale points, you should find that they all have equal red, green and blue values. If that is actually the case, then you simply need to pick one of the colour components of an RGB and compare it against the same colour value of your chosen "grey".

If you need to discriminate multiple shades of black, grey and white, then choose multiple boundary "colours".


Edit: I'm having a bit of trouble. When I try to use this color to find shades of black, so I can ignore them while converting the other pixels to white, this is my result:

What you are seeing there is the effects of anti-aliasing. There is actually very little "pure" black in the image. A lot of what looks black to the human eye is actually dark, or not so dark grey. You need to make your boundary colour (i.e. boundary between "black" and "not black") more grey.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top