3

I'm attempting to convert a color image to a useable monochrome image, but without the "jagged" edges.

From a similar question asking to convert an image from color to black and white, one of the accepted answers provides a simple trick from JavaFX's ColorAdjust class using a setBrightness(-1) technique. This technique has the benefit of maintaining the soft edges between black and white, such as supporting a high-contrast theme without creating all-new icons set.

Note: I do understand the inaccuracy of the word "monochrome" here (some grayscaling will occur) but I'm not sure how else to describe this technique.

What's a way to mimic the ColorAdust technique using pure Java?

Desired:

soft edges


NOT Desired:

jagged edges

tresf
  • 7,103
  • 6
  • 40
  • 101
  • 1
    What's a pure Java? – Fureeish Jul 02 '19 at 16:19
  • 1
    @Fureeish I'm assuming he means the classes that you can use without importing them inside your own module. – Ferrybig Jul 02 '19 at 16:31
  • @Fureeish JavaFX used to be part of the JRE but was removed in JDK11 so it's no longer a viable solution moving forward without depending on it as an external framework (there are ways to do this, but it puts the entire JavaFX runtime as a dependency for a project, inflates the project size, makes the project platform-dependant -- since JavaFX isn't portable). In this context, "Pure Java" simply means "without using JavaFX, external libraries (jars) or any 3rd party command line (e.g. `convert/imagemagick`) techniques. – tresf Jul 02 '19 at 18:09

1 Answers1

1

This is a pure Java approach. The Swing code is not needed to create the image. Instead of changing the image to black and white, we are changing the image to black and transparent. That is how we preserve those feathered edges.

result: enter image description here

If you want a true grayscale image with no alpha, make a graphics2d object, fill it with the desired background color, then draw the image onto it.

As for preserving whites as white, this can be done, but one of two thing must be conceded. Either you give up the black and white aspect and adopt a true grayscale image, or you keep your black and white, but get a jagged edge where white feathers into any other color. This occurs because once we hit a light color pixel, how do we know whether it is a light colored feature, or a transition pixel between white and another color. I don't know of a way to fix that without edge detection.

public class Main {
    private static void createAndShowGUI() {
        //swing stuff
        JFrame.setDefaultLookAndFeelDecorated(true);
        JFrame frame = new JFrame("Alpha Mask");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.PAGE_AXIS));

        JLabel picLabel = new JLabel(new ImageIcon(getImg()));
        frame.getContentPane().add(picLabel);

        BufferedImage alphaMask = createAlphaMask(getImg());

        JLabel maskLabel = new JLabel(new ImageIcon(alphaMask));
        frame.getContentPane().add(maskLabel);

        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    public static BufferedImage getImg() {
        try {
            return ImageIO.read(new URL("https://i.stack.imgur.com/UPmqE.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static BufferedImage createAlphaMask(BufferedImage img) {
        //TODO: deep copy img here if you actually use this
        int width = img.getWidth();
        int[] data = new int[width];

        for (int y = 0; y < img.getHeight(); y++) {
            // pull down a line if argb data
            img.getRGB(0, y, width, 1, data, 0, 1);
            for (int x = 0; x < width; x++) {
                //set color data to black, but preserve alpha, this will prevent harsh edges
                int color = data[x] & 0xFF000000;
                data[x] = color;
            }
            img.setRGB(0, y, width, 1, data, 0, 1);
        }
        return img;
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(() -> createAndShowGUI());
    }
}
Kyle Berezin
  • 597
  • 4
  • 20