当前位置: 首页 > 知识库问答 >
问题:

提高从BuffereImage生成的灰度图像的质量

杭永安
2023-03-14

我正在尝试提高从BufferedImage生成的图像的质量。最终目标是输入JPEG(此处从计算机上的文件中检索),转换为灰度TIFF,然后输出为字节数组。我包含了将最终图像保存到PC的代码,以便更容易识别问题。

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.IndexColorModel;
import java.awt.image.MultiPixelPackedSampleModel;
import java.awt.image.SampleModel;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.media.jai.ImageLayout;
import javax.media.jai.JAI;
import javax.media.jai.KernelJAI;
import javax.media.jai.LookupTableJAI;
import javax.media.jai.PlanarImage;
import javax.media.jai.operator.ErrorDiffusionDescriptor;

public class ByteConversionService {

    private static ByteArrayOutputStream baos;
    private static ImageWriter writer;
    private static ImageOutputStream ios;
    private static ImageWriteParam writeParam;

    public static void main(String args[]) {
        try {
            convertBufferedImageToByteArray();
        } catch (Exception e) {

        }
    }

    private static byte[] convertBufferedImageToByteArray()
            throws Exception {
        byte[] convertedByteArray = null;
        resourceSetup();
        try {                   
            File file = new File("../proj/src/image.jpg");
            BufferedImage image = ImageIo.read(file);
            convertImageToTif(image);
            createImage(baos);
            convertedByteArray = baos.toByteArray();
        } finally {
            resourceCleanup();
        }   
        return convertedByteArray;
    }

    private static void resourceSetup() throws Exception {
        baos = new ByteArrayOutputStream();
        writer = ImageIO.getImageWritersByFormatName(
                "tif").next();
        ios = ImageIO.createImageOutputStream(baos);
        writer.setOutput(ios);
        writeParam = writer.getDefaultWriteParam();
        writeParamSetUp(writeParam);
    }

    private static void writeParamSetUp(ImageWriteParam writeParam) {
        writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        writeParam.setCompressionType("CCITT T.4");
    }

    private static void convertImageToTif(BufferedImage image) throws Exception {
        try {
            BufferedImage blackAndWhiteImage = imageToBlackAndWhite(image);
            writeToByteArrayStream(blackAndWhiteImage);
            IIOImage iioImage = new IIOImage(blackAndWhiteImage, null, null);
            writer.write(null, iioImage, writeParam);
        } finally {
            image.flush();
        }
    }

    private static BufferedImage imageToBlackAndWhite(BufferedImage image) {
        PlanarImage surrogateImage = PlanarImage.wrapRenderedImage(image);
        LookupTableJAI lut = new LookupTableJAI(new byte[][] {
                { (byte) 0x00, (byte) 0xff }, { (byte) 0x00, (byte) 0xff },
                { (byte) 0x00, (byte) 0xff } });
        ImageLayout layout = new ImageLayout();
        byte[] map = new byte[] { (byte) 0x00, (byte) 0xff };
        ColorModel cm = new IndexColorModel(1, 2, map, map, map);
        layout.setColorModel(cm);
        SampleModel sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
                surrogateImage.getWidth(), surrogateImage.getHeight(), 1);
        layout.setSampleModel(sm);
        RenderingHints hints = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, layout);
        PlanarImage op = ErrorDiffusionDescriptor.create(surrogateImage, lut,
                KernelJAI.ERROR_FILTER_FLOYD_STEINBERG, hints);
        BufferedImage blackAndWhiteImage = op.getAsBufferedImage();
        return blackAndWhiteImage;
    }

    private static void writeToByteArrayStream(BufferedImage image) throws Exception {
        ImageIO.write(image, "tif", baos);
    }

    private static void createImage(ByteArrayOutputStream baos) throws Exception {
        ByteArrayInputStream bis = new ByteArrayInputStream(baos.toByteArray());
        ImageReader reader = (ImageReader) ImageIO.getImageReadersByFormatName(
                "tif").next();
        Object source = bis;
        ImageInputStream iis = ImageIO.createImageInputStream(source);
        reader.setInput(iis, true);
        ImageReadParam param = reader.getDefaultReadParam();
        Image image = reader.read(0, param);
        BufferedImage buffered = new BufferedImage(image.getWidth(null),
                image.getHeight(null), BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = buffered.createGraphics();
        g2.drawImage(image, null, null);
        File file = new File("../proj/src/image2.tif");
        ImageIO.write(buffered, "tif", file);
    }

    private static void resourceCleanup() throws Exception {
        ios.flush();
        ios.close();
        baos.flush();
        baos.close();
        writer.dispose();
    }
}

目前的问题是,最终的图像质量较低——放大显示构成图片的像素之间有大量空白。我的理解是,这可能是由于使用了抖动算法(Floyd Steinberg),因此从技术上讲,图像不会以灰度再现。

我已经尝试了多种解决方案,我将在评论中发布,但没有成功。我的问题是,我当前的解决方案是否可以提高最终质量,或者我对灰度的转换是否存在缺陷,并且ImageToBlackAndWhite方法不适合我的需求。

共有1个答案

龙佐
2023-03-14

现在我们已经确定所需的结果确实是灰度图像,我们可以修复代码以生成灰度TIFF。

需要更改两件事,首先是颜色空间从RGB转换为灰色:

private static BufferedImage imageToBlackAndWhite(BufferedImage image) {
    ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
    ColorConvertOp op = new ColorConvertOp(cs, null);  
    return op.filter(image, null);
}

我更喜欢ColorConvertOp,因为它是最“正确”的,并且在大多数平台上使用本机代码。但是您列出的任何其他方法也应该可以工作。为了清楚起见,您可能还需要考虑将方法重命名为ImageToGrayScale

此外,您需要更改TIFF压缩设置,因为CCITT T.4压缩只能用于二进制黑白图像(它是为FAX传输创建的)。如果您可以使用有损压缩,我建议您使用Deflate或LZW压缩,或者JPEG。这些都适用于灰度数据:

private static void writeParamSetUp(ImageWriteParam writeParam) {
    writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    writeParam.setCompressionType("Deflate"); // or LZW or JPEG
} 

PS:您还应该放弃writeToByTearRealStream方法,因为您当前的代码会两次写入TIFF,一次使用ImageIO解压。写(…) ,然后使用编写器进行压缩。写(…)

PPS:createImage方法也可以简化很多,因为ByteArrayOutputStream已经包含完整的TIFF。

private static void createImage(ByteArrayOutputStream baos) throws Exception {
    File file = new File("../proj/src/image2.tif");
    Files.write(file.toPath(), baos.toByteArray(), StandardOpenOption.CREATE);
}

 类似资料:
  • 我正在尝试转换一个。使用BuffereImage将包含灰度值的csv文件转换为图像。csv最初被读入,其中所有值都是双倍的。 我正在尝试使用BufferedImage使用代码创建100x100px输出图像 但我只有一个100x100的黑色方块作为输出。 我尝试了以及outout的png格式,但没有成功,也找不到导致此错误的原因。

  • 实际上我想对每个像素做一些计算,为此我需要显式地访问每个像素。 如有任何帮助或建议,我们将不胜感激。

  • 所以我有一个字节数组,代表像素数据(8位灰度)。没有头球。没什么。只是数据。我想用这些数据创建一个缓冲图像。我做的 其中w只是以像素为单位的宽度,h是以像素为单位的高度,数据是字节数组,图像是BufferedImage 这是我的作画方法 然而,我得到了这个图像(真实图像是指纹,大部分是白色像素) 出什么事了?我试着按原样保存数据,然后在Photoshop中查看。数据很好。 [编辑]别管这个问题。我

  • 我尝试使用PDFBox将BuffereImage中的图像绘制到PDF中,但失败了,我得到了黑色图像,Acrobat Reader发出警告,其中有“内存不足”之类的错误(但PDF是显示的)。 我使用BuffereImage是因为我需要将JavaFX图像对象(来自对Functiones.crearImagenDesdeTexto()的调用,是一个将文本转换为图像的函数)绘制成PDF。其余图像在不使用B

  • 问题内容: 如何以高质量保存Python图形? 也就是说,当我继续放大保存在PDF文件中的对象时,为什么没有任何模糊? 另外,保存它的最佳方式是什么? ,?还是其他?我做不到,因为有一个隐藏的数字发生,导致编译混乱。 问题答案: 如果您正在使用Matplotlib并试图在LaTeX文档中获得良好数据,请另存为EPS。具体来说,请在运行命令以绘制图像后尝试以下操作: 我发现EPS文件效果最好,而参数

  • 高度图 高度图(也称为视差贴图)是一个类似于法线贴图的概念,但是技术更复杂 — 因此更性能开销更大。高度图通常与法线贴图结合使用,负责定义和渲染表面额外的大型凸起。 虽然法线贴图改变了纹理表面的光照,但是视差高度图更进一步,它会移动表面纹理的可见区域,从而实现表面遮挡效果。这意味着,明显的凸起将具有放大的正面(面向相机)和缩小的反面(背向相机),并且反面会被遮挡住。 虽然可以产生非常逼真的 3D