【问题标题】:How to convert any image to JPG?如何将任何图像转换为JPG?
【发布时间】:2020-07-08 17:59:34
【问题描述】:

我从休息控制器收到一个MultipartFile Spring 对象。我正在尝试将任何 inage 文件转换为 JPG 图像但我只需要字节数组将其保存在 mongoDb

我发现这段代码可以做到这一点

public boolean convertImageToJPG(InputStream attachedFile) {
    try {
        BufferedImage inputImage = ImageIO.read(attachedFile);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        boolean result = ImageIO.write(inputImage, "jpg", byteArrayOutputStream);
        
        return result;
        
    } catch (IOException e) {
        
        System.err.println("Error " + e);
        
    }
    return false;
}

但是result 作为false 没有错误,所以ImageIO.write 不起作用

我也发现这样做也可以,但使用File 对象,我不想create 目录上的文件,我只需要字节数组

public static boolean convertFormat(String inputImagePath,
        String outputImagePath, String formatName) throws IOException {
        FileInputStream inputStream = new FileInputStream(inputImagePath);
        FileOutputStream outputStream = new FileOutputStream(outputImagePath);
         
        // reads input image from file
        BufferedImage inputImage = ImageIO.read(inputStream);
         
        // writes to the output image in specified format
        boolean result = ImageIO.write(inputImage, formatName, outputStream);
         
        // needs to close the streams
        outputStream.close();
        inputStream.close();
         
        return result;
    }

测试

public class TestImageConverter {
 
    public static void main(String[] args) {
        String inputImage = "D:/Photo/Pic1.jpg";
        String oututImage = "D:/Photo/Pic1.png";
        String formatName = "PNG";
        try {
            boolean result = ImageConverter.convertFormat(inputImage,
                    oututImage, formatName);
            if (result) {
                System.out.println("Image converted successfully.");
            } else {
                System.out.println("Could not convert image.");
            }
        } catch (IOException ex) {
            System.out.println("Error during converting image.");
            ex.printStackTrace();
        }
    }
}

我该如何解决我的问题?

【问题讨论】:

  • 尝试“jpeg”而不是“jpg”
  • @Tarik 没用
  • 你确定图片读取解码成功了吗?通过打印图像大小、几个像素值来确认。
  • @Tarik Spring MultipartFile 成功给了我字节数组、大小、输入流、上下文类型
  • 我曾经做过一个将 jpg 转换为 jpg(灰度)的脚本,我记得那很痛苦。我有一个来自 ImageIO.read(File f) 的 BufferedImage 然后我循环了 image.height 和 image.width 并用 image.getRGB(x,y) 提取了 rgb 做了一些计算然后 image.setRGB(x,y,rgbaInt )。 rgb 的顺序出乎意料 (b/g/r)。是不是您必须将 rgb 转换为 rgba 才能使用 png 格式?可能是 png 的格式实际上存储为 a/r/g/b。因为这样你也可以在没有 alpha 通道的情况下存储它。

标签: java spring-boot inputstream javax.imageio multipartfile


【解决方案1】:

更新的解决方案(不需要 Raster 和 ColorModel 的替代方案)

确实让我感到困扰的是,我的旧解决方案(见下文)仍然需要 Rasters 和 ColorModels。我的解决方案遇到了挑战,所以我花了更多时间寻找替代方案。所以我现在能想到的最好的事情是:

try {
    final FileInputStream fileInputStream = new FileInputStream("dice.png");
    final BufferedImage image = ImageIO.read(fileInputStream);
    fileInputStream.close(); // ImageIO.read does not close the input stream

    final BufferedImage convertedImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
    convertedImage.createGraphics().drawImage(image, 0, 0, Color.WHITE, null);

    final FileOutputStream fileOutputStream = new FileOutputStream("dice-test.jpg");
    final boolean canWrite = ImageIO.write(convertedImage, "jpg", fileOutputStream);
    fileOutputStream.close(); // ImageIO.write does not close the output stream

    if (!canWrite) {
        throw new IllegalStateException("Failed to write image.");
    }
} catch (IOException e) {
    e.printStackTrace();
}

我最终得到了 BufferedImage 的副本,就像以前一样。它或多或少做同样的事情,但实际上您可以更轻松地重用 ColorModel 和 Raster。

drawImage() 似乎可以处理我之前手动完成的大部分工作。而且由于它一直是标准的java库代码,它似乎确实是一种更好的方式。

请注意,您最终会得到BufferedImage.TYPE_INT_RGB 类型的图像。虽然它似乎适用于 jpgpnggif 类型,但我不确定其他文件格式或具有不同存储 ColorModel 的文件会发生什么 - 信息可能会丢失(例如 4 个颜色通道到 3)。对于上述类型,我们不需要 Alpha 通道,即使我们从 gifjpg 转换为 png(它将是 Color.WHITE)。



旧解决方案

我对我的第一个设计不满意,而且它也没有按应有的方式工作。

因此,我从头开始创建了一个。我最终得到了一个用于sRGB 文件的小转换器。您可以从png 转换为jpg,反之亦然(编辑:还添加了gif 支持)。如果您想处理其他类型,请随时进一步扩展。您可以或多或少以相同的方式添加它。它也可能适用于其他文件类型,但我还没有测试过它们。幸运的是,sRGB 似乎很常见。

太。我不知道您可以生产多少组合和变体(调色板、精度、质量、黑白等),也不知道它们有哪些共同属性。

也许这对你来说已经足够了。也许不吧。至少这对我来说是一个很好的练习。

这个解决方案绝不是完美的。结果看起来还不错。文件类型转换成功,文件大小也小于png

try {
    final String fileName = "dice.png";
    final BufferedImage inputImage = ImageIO.read(new FileInputStream(fileName));
    final boolean isSRGB = inputImage.getColorModel().getColorSpace().isCS_sRGB();
    final String outputFormat = "gif";
    if (!isSRGB) {
        throw new IllegalArgumentException("Please provide an image that supports sRGB.");
    }
    final WritableRaster raster = createRaster(inputImage);
    final ColorModel colorModel = createColorModel(inputImage);
    final BufferedImage outputImage = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
    final String outputFileName = fileName + "-converted." + outputFormat;
    final boolean writeResult = ImageIO.write(outputImage, outputFormat, new FileOutputStream(outputFileName));
    if (!writeResult) {
        throw new IllegalStateException("Could not convert file: " + fileName + " to format: " + outputFormat);
    }
    System.out.println(">> Created file: " + outputFileName);
} catch (Exception e) {
    e.printStackTrace();
}
@NotNull
public static ColorModel createColorModel(@NotNull BufferedImage bufferedImage) {
    Objects.requireNonNull(bufferedImage);
    final int type = bufferedImage.getType();
    boolean isAlphaPremultiplied = false;
    int transparency = Transparency.OPAQUE;
    if (type == BufferedImage.TYPE_3BYTE_BGR) {
        isAlphaPremultiplied = true;
    }
    return new ComponentColorModel(
            ColorModel.getRGBdefault().getColorSpace(),
            false, isAlphaPremultiplied, transparency,
            bufferedImage.getData().getDataBuffer().getDataType()
    );
}

@NotNull
public static WritableRaster createRaster(@NotNull BufferedImage bufferedImage) {
    Objects.requireNonNull(bufferedImage);
    final int type = bufferedImage.getType();
    final int width = bufferedImage.getWidth();
    final int height = bufferedImage.getHeight();
    final int pixelStride = 3;
    int[] offset = new int[]{0, 1, 2};
    DataBufferByte dataBufferByte;

    if (type == BufferedImage.TYPE_4BYTE_ABGR || type == BufferedImage.TYPE_BYTE_INDEXED) {
        int dataIndex = 0;
        final byte[] data = new byte[height * width * pixelStride];
        final int bitmask = 0xff;
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                final int rgb = bufferedImage.getRGB(x, y);
                final int blue = bitmask & rgb;
                final int green = bitmask & (rgb >> 8);
                final int red = bitmask & (rgb >> 16);
                if (rgb == 0) {
                    data[dataIndex++] = (byte) bitmask;
                    data[dataIndex++] = (byte) bitmask;
                    data[dataIndex++] = (byte) bitmask;
                } else {
                    data[dataIndex++] = (byte) red;
                    data[dataIndex++] = (byte) green;
                    data[dataIndex++] = (byte) blue;
                }
            }
        }
        dataBufferByte = new DataBufferByte(data, data.length);
    } else if (type == BufferedImage.TYPE_3BYTE_BGR) {
        dataBufferByte = (DataBufferByte) bufferedImage.getRaster().getDataBuffer();
        offset = new int[]{2, 1, 0};
    } else {
        throw new IllegalArgumentException("Cannot create raster for unsupported image type.");
    }

    return Raster.createInterleavedRaster(
            dataBufferByte, width, height,
            pixelStride * width, pixelStride,
            offset,
            null
    );
}

编辑:添加了对 gif 的支持。

【讨论】:

  • 说真的,这有什么意义?问题基本上是“如何用 Java 编写 JPG”,您当然不必必须处理颜色模型和光栅的所有细节才能做到这一点。使用 Java 将 PNG 转换为 JPG 只需 2 行简单的代码即可完成,如果它对提问者不起作用,那么显然有一个无法从问题中得出的原因。
  • 它运行良好,基本上我是在尝试创建一个新的 JPG 文件以便对其进行处理
  • @Marco13 我不明白你的意思。它不能开箱即用,否则我显然会提供一个开箱即用的答案。相反,我想看看你在这方面的尝试。
  • 顺便说一句,问题出在 ColorModel 上。对于某些文件,它只是不起作用,因为图像数据有时存储在 byte[] 中,有时存储在 int[] 中,并且字节/整数的顺序也很重要。例如,如果它是 b/g/r,则颜色会被错误地解释,因为标准转换希望 srgb 处于不同的顺序。您必须具体说明格式。
  • 应该开箱即用,ImageIO.write(ImageIO.read(in), "jpg", out) 已经在问题中完成了。如果它不起作用,我会问 为什么 它不起作用(并且会要求输入图像不起作用)。无论如何:您发布的代码不是一个适当的解决方案。给我看一个显示问题的minimal reproducible example包括输入图像!),我会告诉你一个更好的解决方案。
猜你喜欢
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
  • 2013-01-11
  • 2013-01-08
  • 1970-01-01
  • 2015-02-09
  • 2018-08-26
  • 1970-01-01
相关资源
最近更新 更多