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
相关产品推荐
相关产品推荐

