You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java实现PPM转JPG格式后图片颜色异常问题求解

问题描述

开发PPM转JPG/PNG/BMP格式功能时,采用「读取PPM内容→创建BufferedImage实例→逐像素赋值」的逻辑实现,目前输出的BMP、PNG文件显示正常,JPG文件显示完全异常。
复现规律:大尺寸图片转换正常,小尺寸测试图转换时JPG必然出现色彩异常,因测试要求必须兼容小尺寸图片,查阅大量资料未找到解决方案。
图片效果对比

复现代码

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 {
    Scanner sc;
    try {
      sc = new Scanner(new FileInputStream("res/test2.ppm"));
    } catch (FileNotFoundException e) {
      throw new IllegalArgumentException("File not found!");
    }

    StringBuilder builder = new StringBuilder();
    while (sc.hasNextLine()) {
      String s = sc.nextLine();
      if (s.charAt(0) != '#') {
        builder.append(s).append(System.lineSeparator());
      }
    }

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

    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);
      }
    }

    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.");
  }
}
问题根因

像素组装逻辑不符合BufferedImage.TYPE_INT_RGB的存储规范:
TYPE_INT_RGB要求每个int像素的结构为最高8位固定为0xFF(即不透明的Alpha值),后续依次存储8位R、8位G、8位B值。
你当前的代码只移位拼接了R、G、B三个通道的值,最高8位默认是0,等价于Alpha通道值为0(全透明)。PNG、BMP编码器写入TYPE_INT_RGB类型图像时会自动丢弃高位的Alpha值,所以输出正常;但JDK自带的JPEG编码器在处理小尺寸图像时不会自动修正高位的异常Alpha值,会把0值带入YUV色彩空间转换计算,最终导致输出JPG色彩错乱。大尺寸图片转换正常只是编码器内存对齐带来的偶发情况,不是逻辑本身正确。

修复方法

修改像素组装代码,在最高位补上固定的不透明Alpha值即可:

// 替换原有rgb计算逻辑
// 原错误实现
// int rgb = r;
// rgb = (rgb << 8) + g;
// rgb = (rgb << 8) + b;
// 修复后实现
int rgb = 0xFF000000 | (r << 16) | (g << 8) | b;

另外代码里还有两个潜在问题,建议一并修复避免后续踩坑:

  • 跳过PPM注释的逻辑存在缺陷:如果注释不是在行首,而是跟在某行有效内容后面,会被当成有效像素数据读入,导致像素解析错位。
  • 没有适配PPM的maxValue非255的场景:如果PPM文件的最大色深不是255(比如10bit色深的1023),直接读入的RGB值会超出0-255的合法范围,导致色彩异常,需要按比例将RGB值映射到0-255区间。

内容的提问来源于stack exchange,提问作者d0nut

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 16:31:06