将BGR位图转换为灰度图后生成损坏图像的技术问题
BGR位图转灰度图后文件损坏问题解决
问题描述
我尝试用以下C代码将BGR格式的位图转换为灰度图,生成的文件大小与输入图像一致,但用Windows图片查看器或画图打开时,提示图像不支持或已损坏。
int main() { FILE* fIn; FILE* fOut; if (fopen_s(&fIn, "D:\\VBox\\Shared\\images\\sample01.bmp", "rb") != 0) { return 1; } if (fopen_s(&fOut, "D:\\VBox\\Shared\\images\\sample01_gs1.bmp", "wb") != 0) { return 1; } unsigned char info[54]; fread(info, sizeof(unsigned char), 54, fIn); // read the 54-byte header int32_t width, height; uint16_t bitperpixl; memcpy(&width, info + 18, sizeof(int32_t)); memcpy(&height, info + 22, sizeof(int32_t)); memcpy(&bitperpixl, info + 28, sizeof(uint16_t)); int padding = ((width * bitperpixl) / 8) % 4; // which Is 0 for the image which I am using. fwrite(info, sizeof(unsigned char), 54, fOut); unsigned char pixel[3]; //bgr for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { fread(pixel, 3, 1, fIn); unsigned char gray = pixel[2] * 0.3 + pixel[1] * 0.58 + pixel[0] * 0.11; memset(pixel, gray, sizeof(pixel)); fwrite(&pixel, 3, 1, fOut); } fread(pixel, padding, 1, fIn); fwrite(pixel, padding, 1, fOut); } fclose(fOut); fclose(fIn); }
问题原因及修复方案
1. 灰度值计算的浮点精度问题
原代码用浮点运算计算灰度值,容易因精度误差导致结果超出unsigned char的0-255范围,截断后生成异常像素值,破坏图像数据。
修复方法:改用整数运算替代浮点运算,彻底避免精度损失:
// 替换原灰度计算行 unsigned char gray = (pixel[2] * 30 + pixel[1] * 58 + pixel[0] * 11) / 100;
2. 缺失文件读写的错误检查
原代码未验证fread和fwrite的返回值,若出现读写不完整的情况,会直接导致图像数据损坏。
修复方法:添加返回值检查,确保每一步读写操作完成:
// 读取头文件时检查 if (fread(info, sizeof(unsigned char), 54, fIn) != 54) { fclose(fIn); return 1; } // 读取单个像素时检查 if (fread(pixel, 3, 1, fIn) != 1) { fclose(fIn); fclose(fOut); return 1; } // 写入单个像素时检查 if (fwrite(pixel, 3, 1, fOut) != 1) { fclose(fIn); fclose(fOut); return 1; } // 处理padding时同样检查 if (padding > 0) { if (fread(pixel, padding, 1, fIn) != 1) { fclose(fIn); fclose(fOut); return 1; } if (fwrite(pixel, padding, 1, fOut) != 1) { fclose(fIn); fclose(fOut); return 1; } }
3. 可选:生成标准8位灰度图(而非24位伪灰度)
原代码是将24位彩色图的三个通道都设为灰度值,本质还是24位图像。如果要生成标准的8位灰度BMP,需要修改头信息并添加调色板:
- 修改位深度(偏移28的位置)为8
- 重新计算图像数据大小:
(width + padding_new) * height,其中padding_new = (4 - width % 4) % 4 - 添加256项调色板(每个项为4字节的BGR0格式)
- 每个像素仅写入1字节灰度值
修改后的完整代码(24位伪灰度版本)
#include <stdio.h> #include <string.h> #include <stdint.h> int main() { FILE* fIn; FILE* fOut; if (fopen_s(&fIn, "D:\\VBox\\Shared\\images\\sample01.bmp", "rb") != 0) { return 1; } if (fopen_s(&fOut, "D:\\VBox\\Shared\\images\\sample01_gs1.bmp", "wb") != 0) { fclose(fIn); return 1; } unsigned char info[54]; // 读取头文件并检查完整性 if (fread(info, sizeof(unsigned char), 54, fIn) != 54) { fclose(fIn); fclose(fOut); return 1; } int32_t width, height; uint16_t bitperpixl; memcpy(&width, info + 18, sizeof(int32_t)); memcpy(&height, info + 22, sizeof(int32_t)); memcpy(&bitperpixl, info + 28, sizeof(uint16_t)); // 仅处理24位输入图像 if (bitperpixl != 24) { fclose(fIn); fclose(fOut); return 1; } int padding = ((width * bitperpixl) / 8) % 4; if (padding < 0) padding += 4; // 写入头文件并检查 if (fwrite(info, sizeof(unsigned char), 54, fOut) != 54) { fclose(fIn); fclose(fOut); return 1; } unsigned char pixel[3]; // BGR for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { if (fread(pixel, 3, 1, fIn) != 1) { fclose(fIn); fclose(fOut); return 1; } // 整数运算计算灰度值 unsigned char gray = (pixel[2] * 30 + pixel[1] * 58 + pixel[0] * 11) / 100; memset(pixel, gray, sizeof(pixel)); if (fwrite(pixel, 3, 1, fOut) != 1) { fclose(fIn); fclose(fOut); return 1; } } // 处理行尾padding if (padding > 0) { if (fread(pixel, padding, 1, fIn) != 1) { fclose(fIn); fclose(fOut); return 1; } if (fwrite(pixel, padding, 1, fOut) != 1) { fclose(fIn); fclose(fOut); return 1; } } } fclose(fOut); fclose(fIn); return 0; }
内容的提问来源于stack exchange,提问作者Akhil V Suku
相关产品推荐
相关产品推荐

