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

使用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的位深度)。

具体来说:

  1. 在readBMP里,你先读取了原始图像的信息头,此时fi->biBitCount是24(彩色BMP),但紧接着你把它改成了8,然后计算oldBiWidth时用的是修改后的值,导致计算出的原图像行字节数完全错误。
  2. 错误的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;
}
关键修改点
  1. 修复行字节数计算错误:在readBMP中先保存原始位深度,计算出原图像的行字节数后,再修改fi->biBitCount为8,确保后续跳过补位字节时使用正确的数值。
  2. 修正灰度值计算顺序:BMP存储颜色为BGR顺序,所以公式中对应红、绿、蓝的权重应该匹配正确的通道。
  3. 移除宏末尾的分号:避免编译时出现语法错误。
  4. 优化补位处理逻辑:将原图像和灰度图像的补位处理分开,逻辑更清晰,避免混淆。
  5. 调整灰度数据类型:用unsigned char存储灰度值,避免unsigned int带来的多余字节写入问题。

内容的提问来源于stack exchange,提问作者Jamth

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 05:27:23