【问题标题】:How to convert image to black and white using Java如何使用Java将图像转换为黑白
【发布时间】:2013-01-08 22:17:52
【问题描述】:

我在 imagemagick 中使用如下命令将图像转换为黑白:

convert myimg.png -monochrome  out3.png

我想知道它是否有可能在 Java 中实现相同的结果?不使用 Im4Java 或 JMagick?

【问题讨论】:

  • @AndrewThompson 的答案是处理灰度转换。
  • 既然你从来没有提到任何东西,我想我应该试着澄清一下这里需要什么。您在使用抖动算法,我不知道 ImageMagick 使用哪个具体实现,但经典的 Floyd-Steinberg 是一个很好的初步猜测。
  • 为什么这个问题是-1?
  • 可能是因为您没有表现出努力去理解convert -monochrome 产生的内容,并且得到了与您所要求的完全不同的答案。您可能还问:“如果值低于 127,我如何将值转换为 0,如果值高于或等于 127,我如何将值转换为 255?”,这就是您现在使用的并且是一个非常愚蠢的问题。

标签: java image image-processing imagemagick


【解决方案1】:

我想这取决于您所说的“单色”/“黑白”......

public class TestBlackAndWhite {

    public static void main(String[] args) {
        new TestBlackAndWhite();
    }

    public TestBlackAndWhite() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception ex) {
                }

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

            }
        });
    }

    public class TestPane extends JPanel {

        private BufferedImage master;
        private BufferedImage grayScale;
        private BufferedImage blackWhite;

        public TestPane() {
            try {
                master = ImageIO.read(new File("C:/Users/shane/Dropbox/pictures/439px-Join!_It's_your_duty!.jpg"));
                grayScale = ImageIO.read(new File("C:/Users/shane/Dropbox/pictures/439px-Join!_It's_your_duty!.jpg"));
                ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
                op.filter(grayScale, grayScale);

                blackWhite = new BufferedImage(master.getWidth(), master.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
                Graphics2D g2d = blackWhite.createGraphics();
                g2d.drawImage(master, 0, 0, this);
                g2d.dispose();

            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

        @Override
        public Dimension getPreferredSize() {
            Dimension size = super.getPreferredSize();
            if (master != null) {
                size = new Dimension(master.getWidth() * 3, master.getHeight());
            }
            return size;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (master != null) {

                int x = (getWidth() - (master.getWidth() * 3)) / 2;
                int y = (getHeight() - master.getHeight()) / 2;

                g.drawImage(master, x, y, this);
                x += master.getWidth();
                g.drawImage(grayScale, x, y, this);
                x += master.getWidth();
                g.drawImage(blackWhite, x, y, this);

            }
        }


    }
}

【讨论】:

  • 在你回答的三张图片中,我在最右边的一张
  • 只是好奇,你能设置一个阈值(亮度)级别以转换为 1 位单色吗?
  • @birdy 那你在blackWhite BufferedImage
  • @Loadmaster 据我所知,没有使用上述方法。你可以写一个 BufferedImageOP 来完成它
  • 转换前的RescaleOp 可以解决问题。看我的回答。
【解决方案2】:

试试这个粗略的例子。我们首先使用RescaleOp 使图像变亮或变暗。

import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

class ColorToBlackAndWhite {

    /**
     * Returns the supplied src image brightened by a float value from 0 to 10.
     * Float values below 1.0f actually darken the source image.
     */
    public static BufferedImage brighten(BufferedImage src, float level) {
        BufferedImage dst = new BufferedImage(
                src.getWidth(), src.getHeight(), BufferedImage.TYPE_INT_RGB);
        float[] scales = {level, level, level};
        float[] offsets = new float[4];
        RescaleOp rop = new RescaleOp(scales, offsets, null);

        Graphics2D g = dst.createGraphics();
        g.drawImage(src, rop, 0, 0);
        g.dispose();

        return dst;
    }

    public static void main(String[] args) throws Exception {
        URL colorURL = new URL("http://i.stack.imgur.com/AuY9o.png");
        final BufferedImage colorImage = ImageIO.read(colorURL);

        float[] scales = {2f, 2f, 2f};
        float[] offsets = new float[4];
        RescaleOp rop = new RescaleOp(scales, offsets, null);

        final BufferedImage scaledImage = new BufferedImage(
                colorImage.getWidth(),
                colorImage.getHeight(),
                BufferedImage.TYPE_INT_RGB);
        Graphics2D g = scaledImage.createGraphics();
        g.drawImage(colorImage, rop, 0, 0);

        final BufferedImage grayImage = new BufferedImage(
                colorImage.getWidth(),
                colorImage.getHeight(),
                BufferedImage.TYPE_BYTE_GRAY);
        g = grayImage.createGraphics();
        g.drawImage(colorImage, 0, 0, null);

        final BufferedImage blackAndWhiteImage = new BufferedImage(
                colorImage.getWidth(),
                colorImage.getHeight(),
                BufferedImage.TYPE_BYTE_BINARY);
        g = blackAndWhiteImage.createGraphics();
        g.drawImage(colorImage, 0, 0, null);

        g.dispose();

        Runnable r = new Runnable() {

            @Override
            public void run() {
                JPanel gui = new JPanel(new BorderLayout(2, 2));
                JPanel images = new JPanel(new GridLayout(0, 2, 2, 2));
                gui.add(images, BorderLayout.CENTER);

                final JLabel scaled = new JLabel(new ImageIcon(scaledImage));
                final JSlider brighten = new JSlider(0, 1000, 100);
                gui.add(brighten, BorderLayout.PAGE_START);
                ChangeListener cl = new ChangeListener() {

                    @Override
                    public void stateChanged(ChangeEvent e) {
                        int val = brighten.getValue();
                        float valFloat = val / 1000f;
                        BufferedImage bi = brighten(colorImage, valFloat);
                        BufferedImage bw = new BufferedImage(
                                colorImage.getWidth(),
                                colorImage.getHeight(),
                                BufferedImage.TYPE_BYTE_BINARY);
                        Graphics g = bw.createGraphics();
                        g.drawImage(bi, 0, 0, null);
                        g.dispose();

                        scaled.setIcon(new ImageIcon(bw));
                    }
                };
                brighten.addChangeListener(cl);

                images.add(new JLabel(new ImageIcon(colorImage)));
                images.add(scaled);
                images.add(new JLabel(new ImageIcon(grayImage)));
                images.add(new JLabel(new ImageIcon(blackAndWhiteImage)));

                JOptionPane.showMessageDialog(null, gui);
            }
        };
        // Swing GUIs should be created and updated on the EDT
        // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
        SwingUtilities.invokeLater(r);
    }
}

【讨论】:

  • 太好了,谢谢。对于扫描的黑白文本页面,我发现 0.55f 的级别可以很好地在原始图像的缩略图中保留很多细节。
【解决方案3】:

您实现的效果不是通过预定义阈值的二值化,而是通过一种称为抖动的技术完成的。许多抖动方法通过传播误差(当前图像中的强度 - 给定点的二进制输出)来工作,从而调整下一个输出。这样做是为了创建一种视觉效果,如果您不仔细看的话,结果图像可能看起来不是黑白的。

Floyd-Steinberg 提出了这样一个简单而著名的方法,它的伪代码是:

for y := 1 to image height
    for x := 1 to image width
        v := im(y, x)
        if v < 128 then
            result(y, x) := 0
        else
            result(y, x) := 255
        error := v - result(y, x)
        propagate_error(im, y, x, error)

此方法的propagate_error 可以指定为(不考虑边界情况):

    im(y,   x+1) := im(y,   x+1) + (7/16) * error
    im(y+1, x+1) := im(y+1, x+1) + (1/16) * error
    im(y+1, x  ) := im(y+1, x  ) + (5/16) * error
    im(y+1, x-1) := im(y+1, x-1) + (3/16) * error

考虑到给出的伪代码的直接实现,右下图是左图的二进制版本。右边的图像实际上只有黑白两种颜色,这对于知道这种方法的人来说是一件小事,但对于那些不知道这种方法的人来说,这似乎是不可能的。创建的图案给人的印象是有几种灰色调,具体取决于您从远处看图像。

【讨论】:

    【解决方案4】:

    -试试下面的简单代码,

     package com.bethecoder.tutorials.imageio;
    
    import java.awt.Graphics2D;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    
    import javax.imageio.ImageIO;
    
    public class BlackAndWhiteTest {
    
      /**
       * @param args
       * @throws IOException 
       */
      public static void main(String[] args) throws IOException {
    
        File file = new File("C:/Temp/stpatricks_08.gif");
        BufferedImage orginalImage = ImageIO.read(file);
    
        BufferedImage blackAndWhiteImg = new BufferedImage(
            orginalImage.getWidth(), orginalImage.getHeight(),
            BufferedImage.TYPE_BYTE_BINARY);
    
        Graphics2D graphics = blackAndWhiteImg.createGraphics();
        graphics.drawImage(orginalImage, 0, 0, null);
    
        ImageIO.write(blackAndWhiteImg, "png", new File("c:/Temp/stpatricks_08_bw.png")); 
      }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-29
      • 1970-01-01
      • 1970-01-01
      • 2021-05-02
      • 2010-10-08
      • 1970-01-01
      • 2016-11-25
      • 2012-06-15
      相关资源
      最近更新 更多