首页 文章

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

提问于
浏览
0

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

我有这3个结构:

对于文件头:

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

信息 Headers :

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个像素(其中之一:蓝色 - >红色,绿色 - >蓝色等) . 你能告诉我我的代码在哪里有错误吗?我怎样才能改变我的代码正常工作?谢谢

编辑:

代替

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 回答

  • 1

    我使用MinGW,因此您需要更改用于结构对齐的 #pragma . 这是一个多功能的源头

    • 读取图像

    • 更改指定的像素

    • 将修改后的图像输出到新文件

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

    首先,这是前后图像 . 图像本身是2x2像素,为了可见性,我刚刚在这里展示了它 .

    Before:

    enter image description here

    enter image description here

    After:

    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;
        }
    }
    

相关问题