【问题标题】:Pixel manipulation on Bitmap file in CC中位图文件的像素操作
【发布时间】:2014-07-06 16:07:06
【问题描述】:

我在 C 中创建了程序,女巫读取 .bmp 文件并对其应用模糊效果。算法很简单。读取文件,分离每个颜色通道并使用给定矩阵进行通道卷积。我的图像是每像素 24 位。这是我的程序的结果:http://imgur.com/lTNvPrX 而这是原始的http://imgur.com/IVGHCQk。我认为在单独的通道中访问数据时会出错,但我不知道该怎么做。我知道rowscols 包含像素数而不是字节数,但是当我将其乘以3 时,我得到channelBlur[y*cols+x] = toWrite 的分段错误。我正在用gcc -m32 ... 编译它
下面是用于读取文件和处理图像的结构和代码:

typedef struct __attribute__((__packed__))
{
    WORD fType;
    DWORD byteSize;
    WORD reserved1;
    WORD reserved2;
    DWORD offsetToImage;

}BITMAPHEADER;

typedef struct __attribute__((__packed__))
{
    DWORD structSize;
    LONG width;
    LONG height;
    WORD planes;
    WORD bitsPerPix;
    DWORD compression;
    DWORD imgSizeBytes;
    LONG pixPerMeterX;
    LONG pixPerMeterY;
    DWORD colorUsd;
    DWORD colorImp;
}BITMAPINFOHEADER;

typedef unsigned char* IMAGE;

加载函数:

IMAGE loadImg(const char* filename, BITMAPINFOHEADER* infoHeader)
{
    FILE* fImg;
    BITMAPHEADER header;
    IMAGE bitmap;

    fImg = fopen(filename,"rb");
    if(fImg == NULL)
        return NULL;

    fread(&header,sizeof(BITMAPHEADER),1,fImg);
    if(header.fType != 0x4D42)
    {
        fclose(fImg);
        return NULL;
    }

    fread(infoHeader,sizeof(BITMAPINFOHEADER),1,fImg);
    fseek(fImg,header.offsetToImage, SEEK_SET);

    bitmap = (unsigned char*)malloc(infoHeader->imgSizeBytes);
    fread(bitmap,infoHeader->imgSizeBytes,1,fImg);
    fclose(fImg);    
    return bitmap;
}

和幼稚的分离通道和组合功能:

void separateChannels(const IMAGE src,unsigned int nPixels, unsigned char* red, unsigned char* green,unsigned char* blue)
{
    PIXEL pix;
    unsigned int i;
    for(i=0;i<nPixels;i=i+3)
    {
        blue[i] = src[i];
        green[i] = src[i+1];
        red[i] = src[i+2];
    }  
}

IMAGE combineChannels(unsigned char* red, unsigned char* green,unsigned char* blue, unsigned int nPixels, BITMAPINFOHEADER infoHeader)
{    
    IMAGE image = createNewImage(infoHeader);
    unsigned int i;
    for(i=0;i<nPixels;i=i+3)
    {
        image[i] = blue[i];
        image[i+1] = green[i];
        image[i+2] = red[i];
    }
    return image;
}


createNewImage 工作正常
分配频道:

unsigned char* redBlur = (unsigned char*)malloc(infoHeader.imgSizeBytes);
unsigned char* greenBlur = (unsigned char*)malloc(infoHeader.imgSizeBytes);
unsigned char* blueBlur = (unsigned char*)malloc(infoHeader.imgSizeBytes);

这是有问题的函数:

void applyFilterOnChannel(const unsigned char* channel,unsigned char* channelBlur ,unsigned int cols,unsigned int rows, const float* filter)
{
    unsigned int x;
    unsigned int y;

    for(y=0;y<cols;++y)
    {
        for(x=0;x<rows;++x)
        {
            float value = 0;
            unsigned int filterX;
            unsigned int filterY;
            for(filterY=0;filterY<filterSize/2;++filterY)
            {
                for(filterX=0;filterX<filterSize/2;++filterX)
                {
                    int pixelPosY = min(max(y+filterY,0),(int)rows-1);
                    int pixelPosX = min(max(x+filterX,0),(int)cols-1);

                    float pixelVal = (float)channel[pixelPosY*cols+pixelPosX];
                    float filterVal = filter[(filterY+filterSize/2)*filterSize+filterX+filterSize/2];
                    value += pixelVal*filterVal;
                }
            }
            unsigned char toWrite = (unsigned char)value;
            channelBlur[y*cols+x] = toWrite;
        }
    }
}

【问题讨论】:

  • nPixels 是如何计算的? (如果这是宽度 * 高度,那么在处理过程中不应跳过 3。)另外,请仔细检查:读取文件后,立即将其写回。这会扭曲它吗?
  • 当您分离和合并频道时,您的索引是错误的。在你的循环中,你应该只将i 增加一并计算像image[3*i + 2] = red[i] 这样的索引等等。
  • @Jongware nPixels = BITMAPINFOHEADER.imgSizeBytes 我知道这个变量名是错误的......加载功能很好,我在代码中执行图像翻转并得到正确的输出。
  • 如果你不打电话给applyFilterOnChannel,生成的图像可以吗?它应该与原始图像相同。
  • @Michael Walz 是的,如果我只是调用 CombineChannels 并作为参数传递提取的颜色通道,我会得到原始图像。

标签: c image-processing memory-management bitmap


【解决方案1】:

有几个问题。有些可能适用于您的情况,有些可能不适用(取决于特定的图像和处理器架构):

首先,separateChannelcombineChannel 不能正常工作。您只能访问数组的每三个字节。它应该是(对于 combineChannel 也是如此):

void separateChannels(const IMAGE src, unsigned int nPixels, unsigned char* red, unsigned char* green, unsigned char* blue)
{
    unsigned int i, j;
    for(i = 0, j = 0; i < nPixels; i++, j = j + 3)
    {
        blue[i]  = src[j];
        green[i] = src[j + 1];
        red[i]   = src[j + 2];
    }  
}

为三个通道分配的数组可以相应地更小。

接下来,您似乎在处理一个 BGR 文件,即每个像素需要 3 个字节。 BMP 标准的一部分是每行的大小被四舍五入为 4 字节的倍数,以便每个像素行从双字边界开始。

接下来,我假设您在小端处理器架构上运行代码。否则,你的读取 BMP 头的代码是无效的。

最后,BMP 格式涵盖了广泛的图像类型,但您的代码仅处理每像素 24 位、从上到下的 BGR 文件。

【讨论】:

  • 我已经更改了 separteChannelscombineChannels 正如你所指出的,但这仍然没有解决问题。作为nPixels 变量,我将值形式传递给BITMAPINFOHEADER.imgSizeBytes。如何正确读取通道值,了解每一行的舍入/
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-18
  • 2010-12-08
  • 1970-01-01
  • 2018-04-06
  • 2013-05-04
相关资源
最近更新 更多