Java如何实现RGB图像转灰度图且保留指定单一颜色(红色)
保留红色区域其余转灰度的BMP图像处理方案
需求
实现类似《罪恶之城》的影视特效,将24位BMP图像中除红色区域外的所有内容转为灰度,仅保留红色区域的原色。
现有代码问题
你写的代码有三处核心错误,导致无法得到预期效果:
- 逻辑完全颠倒:判定像素偏红时修改了蓝绿通道为灰度值,非红区域反而保留了原色,和需求相反
- 红色判定规则过松:仅判断R通道为最大值会误把大量偏橙、偏粉的浅暖色都判定为红色保留,最终效果杂乱
- 未处理BMP行对齐规则:BMP要求每行像素数据的字节数必须对齐到4的倍数,不足的会补填充字节,直接按3字节一组读取会导致宽度不是4的倍数的图像输出错位、损坏
修正后可运行代码
package poza; import java.io.*; public class poza { public static void main(String[] args) { try { FileInputStream fis = new FileInputStream("poza.bmp"); BufferedInputStream dis = new BufferedInputStream(fis); FileOutputStream sif = new FileOutputStream("poza1.bmp"); BufferedOutputStream bos = new BufferedOutputStream(sif); // 读取并原样写入BMP文件头 byte[] header = new byte[54]; dis.read(header, 0, 54); bos.write(header); // 从文件头读取图像宽度,计算行填充字节数 int width = ((header[21] & 0xff) << 24) | ((header[20] & 0xff) << 16) | ((header[19] & 0xff) << 8) | (header[18] & 0xff); int rowByteCount = (width * 3 + 3) / 4 * 4; int paddingByteCount = rowByteCount - width * 3; while (dis.available() > 0) { // 遍历当前行所有像素 for (int i = 0; i < width; i++) { int b = dis.read() & 0xff; int g = dis.read() & 0xff; int r = dis.read() & 0xff; int gray = (int) (0.114 * b + 0.587 * g + 0.299 * r); // 红色判定阈值可自行调整,数值越大保留的红色范围越小 if (r > g + 30 && r > b + 30) { // 红色区域保留原色 bos.write(b); bos.write(g); bos.write(r); } else { // 非红色区域转灰度 bos.write(gray); bos.write(gray); bos.write(gray); } } // 原样写入行填充字节 for (int i = 0; i < paddingByteCount; i++) { bos.write(dis.read()); } } dis.close(); bos.close(); } catch (Exception e) { System.out.println(e); } } }
调整说明
代码里红色判定的阈值30可以根据你想要的效果灵活修改:阈值越小,会保留越多偏红的暖色区域;阈值越大,仅会保留非常鲜艳的纯红色区域。
内容的提问来源于stack exchange,提问作者Leaf Clan
相关产品推荐
相关产品推荐

