关于BMP文件。如何编写/更改像素颜色? (在C中)

时间:2015-07-24 10:58:16

标签: c memory file bitwise-operators

我正在尝试更改图片中的像素(格式为bmp,24位)。

我有这三种结构:

表示文件标题:

#pragma pack(2)
typedef struct {
unsigned short int typeID;
unsigned int size;
unsigned short int reserved1, reserved2;
unsigned int offset;
}BITMAPFILEHEADER;
#pragma pack(0)

获取信息标题:

typedef struct {
unsigned int headerSize;
signed int widthPixel, heightPixel;
unsigned short int colorPlanes;
unsigned short int bitsPerPixel;
unsigned int compressionMethod;
unsigned int imagesize;
signed int xResolution, yResolution; // pixel per meter
unsigned int nbColor;
unsigned int importantColor;
}BITMAPINFOHEADER;

表示RGB颜色:

typedef struct {
unsigned char blue;
unsigned char green;
unsigned char red;
unsigned char reserved;
} RGBCOLOR;

然后是主要代码:

int main(void) {
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
FILE *inFileImage = NULL;

unsigned char *pBitsData = NULL;
int rowSize     = 0;
int nImageSize  = 0;


inFileImage = fopen("panda.bmp", "r+");
if (inFileImage == NULL)
{
    fprintf(stderr, "Unable to open image");
    return 0;
}

fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, inFileImage);
rewind(inFileImage);
pBitsData = (unsigned char *)calloc( fileHeader.size, sizeof(unsigned char) );

if( NULL == pBitsData )
{
    printf("NO memory!!!!!\n");
}
else
{
    fread(pBitsData, fileHeader.size * sizeof(unsigned char), 1, inFileImage);

    memcpy(&fileHeader,pBitsData,sizeof(BITMAPFILEHEADER));

    printf("Type ID: %x\n", fileHeader.typeID);
    printf("File size: %d\n", fileHeader.size);
    printf("Offset: %d\n", fileHeader.offset);
    printf("**********************\n");

    memcpy(&infoHeader, (pBitsData + sizeof(BITMAPFILEHEADER)), sizeof(BITMAPINFOHEADER));

    printf("Header size: %u\n", infoHeader.headerSize);
    printf("Width: %d\n", infoHeader.widthPixel);
    printf("Height: %d\n", infoHeader.heightPixel);
    printf("Nb planes: %d\n", infoHeader.colorPlanes);
    printf("Bits per pixel: %d\n", infoHeader.bitsPerPixel);
    printf("Compression method: %u\n", infoHeader.compressionMethod);
    printf("Image size: %u\n", infoHeader.imagesize);
    printf("Horizontal pixel per metre: %d\n", infoHeader.xResolution);
    printf("Vertical pixel per metre: %d\n", infoHeader.yResolution);
    printf("Number color in color table: %u\n", infoHeader.nbColor);
    printf("Color important count: %u\n", infoHeader.importantColor);
    printf("**********************\n");

    rowSize = ((infoHeader.bitsPerPixel * infoHeader.widthPixel + 31) / 32) * 4;
    printf("Row Size: %d\n", rowSize);
    nImageSize = rowSize * abs(infoHeader.heightPixel);
    printf("Pixel Array Size: %d\n", nImageSize);
    printf("**********************\n");

    RGBCOLOR* pixelData = (RGBCOLOR*)(pBitsData + fileHeader.offset);

    // M is define with the value 5 - the 5 pixel from image
    printf("Pixel %x, %x, %x\n", pixelData[M].blue, pixelData[M].green, pixelData[M].red);

    //fseek(inFileImage, fileHeader.offset, SEEK_CUR);
    //fread(pixelData, sizeof(RGBCOLOR), 1, inFileImage);

    pixelData[M].red    = 0x00;
    pixelData[M].blue   = 0xef;
    pixelData[M].green  = 0x00;

    //memcpy((pBitsData + fileHeader.offset), &pixelData, sizeof(RGBCOLOR));     // <= here seems to be my problem
    //fwrite(pixelData, sizeof(RGBCOLOR), 1, inFileImage);   // how can i copy to image from a memory

    printf("Pixel %x, %x, %x\n", pixelData[M].blue, pixelData[M].green, pixelData[M].red);  
}


fclose(inFileImage);
if(NULL != pBitsData)
{
   free(pBitsData);
}

return 0;

}

我想从图像中更改第5个像素(其中之一:蓝色 - >红色,绿色 - >蓝色等)。 你能告诉我我的代码在哪里有错误吗?我怎样才能改变我的代码正常工作? THX

编辑:

而不是

RGBCOLOR* pixelData = (RGBCOLOR*)(pBitsData + fileHeader.offset);

    // M is define with the value 5 - the 5 pixel from image
    printf("Pixel %x, %x, %x\n", pixelData[M].blue, pixelData[M].green, pixelData[M].red);

    //fseek(inFileImage, fileHeader.offset, SEEK_CUR);
    //fread(pixelData, sizeof(RGBCOLOR), 1, inFileImage);

    pixelData[M].red    = 0x00;
    pixelData[M].blue   = 0xef;
    pixelData[M].green  = 0x00;

    //memcpy((pBitsData + fileHeader.offset), &pixelData, sizeof(RGBCOLOR));     // <= here seems to be my problem
    //fwrite(pixelData, sizeof(RGBCOLOR), 1, inFileImage);   // how can i copy to image from a memory

    printf("Pixel %x, %x, %x\n", pixelData[M].blue, pixelData[M].green,       pixelData[M].red);    

如果我写

    RGBCOLOR img;
    fseek(inFileImage, fileHeader.offset, SEEK_SET);
    img.blue    = 0x00;
    img.green   = 0x00;
    img.red     = 0xff;
    fwrite(&img, sizeof(RGBCOLOR), 1, inFileImage);

这将改变第一个像素。但我想在内存中复制,并从那里更改像素。

1 个答案:

答案 0 :(得分:1)

我使用MinGW,因此您需要更改用于结构对齐的#pragma。 这是

的一体化资源
  • 阅读图片
  • 更改指定的像素
  • 将修改后的图像输出到新文件

为了简洁起见,我没有为任何错误检查而烦恼。我用24/32位图像测试了它。与32位图像一起使用时,它不会输出有效图像。

首先,这里是之前和之后的图像。图像本身是2x2像素,为了可见性,我在这里只是将它显示得更大。

<强>之前:

enter image description here enter image description here

<强>后:

enter image description here enter image description here

如果仔细观察,可以看到2个图像之间唯一不同的字节是文件中0x3E处的字节。我们将红色像素(00 00 FF)更改为紫色像素(FF 00 FF)。由于图像是自下而上的图像,因此像素数据的前3个字节用于黄色像素,接下来的3个用于蓝色像素,接着是2个字节的填充,然后我们有3个用于红色,3个用于绿色和另外2个填充字节。

以下是进行更改的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#ifndef WORD
 #define WORD unsigned short
#endif

#ifndef DWORD
 #define DWORD unsigned long
#endif

#ifndef BYTE
 #define BYTE unsigned char
#endif

#ifndef LONG
 #define LONG long
#endif


#define minGW  1

#ifdef minGW
    #pragma pack(push,2)
#endif
typedef struct tagBITMAPFILEHEADER {
    WORD    bfType;
    DWORD   bfSize;
    WORD    bfReserved1;
    WORD    bfReserved2;
    DWORD   bfOffBits;
} BITMAPFILEHEADER,*LPBITMAPFILEHEADER,*PBITMAPFILEHEADER;
#ifdef minGW
    #pragma pack(pop)
#endif

typedef struct tagBITMAPINFOHEADER{
    DWORD   biSize;
    LONG    biWidth;
    LONG    biHeight;
    WORD    biPlanes;
    WORD    biBitCount;
    DWORD   biCompression;
    DWORD   biSizeImage;
    LONG    biXPelsPerMeter;
    LONG    biYPelsPerMeter;
    DWORD   biClrUsed;
    DWORD   biClrImportant;
} BITMAPINFOHEADER,*LPBITMAPINFOHEADER,*PBITMAPINFOHEADER;

typedef struct tagRGBQUAD
{
    BYTE    rgbBlue;
    BYTE    rgbGreen;
    BYTE    rgbRed;
    BYTE    rgbReserved;
} RGBQUAD,*LPRGBQUAD;

typedef struct tagBITMAPINFO
{
    BITMAPINFOHEADER bmiHeader;
    RGBQUAD bmiColors[1];
} BITMAPINFO,*LPBITMAPINFO,*PBITMAPINFO;

typedef struct tagBITMAP
{
    BITMAPINFOHEADER    bmInfo;
    unsigned char       *pBits;
} BITMAP, *PBITMAP;

PBITMAP readBitmapFile(const char *filename)
{
    FILE *fp;
    PBITMAP result;
    BITMAPFILEHEADER fileHeader;
    BITMAPINFO bmpInfo;

    fp = fopen(filename, "rb");

    fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, fp);

    fread(&bmpInfo, sizeof(BITMAPINFO), 1, fp);

    fseek(fp, fileHeader.bfOffBits, SEEK_SET);
    unsigned char *pBits = (unsigned char *)calloc(bmpInfo.bmiHeader.biSizeImage, sizeof(unsigned char) );
    fread(pBits, sizeof(char), bmpInfo.bmiHeader.biSizeImage, fp);
    fclose(fp);

    result = (PBITMAP) calloc(1, sizeof(*result) );
    memcpy(&result->bmInfo, &bmpInfo, sizeof(bmpInfo) );
    result->pBits = pBits;
    return result;
}

void saveBitmapFile(const char *filename, PBITMAP img)
{
    FILE *fp;
    BITMAPFILEHEADER fileHeader;
    memset(&fileHeader, 0, sizeof(fileHeader));
    fileHeader.bfType = 0x4d42; //'BM'
    fileHeader.bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);
    fileHeader.bfSize = fileHeader.bfOffBits + (img->bmInfo.biSizeImage);

    fp = fopen(filename, "wb");
    fwrite(&fileHeader, sizeof(fileHeader), 1, fp);
    fwrite(&img->bmInfo, sizeof(img->bmInfo), 1, fp);
    fwrite(img->pBits, 1, img->bmInfo.biSizeImage, fp);
    fclose(fp);
}


void myPixel(int x, int y, char r, char g, char b, PBITMAP image)
{
    unsigned char *ptrToPixel;
    int rowSize = ((image->bmInfo.biBitCount * image->bmInfo.biWidth + 31) / 32) * 4;
    int curRow, bytesPerPixel;
    if (image->bmInfo.biHeight < 0)
    {
        curRow = y;
    }
    else if (image->bmInfo.biHeight > 0)
    {
        curRow = (image->bmInfo.biHeight-1) - y;
    }
    bytesPerPixel = image->bmInfo.biBitCount / 8;
    ptrToPixel = (curRow * rowSize) + (x*bytesPerPixel) + image->pBits;

    ptrToPixel[0] = b;
    ptrToPixel[1] = g;
    ptrToPixel[2] = r;
}

int main()
{
    PBITMAP inImage = readBitmapFile("colorTile.bmp");
    myPixel(0,0, 255,0,255, inImage);
    saveBitmapFile("colorTileOut.bmp", inImage);
}

编辑: 在阅读Can someone provide me a specification of 32 bit BMP image format?https://forums.adobe.com/message/3272950#3272950之后,很明显32位位图正在使用V3标头 - 包含4个长的标头,用于指定4个通道中每个通道的位掩码。我随后修改了saveBitmapFilemyPixel例程,并且可以确认代码现在看起来也能正常运行32位位图。

void saveBitmapFile(const char *filename, PBITMAP img)
{
    FILE *fp;
    BITMAPFILEHEADER fileHeader;
    unsigned long maskArray[] = {0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF};
    memset(&fileHeader, 0, sizeof(fileHeader));
    fileHeader.bfType = 0x4d42; //'BM'
    fileHeader.bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER);
    if (img->bmInfo.biBitCount == 32)
        fileHeader.bfOffBits += sizeof(maskArray);
    fileHeader.bfSize = fileHeader.bfOffBits + (img->bmInfo.biSizeImage);

    fp = fopen(filename, "wb");
    fwrite(&fileHeader, sizeof(fileHeader), 1, fp);
    fwrite(&img->bmInfo, sizeof(img->bmInfo), 1, fp);
    if (img->bmInfo.biBitCount == 32)
        fwrite(&maskArray, sizeof(long), 4, fp);
    fwrite(img->pBits, 1, img->bmInfo.biSizeImage, fp);
    fclose(fp);
}

void myPixel(int x, int y, char r, char g, char b, PBITMAP image)
{
    unsigned char *ptrToPixel;
    int rowSize = ((image->bmInfo.biBitCount * image->bmInfo.biWidth + 31) / 32) * 4;
    int curRow, bytesPerPixel;
    if (image->bmInfo.biHeight < 0)
    {
        curRow = y;
    }
    else if (image->bmInfo.biHeight > 0)
    {
        curRow = (image->bmInfo.biHeight-1) - y;
    }
    bytesPerPixel = image->bmInfo.biBitCount / 8;
    ptrToPixel = (curRow * rowSize) + (x*bytesPerPixel) + image->pBits;

    if (image->bmInfo.biBitCount == 24)
    {
        ptrToPixel[0] = b;
        ptrToPixel[1] = g;
        ptrToPixel[2] = r;
    }

    else if (image->bmInfo.biBitCount == 32)
    {
//        ptrToPixel[0] = a;
        ptrToPixel[1] = b;
        ptrToPixel[2] = g;
        ptrToPixel[3] = r;
    }
}