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

Java中24位BMP转16色BMP异常问题求助

解决16色BMP转换后图像拉伸、右半部分缺失的问题

你的问题核心是16色BMP的扫描行对齐规则和像素打包格式没处理对:16色属于4位索引色,每个字节存2个像素,且BMP规范要求每行字节数必须是4的倍数,没遵守这两个规则就会出现图像拉伸、缺失的异常。下面是可直接运行的修正方案:

1. 生成合规的16色调色板

先创建16色的IndexColorModel,你可以用固定调色板,也可以从原图像提取高频颜色(示例用灰度渐变调色板):

byte[] reds = new byte[16];
byte[] greens = new byte[16];
byte[] blues = new byte[16];
for (int i = 0; i < 16; i++) {
    int val = i * 17; // 0,17,...,255 均匀覆盖灰度区间
    reds[i] = (byte) val;
    greens[i] = (byte) val;
    blues[i] = (byte) val;
}
IndexColorModel colorModel = new IndexColorModel(4, 16, reds, greens, blues);

2. 正确转换像素数据(核心)

重点处理2个像素打包成1字节的顺序,以及扫描行的4字节对齐填充:

BufferedImage original = ImageIO.read(new File("input.bmp"));
int width = original.getWidth();
int height = original.getHeight();

// 计算每行所需字节数(4位像素,2个像素占1字节)
int lineBytes = (width + 1) / 2;
// 计算需要填充的字节数,确保每行总字节数是4的倍数
int padding = (4 - (lineBytes % 4)) % 4;
int totalBytes = (lineBytes + padding) * height;

byte[] pixelBuffer = new byte[totalBytes];
int bufferIdx = 0;

for (int y = 0; y < height; y++) {
    int x = 0;
    // 逐行打包像素
    while (x < width) {
        // 取第一个像素的调色板索引
        int rgb1 = original.getRGB(x, y);
        int idx1 = getClosestPaletteIndex(rgb1, colorModel);
        
        // 取第二个像素(如果当前行还有剩余像素)
        int idx2 = 0;
        if (x + 1 < width) {
            int rgb2 = original.getRGB(x + 1, y);
            idx2 = getClosestPaletteIndex(rgb2, colorModel);
            x += 2;
        } else {
            x++;
        }
        
        // 高4位存左侧像素,低4位存右侧像素
        pixelBuffer[bufferIdx++] = (byte) ((idx1 << 4) | idx2);
    }
    // 填充对齐字节,满足BMP的扫描行规则
    for (int p = 0; p < padding; p++) {
        pixelBuffer[bufferIdx++] = 0;
    }
}

// 创建符合要求的索引色图像
BufferedImage indexedImg = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_INDEXED, colorModel);
indexedImg.getRaster().setDataElements(0, 0, width, height, pixelBuffer);

3. 写入BMP文件

直接用ImageIO写入即可,注意指定格式为"BMP":

ImageIO.write(indexedImg, "BMP", new File("output.bmp"));

辅助方法:匹配RGB到调色板索引

private static int getClosestPaletteIndex(int rgb, IndexColorModel cm) {
    int r = (rgb >> 16) & 0xFF;
    int g = (rgb >> 8) & 0xFF;
    int b = rgb & 0xFF;
    // 用colorModel自带方法找最接近的索引,也可以自己实现颜色距离算法
    return cm.getIndex(r, g, b);
}

要规避的坑

  • 像素打包顺序搞反:4位像素是左像素存高4位,右像素存低4位,反了会导致图像乱序。
  • 跳过扫描行对齐:哪怕你的图像宽度是32(刚好16字节,是4的倍数),也要保留填充逻辑,适配其他宽度的图像。
  • 用错BufferedImage类型:必须用TYPE_BYTE_INDEXED,不能用其他类型,否则ImageIO写入时会出错。

内容的提问来源于stack exchange,提问作者Park-se-jin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 22:50:28