【问题标题】:Java Text on Image图像上的 Java 文本
【发布时间】:2011-08-25 03:29:38
【问题描述】:

我在 bufferedimage 中加载图像,然后在上面写一些文本。添加文本后,它会使图像模糊和文本扭曲。我有文本抗锯齿。可以看作是附加的。

【问题讨论】:

    标签: java image image-processing graphics antialiasing


    【解决方案1】:
    import java.awt.image.BufferedImage;
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    import javax.swing.event.*;
    import javax.imageio.*;
    import java.io.*;
    import javax.imageio.stream.ImageOutputStream;
    import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
    import java.util.Locale;
    
    class ImageCompressionDemo {
    
        private BufferedImage originalImage;
        private BufferedImage textImage;
    
        private JPanel gui;
    
        private JCheckBox antialiasing;
        private JCheckBox rendering;
        private JCheckBox fractionalMetrics;
        private JCheckBox strokeControl;
        private JCheckBox colorRendering;
        private JCheckBox dithering;
    
        private JComboBox textAntialiasing;
        private JComboBox textLcdContrast;
    
        private JLabel jpegLabel;
        private JLabel pngLabel;
    
        private JTextArea output;
    
        private JSlider quality;
    
        private int pngSize;
        private int jpgSize;
    
        final static Object[] VALUES_TEXT_ANTIALIASING = {
            RenderingHints.VALUE_TEXT_ANTIALIAS_OFF,
            RenderingHints.VALUE_TEXT_ANTIALIAS_ON,
            RenderingHints.VALUE_TEXT_ANTIALIAS_GASP,
            RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HBGR,
            RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB,
            RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VBGR,
            RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB
        };
    
        final static Object[] VALUES_TEXT_LCD_CONTRAST = {
            new Integer(100),
            new Integer(150),
            new Integer(200),
            new Integer(250)
        };
    
        ImageCompressionDemo() {
            int width = 280;
            int height = 100;
    
            gui = new JPanel(new BorderLayout(3,4));
    
            quality = new JSlider(JSlider.VERTICAL, 0, 100, 75);
            quality.setSnapToTicks(true);
            quality.setPaintTicks(true);
            quality.setPaintLabels(true);
            quality.setMajorTickSpacing(10);
            quality.setMinorTickSpacing(5);
            quality.addChangeListener( new ChangeListener(){
                public void stateChanged(ChangeEvent ce) {
                    updateImages();
                }
            } );
            gui.add(quality, BorderLayout.WEST);
    
            originalImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
            textImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
    
            JPanel controls = new JPanel(new GridLayout(0,1,0,0));
            antialiasing = new JCheckBox("Anti-aliasing", false);
            rendering = new JCheckBox("Rendering - Quality", true);
            fractionalMetrics = new JCheckBox("Fractional Metrics", true);
            strokeControl = new JCheckBox("Stroke Control - Pure", false);
            colorRendering = new JCheckBox("Color Rendering - Quality", true);
            dithering = new JCheckBox("Dithering", false);
    
            controls.add(antialiasing);
    
            controls.add(fractionalMetrics);
    
            textLcdContrast = new JComboBox(VALUES_TEXT_LCD_CONTRAST);
            JPanel lcdContrastPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
            lcdContrastPanel.add(textLcdContrast);
            lcdContrastPanel.add(new JLabel("Text LCD Contrast"));
            controls.add(lcdContrastPanel);
    
            textAntialiasing = new JComboBox(VALUES_TEXT_ANTIALIASING);
            controls.add(textAntialiasing);
    
            controls.add(dithering);
            controls.add(rendering);
            controls.add(colorRendering);
            controls.add(strokeControl);
    
    
            ItemListener itemListener = new ItemListener(){    
                public void itemStateChanged(ItemEvent e) {
                    updateImages();
                }
            };
            antialiasing.addItemListener(itemListener);
            rendering.addItemListener(itemListener);
            fractionalMetrics.addItemListener(itemListener);
            strokeControl.addItemListener(itemListener);
            colorRendering.addItemListener(itemListener);
            dithering.addItemListener(itemListener);
    
            textAntialiasing.addItemListener(itemListener);
            textLcdContrast.addItemListener(itemListener);
    
            Graphics2D g2d = originalImage.createGraphics();
            GradientPaint gp = new GradientPaint(
                0f, 0f, Color.red,
                (float)width, (float)height, Color.orange);
            g2d.setPaint(gp);
            g2d.fillRect(0,0, width, height);
    
            g2d.setColor(Color.blue);
            for (int ii=0; ii<width; ii+=10) {
                g2d.drawLine(ii, 0, ii, height);
            }
            g2d.setColor(Color.green);
            for (int jj=0; jj<height; jj+=10) {
                g2d.drawLine(0, jj, width, jj);
            }
    
            gui.add(controls, BorderLayout.EAST);
    
            JPanel images = new JPanel(new GridLayout(0,1,2,2));
            images.add(new JLabel(new ImageIcon(textImage)));
    
            try {
                pngLabel = new JLabel(new ImageIcon(getPngCompressedImage(textImage)));
                images.add(pngLabel);
                jpegLabel = new JLabel(new ImageIcon(getJpegCompressedImage(textImage)));
                images.add(jpegLabel);
            } catch(IOException ioe) {
            }
    
            gui.add(images, BorderLayout.CENTER);
            output = new JTextArea(4,40);
            output.setEditable(false);
            gui.add(new JScrollPane(output), BorderLayout.SOUTH);
    
            updateImages();
    
            JOptionPane.showMessageDialog(null, gui);
        }
    
        private Image getPngCompressedImage(BufferedImage image) throws IOException {
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
            ImageIO.write( image, "png", outStream );
    
            pngSize = outStream.toByteArray().length;
    
            BufferedImage compressedImage =
                ImageIO.read(new ByteArrayInputStream(outStream.toByteArray()));
    
            return compressedImage;
        }
    
        private Image getJpegCompressedImage(BufferedImage image) throws IOException {
            float qualityFloat = (float)quality.getValue()/100f;
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
    
            ImageWriter imgWriter = ImageIO.getImageWritersByFormatName( "jpg" ).next();
            ImageOutputStream ioStream = ImageIO.createImageOutputStream( outStream );
            imgWriter.setOutput( ioStream );
    
            JPEGImageWriteParam jpegParams = new JPEGImageWriteParam( Locale.getDefault() );
            jpegParams.setCompressionMode( ImageWriteParam.MODE_EXPLICIT );
            jpegParams.setCompressionQuality( qualityFloat );
    
            imgWriter.write( null, new IIOImage( image, null, null ), jpegParams );
    
            ioStream.flush();
            ioStream.close();
            imgWriter.dispose();
    
            jpgSize = outStream.toByteArray().length;
    
            BufferedImage compressedImage = ImageIO.read(new ByteArrayInputStream(outStream.toByteArray()));
            return compressedImage;
        }
    
        private void updateText() {
            StringBuilder builder = new StringBuilder();
    
            builder.append("Fractional Metrics: \t");
            builder.append( fractionalMetrics.isSelected() );
            builder.append("\n");
            builder.append( textAntialiasing.getSelectedItem() );
            builder.append("\nPNG size: \t");
            builder.append(pngSize);
            builder.append(" bytes\n");
            builder.append("JPG size: \t");
            builder.append(jpgSize);
            builder.append(" bytes \tquality: ");
            builder.append(quality.getValue());
    
            output.setText(builder.toString());
        }
    
        private void updateImages() {
            int width = originalImage.getWidth();
            int height = originalImage.getHeight();
    
            Graphics2D g2dText = textImage.createGraphics();
    
            if (antialiasing.isSelected()) {
                g2dText.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
            } else {
                g2dText.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_OFF);
            }
    
            if (rendering.isSelected()) {
                g2dText.setRenderingHint(RenderingHints.KEY_RENDERING,
                    RenderingHints.VALUE_RENDER_QUALITY);
            } else {
                g2dText.setRenderingHint(RenderingHints.KEY_RENDERING,
                    RenderingHints.VALUE_RENDER_SPEED);
            }
    
            if (fractionalMetrics.isSelected()) {
                g2dText.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
                    RenderingHints.VALUE_FRACTIONALMETRICS_ON);
            } else {
                g2dText.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
                    RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
            }
    
            if (strokeControl.isSelected()) {
                g2dText.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
                    RenderingHints.VALUE_STROKE_NORMALIZE);
            } else {
                g2dText.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
                    RenderingHints.VALUE_STROKE_PURE);
            }
    
            if (dithering.isSelected()) {
                g2dText.setRenderingHint(RenderingHints.KEY_DITHERING,
                    RenderingHints.VALUE_DITHER_ENABLE);
            } else {
                g2dText.setRenderingHint(RenderingHints.KEY_DITHERING,
                    RenderingHints.VALUE_DITHER_DISABLE);
            }
    
            if (colorRendering.isSelected()) {
                g2dText.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
                    RenderingHints.VALUE_COLOR_RENDER_QUALITY);
            } else {
                g2dText.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
                    RenderingHints.VALUE_COLOR_RENDER_SPEED);
            }
    
            g2dText.setRenderingHint(RenderingHints.KEY_TEXT_LCD_CONTRAST,
                textLcdContrast.getSelectedItem());
    
            g2dText.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
                textAntialiasing.getSelectedItem());
    
            g2dText.drawImage(originalImage, 0,0, null);
            g2dText.setColor(Color.black);
            g2dText.drawString("The quick brown fox jumped over the lazy dog.", 10,50);
    
            try {
                jpegLabel.setIcon(new ImageIcon(getJpegCompressedImage(textImage)));
                pngLabel.setIcon(new ImageIcon(getPngCompressedImage(textImage)));
            } catch(IOException ioe) {
            }
    
            gui.repaint();
            updateText();
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater( new Runnable() {
                public void run() {
                    ImageCompressionDemo iwt = new ImageCompressionDemo();
                }
            } );
        }
    }
    

    屏幕截图

    典型输出

    Fractional Metrics:     true
    Nonantialiased text mode
    PNG size:   7390 bytes
    JPG size:   7036 bytes  quality: 35
    
    Fractional Metrics:     true
    Antialiased text mode
    PNG size:   8741 bytes
    JPG size:   8663 bytes  quality: 55
    
    Fractional Metrics:     false
    Antialiased text mode
    PNG size:   8720 bytes
    JPG size:   8717 bytes  quality: 55
    

    【讨论】:

    • +1 包含这么多细节!虽然我在删除抗锯齿后遇到了同样的文本模糊问题。
    • 您也应该使用ItemListener 复选框。
    【解决方案2】:

    您应该能够使用其他响应中显示的呈现提示来控制文本质量。我知道它有效,因为我做了很多,所以我认为在这种情况下,它一定是导致质量下降的其他原因。

    1. 如何检查图像质量?您是直接将生成的 BufferedImage 绘制到您的 Java 应用程序中的屏幕图形中,还是保存到磁盘,即以 JPEG 格式?如果将其保存到磁盘,请尝试将其保存为 PNG 24 而不是 JPEG。我假设您的桌面屏幕以真彩色(32 或 24 位色深)运行,对吧?
    2. 您确定图像实际上是作为 BufferedImage.TYPE_INT_RGB 创建的吗?如果您无法控制代码中 BufferedImage 的创建,请尝试使用 TYPE_INT_RGB 创建一个新的 BufferedImage 并将源绘制到这个中,然后将文本绘制到其中。
    3. 尝试将 RenderingHints.KEY_DITHERING 设置为 RenderingHints.VALUE_DITHER_DISABLE(尽管真彩色图像不需要这样做)。

    如果这仍然不能帮助您找到原因,请提供更多信息(VM 版本、操作系统)和源代码。 JDK 1.6 Update 10 的文本渲染已经变得相当好,但早期版本也能够在图像中生成干净的文本,只是因为抗锯齿不太复杂,所以看起来不太好。

    回应您的评论:

    文本中的高对比度锐边是general problem with JPEG compression,因为它不是无损压缩。如果您确实需要使用 JPEG 并且无法切换到 PNG,您可以调整保存图像的压缩质量,以便在图像质量和文件大小之间找到更好的折衷方案。保存 JPEG 时如何设置压缩质量,请参见以下代码。

    import javax.imageio.IIOImage;
    import javax.imageio.ImageIO;
    import javax.imageio.ImageWriteParam;
    import javax.imageio.ImageWriter;
    import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
    import javax.imageio.stream.ImageOutputStream;
    
    //----
    
    float quality = 0.85f;
    File outfile = new File( "MyImage.jpg" );
    BufferedImage image = ...;
    
    ImageWriter imgWriter = ImageIO.getImageWritersByFormatName( "jpg" ).next();
    ImageOutputStream ioStream = ImageIO.createImageOutputStream( outfile );
    imgWriter.setOutput( ioStream );
    
    JPEGImageWriteParam jpegParams = new JPEGImageWriteParam( Locale.getDefault() );
    jpegParams.setCompressionMode( ImageWriteParam.MODE_EXPLICIT );
    jpegParams.setCompressionQuality( quality );
    
    imgWriter.write( null, new IIOImage( image, null, null ), jpegParams );
    
    ioStream.flush();
    ioStream.close();
    imgWriter.dispose();
    

    【讨论】:

    • 好吧,我只是想看看图像类型是否有所不同,发现添加文本后 png 图像没有失真。
    • 所以看起来应该归咎于 JPEG 压缩。我在上面扩展了我的答案文本,因为评论太长了。
    • @x4u: “您是直接将生成的 BufferedImage 绘制到您的 Java 应用程序中的屏幕图形中,还是保存到磁盘,即以 JPEG 格式?” 很好的问题。 +1
    • @x4u:确实,这看起来像是普通的有损JPEG。 +1
    • 这取决于你想对图像做什么。 JPEG(尤其是照片)通常可以压缩到比 PNG 小得多的文件大小,即使您使用了相当高的 JPEG 压缩质量。另一方面,PNG 可为您提供最佳的图像质量。我建议尝试不同的压缩质量值,并亲自看看是否可以使用更好但仍不完美的JPEG,或者它是否必须是PNG。如果文件大小不重要,请使用 PNG。
    【解决方案3】:

    这只是java。只需关闭文本抗锯齿。 或者,如果您确实需要它,请将文本呈现在单独的图像上,如果文本是黑色的,则获取所有颜色低于 rgb(100,100,100) 的像素并将它们设置为 0。然后将该缓冲区绘制到您的主图像上。

    【讨论】:

      【解决方案4】:

      试试这个:

      public static void setGraphicsQuality(Graphics2D g2D) {
          g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                  RenderingHints.VALUE_ANTIALIAS_ON);
          g2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
                  RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
          g2D.setRenderingHint(RenderingHints.KEY_RENDERING,
                  RenderingHints.VALUE_RENDER_QUALITY);
      }
      

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-26
      • 1970-01-01
      • 2016-12-25
      • 2019-06-16
      • 2013-07-21
      • 2014-01-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多