简体   繁体   English

为什么我的图像写成 jpg 文件后颜色会发生变化?

[英]Why is the color of my image changed after writing it as a jpg file?

I'm currently making a method that converts a ppm file to a jpg, png, and bmp file.我目前正在制作一种将 ppm 文件转换为 jpg、png 和 bmp 文件的方法。 The way I did it is reading the content of a ppm file, creating a BufferedImage, and assigning each pixel from the ppm file to the corresponding pixel in the BufferedImage.我这样做的方法是读取 ppm 文件的内容,创建 BufferedImage,并将 ppm 文件中的每个像素分配给 BufferedImage 中的相应像素。 My bmp and png files look correct.我的 bmp 和 png 文件看起来正确。 However, the jpg file looks completely different.但是,jpg 文件看起来完全不同。

Below is my code:下面是我的代码:

import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import javax.imageio.ImageIO;

public class readPPMOutputOthers {

  public static void main(String[] args) throws InterruptedException {
    // read a ppm file

    Scanner sc;
    // if the file is not found, it will throw an exception
    try {
      sc = new Scanner(new FileInputStream("res/test2.ppm"));
    } catch (FileNotFoundException e) {
      throw new IllegalArgumentException("File not found!");
    }

    // the file now is a StringBuilder
    // read line by line to get information
    StringBuilder builder = new StringBuilder();
    while (sc.hasNextLine()) {
      String s = sc.nextLine();
      // ignore comment #
      if (s.charAt(0) != '#') {
        builder.append(s).append(System.lineSeparator());
      }
    }

   
    sc = new Scanner(builder.toString());
    String token;
    token = sc.next();

    // set the fields
    // initial load image
    int width = sc.nextInt();
    int height = sc.nextInt();
    int maxValue = sc.nextInt();

    List<Integer> pixels = new ArrayList<>();
    for (int i = 0; i < height; i++) {
      for (int j = 0; j < width; j++) {
        int r = sc.nextInt();
        int g = sc.nextInt();
        int b = sc.nextInt();

        int rgb = r;
        rgb = (rgb << 8) + g;
        rgb = (rgb << 8) + b;
        pixels.add(rgb);
      }
    }

    // make a BufferedImage from pixels
    BufferedImage outputImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    int[] outputImagePixelData = ((DataBufferInt) outputImg.getRaster().getDataBuffer()).getData();

    for (int i = 0; i < pixels.size(); i++) {
      outputImagePixelData[i] = pixels.get(i);
    }

    try {
        ImageIO.write(outputImg, "png",
            new File("res/test.png"));
      ImageIO.write(outputImg, "jpg",
          new File("res/test2.jpg"));
        ImageIO.write(outputImg, "bmp",
            new File("res/test.bmp"));
    } catch (IOException e) {
      System.out.println("Exception occurred :" + e.getMessage());
    }
    System.out.println("Images were written successfully.");
  }
}

images comparison图像比较

The weird thing is it works for a very large image but not for this small image.奇怪的是它适用于非常大的图像,但不适用于这个小图像。 I need to make it work for such small images because of testing.由于测试,我需要使它适用于如此小的图像。 I've been digging posts about this on google and still didn't find a way to solve this.我一直在谷歌上挖掘关于这个的帖子,但仍然没有找到解决这个问题的方法。 Any help would be appreciated!任何帮助,将不胜感激!

The reason for the strange colors is YUV420 chroma subsumpling used by JPEG encoding.奇怪颜色的原因是 JPEG 编码使用的YUV420色度子采样。

In YUV420 every 2x2 pixels have the same chroma information (the 2x2 pixels have the same color).在 YUV420 中,每 2x2 像素具有相同的色度信息(2x2 像素具有相同的颜色)。
The 2x2 pixels have the same color, but each pixel has different luminance (brighness). 2x2 像素具有相同的颜色,但每个像素具有不同的亮度(亮度)。


The YUV420 Chroma subsumpling is demonstrated in Wikipedia : Wikipedia中演示了 YUV420 Chroma subsumpling:
在此处输入图像描述

And in our case:在我们的例子中:
在此处输入图像描述 becomes变成在此处输入图像描述
The brown color is a mixture of the original red, cyan magenta and the yellow colors (the brown color is "shared" by the 4 pixels).棕色是原始红色、青色洋红色和黄色的混合(棕色由 4 个像素“共享”)。


  • Note:笔记:
    Chroma subsumpling is not considered as "compression", is the sense that it not performed as part of the JPEG compression stage.色度 subsumpling 不被视为“压缩”,是指它不是作为 JPEG 压缩阶段的一部分执行的。
    We can't control the chroma subsumpling by setting the compression quality parameter.我们无法通过设置压缩质量参数来控制色度二次采样。
    Chroma subsumpling is referred as part of the "color format conversion" pre-processing stage - converting from RGB to YUV420 color format. Chroma subsumpling 被称为“颜色格式转换”预处理阶段的一部分 - 从 RGB 转换为 YUV420 颜色格式。

The commonly used JPEG color format is YUV420, but JPEG standard does support YUV444 Chroma subsumpling.常用的 JPEG 颜色格式是 YUV420,但 JPEG 标准确实支持 YUV444 Chroma subsumpling。
GIMP manages to save JPEG images with YUV444 Chroma subsumpling. GIMP设法使用 YUV444 Chroma subsumpling 保存 JPEG 图像。

Example (2x2 image):示例(2x2 图像):
Too small:太小: 在此处输入图像描述 Enlarged:放大: 在此处输入图像描述

I couldn't find an example for saving YUV444 JPEG in JAVA...我找不到在 JAVA 中保存 YUV444 JPEG 的示例...

To some degree the effect you describe is to be expected.在某种程度上,您描述的效果是可以预期的。

From https://en.wikipedia.org/wiki/JPEG :来自https://en.wikipedia.org/wiki/JPEG

JPEG is a commonly used method of lossy compression for digital images, particularly for those images produced by digital photography. JPEG 是一种常用的数字图像有损压缩方法,特别是对于那些由数字摄影产生的图像。 The degree of compression can be adjusted, allowing a selectable tradeoff between storage size and image quality.可以调整压缩程度,允许在存储大小和图像质量之间进行可选择的权衡。 JPEG typically achieves 10:1 compression with little perceptible loss in image quality. JPEG 通常实现 10:1 压缩,图像质量几乎没有可察觉的损失。

Maybe when storing small files you can set the compression to be low and thus increase quality.也许在存储小文件时,您可以将压缩设置为低,从而提高质量。 See Setting jpg compression level with ImageIO in Java请参阅在 Java 中使用 ImageIO 设置 jpg 压缩级别

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM