使用C语言实现均值滤波器处理彩色BMP图像出现偏移问题求助
问题描述
我正在完成计算机课程的图像均值滤波作业,要求不得使用C/C++外部库,但目前遇到了问题。我处理的是一张宽322、高315的彩色BMP图像,转换后的图像出现了偏移现象,但处理其他图像时无异常,转换成功。不过使用Python处理该图像时可以正常转换。
输入图像:
输出偏移图像:
以下是我的C语言代码:
#include <stdio.h> #include <stdlib.h> #define getLineBytes(biWidth, biBitCount) (biWidth * biBitCount / 8 + 3) / 4 * 4; #pragma pack(1) typedef struct tagBITMAPFILEHEADER { unsigned char bfType[2]; unsigned long bfSize; unsigned short bfReserved1; unsigned short bfReserved2; unsigned long bfOffBits; } fileHeader; typedef struct tagBITMAPINFOHEADER { unsigned long biSize; long biWidth; long biHeight; unsigned short biPlanes; unsigned short biBitCount; unsigned long biCompression; unsigned long biSizeImage; long biXPixPerMeter; long biYPixPerMeter; unsigned long biClrUsed; unsigned long biClrImportant; } fileInfo; typedef struct tagRGBQUAD { unsigned char rgbBlue; unsigned char rgbGreen; unsigned char rgbRed; unsigned char rgbReserved; } rgbq; void readBMP(FILE *fpBMP, fileHeader *fh, fileInfo *fi, rgbq *fq) { fread(fh, sizeof(fileHeader), 1, fpBMP); fread(fi, sizeof(fileInfo), 1, fpBMP); long oldBiWidth = getLineBytes(fi->biWidth, fi->biBitCount); fi->biBitCount = 8; long newBiWidth = getLineBytes(fi->biWidth, fi->biBitCount); fi->biSizeImage = newBiWidth * fi->biHeight; fh->bfOffBits = sizeof(fileHeader) + sizeof(fileInfo) + 256 * sizeof(rgbq); fh->bfSize = fh->bfOffBits + fi->biSizeImage; for (int i = 0; i < 256; i++) { fq[i].rgbBlue = fq[i].rgbGreen = fq[i].rgbRed = i; } } void writeBMP(FILE *fpGray, fileHeader *fh, fileInfo *fi, rgbq *fq) { fwrite(fh, sizeof(fileHeader), 1, fpGray); fwrite(fi, sizeof(fileInfo), 1, fpGray); fwrite(fq, sizeof(rgbq), 256, fpGray); } void convertToGray(FILE *fpBMP, FILE *fpGray, fileInfo *fi) { unsigned char rgbImgData[3]; unsigned int grayImgData; unsigned char rgbAnthor; long oldBiWidth = getLineBytes(fi->biWidth, fi->biBitCount); long newBiWidth = getLineBytes(fi->biWidth, 8); for (int i = 0; i < fi->biHeight; i++) { for (int j = 0; j < newBiWidth; j++) { if (j < fi->biWidth) { for (int k = 0; k < 3; k++) { fread(&rgbImgData[k], 1, 1, fpBMP); } grayImgData = (int)((float)rgbImgData[0] * 0.299 + (float)rgbImgData[1] * 0.587 + (float)rgbImgData[2] * 0.114); } else { grayImgData = 0; } fwrite(&grayImgData,1, 1, fpGray); } for (int k = fi->biWidth * 3; k < oldBiWidth; k++) { fread(&rgbAnthor, 1, 1, fpBMP); } } } int main() { unsigned char bmpRGBPath[] = "test.bmp"; unsigned char bmpGrayPath[] = "gray.bmp"; FILE *fpBMP, *fpGray; fileHeader fh; fileInfo fi; rgbq fq[256]; fpBMP = fopen(bmpRGBPath, "rb"); fpGray = fopen(bmpGrayPath, "wb"); readBMP(fpBMP, &fh, &fi, fq); writeBMP(fpGray, &fh, &fi, fq); convertToGray(fpBMP, fpGray, &fi); fclose(fpGray); fclose(fpBMP); return 0; }
问题根源
偏移的核心原因是你在readBMP函数中修改了fi->biBitCount为8,但后续计算原图像行字节数oldBiWidth时,用的是修改后的fi->biBitCount,而非原始的24位(彩色BMP的位深度)。
具体来说:
- 在
readBMP里,你先读取了原始图像的信息头,此时fi->biBitCount是24(彩色BMP),但紧接着你把它改成了8,然后计算oldBiWidth时用的是修改后的值,导致计算出的原图像行字节数完全错误。 - 错误的
oldBiWidth会让你在convertToGray中跳过的补位字节数不对,每一行都会多读或少读数据,累积下来就出现了图像偏移。
另外还有几个细节问题:
getLineBytes宏末尾多了分号,会导致编译时语法错误(比如赋值时会多一个空语句)。- 灰度值计算时,
rgbImgData的顺序是BGR(因为BMP存储是蓝绿红顺序),你的公式用的是rgbImgData[0](蓝)*0.299,这会导致灰度值计算错误,应该对应正确的权重:蓝0.114、绿0.587、红0.299。
修正后的代码
#include <stdio.h> #include <stdlib.h> // 去掉宏末尾的分号,避免语法错误 #define getLineBytes(biWidth, biBitCount) ((biWidth * biBitCount / 8 + 3) / 4 * 4) #pragma pack(1) typedef struct tagBITMAPFILEHEADER { unsigned char bfType[2]; unsigned long bfSize; unsigned short bfReserved1; unsigned short bfReserved2; unsigned long bfOffBits; } fileHeader; typedef struct tagBITMAPINFOHEADER { unsigned long biSize; long biWidth; long biHeight; unsigned short biPlanes; unsigned short biBitCount; unsigned long biCompression; unsigned long biSizeImage; long biXPixPerMeter; long biYPixPerMeter; unsigned long biClrUsed; unsigned long biClrImportant; } fileInfo; typedef struct tagRGBQUAD { unsigned char rgbBlue; unsigned char rgbGreen; unsigned char rgbRed; unsigned char rgbReserved; } rgbq; void readBMP(FILE *fpBMP, fileHeader *fh, fileInfo *fi, rgbq *fq, long *oldLineBytes) { fread(fh, sizeof(fileHeader), 1, fpBMP); fread(fi, sizeof(fileInfo), 1, fpBMP); // 先保存原始位深度,计算原图像行字节数后再修改为8 int originalBitCount = fi->biBitCount; *oldLineBytes = getLineBytes(fi->biWidth, originalBitCount); fi->biBitCount = 8; long newBiWidth = getLineBytes(fi->biWidth, fi->biBitCount); fi->biSizeImage = newBiWidth * fi->biHeight; fh->bfOffBits = sizeof(fileHeader) + sizeof(fileInfo) + 256 * sizeof(rgbq); fh->bfSize = fh->bfOffBits + fi->biSizeImage; for (int i = 0; i < 256; i++) { fq[i].rgbBlue = fq[i].rgbGreen = fq[i].rgbRed = i; } } void writeBMP(FILE *fpGray, fileHeader *fh, fileInfo *fi, rgbq *fq) { fwrite(fh, sizeof(fileHeader), 1, fpGray); fwrite(fi, sizeof(fileInfo), 1, fpGray); fwrite(fq, sizeof(rgbq), 256, fpGray); } void convertToGray(FILE *fpBMP, FILE *fpGray, fileInfo *fi, long oldLineBytes) { unsigned char rgbImgData[3]; unsigned char grayImgData; // 改用unsigned char存储,避免多余字节 unsigned char padByte; long newLineBytes = getLineBytes(fi->biWidth, 8); for (int i = 0; i < fi->biHeight; i++) { for (int j = 0; j < fi->biWidth; j++) { // 读取BGR三个通道 fread(&rgbImgData[0], 1, 1, fpBMP); // 蓝 fread(&rgbImgData[1], 1, 1, fpBMP); // 绿 fread(&rgbImgData[2], 1, 1, fpBMP); // 红 // 正确的灰度计算公式:Y = 0.299R + 0.587G + 0.114B grayImgData = (unsigned char)(rgbImgData[2] * 0.299 + rgbImgData[1] * 0.587 + rgbImgData[0] * 0.114); fwrite(&grayImgData, 1, 1, fpGray); } // 处理灰度图像的补位字节 int grayPad = newLineBytes - fi->biWidth; for (int k = 0; k < grayPad; k++) { grayImgData = 0; fwrite(&grayImgData, 1, 1, fpGray); } // 跳过原图像的补位字节 int originalPad = oldLineBytes - fi->biWidth * 3; for (int k = 0; k < originalPad; k++) { fread(&padByte, 1, 1, fpBMP); } } } int main() { unsigned char bmpRGBPath[] = "test.bmp"; unsigned char bmpGrayPath[] = "gray.bmp"; FILE *fpBMP, *fpGray; fileHeader fh; fileInfo fi; rgbq fq[256]; long oldLineBytes; // 存储原图像的行字节数 fpBMP = fopen(bmpRGBPath, "rb"); fpGray = fopen(bmpGrayPath, "wb"); readBMP(fpBMP, &fh, &fi, fq, &oldLineBytes); writeBMP(fpGray, &fh, &fi, fq); convertToGray(fpBMP, fpGray, &fi, oldLineBytes); fclose(fpGray); fclose(fpBMP); return 0; }
关键修改点
- 修复行字节数计算错误:在
readBMP中先保存原始位深度,计算出原图像的行字节数后,再修改fi->biBitCount为8,确保后续跳过补位字节时使用正确的数值。 - 修正灰度值计算顺序:BMP存储颜色为BGR顺序,所以公式中对应红、绿、蓝的权重应该匹配正确的通道。
- 移除宏末尾的分号:避免编译时出现语法错误。
- 优化补位处理逻辑:将原图像和灰度图像的补位处理分开,逻辑更清晰,避免混淆。
- 调整灰度数据类型:用
unsigned char存储灰度值,避免unsigned int带来的多余字节写入问题。
内容的提问来源于stack exchange,提问作者Jamth
相关产品推荐
相关产品推荐

