位图颜色 - C

Bitmap color - C

我仍然无法访问位图图像的颜色位。问题是,将位图的内容保存到缓冲区后,我不知道:

  1. 从哪里开始循环(如果我从 0 开始我认为它会删除 headers)?

  2. 如何访问字节并进行更改(将 BMP 中的颜色转换为输出中所需的颜色)?

  3. 以及,如何将缓冲区插入到新的位图文件中?

我要修改的所有图像都有可被 4 整除的行(当特定字节填充时我必须插入 0)和每像素 24 位。即使是很少的提示也将不胜感激。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "bmp_header.h"

int main(void)
{
    FILE *f;
    f = fopen("captcha.bmp","rb");
    if ((f = fopen("captcha.bmp", "rb")) == NULL)
    {
        printf("Error opening file %s.\n", "captcha.bmp");
        exit(1);
    }
    fread(&BMP_header,sizeof(BMP_header),1,f);
    fread(&BMP_info_header,sizeof(BMP_info_header),1,f);
    fseek(f,BMP_header.imageDataOffset,SEEK_SET);
    int rows = (BMP_info_header.bitPix * BMP_info_header.width + 31 ) /32 * 4 ;
    char *PixelArray =malloc( rows * abs(BMP_info_header.height)*sizeof(char));
    int i;
    for( i =sizeof(BMP_header)+sizeof(BMP_info_header); i<=(rows * abs(BMP_info_header.height))-2;i+=3)
    {
        PixelArray[i]=255; // just a random value to test if this makese any sense
        PixelArray[i+1]=255;
        PixelArray[i+2]=255;

    }

    return 0;
}

还有,这里是bmp_header.h的内容:

#pragma pack(1)

struct bmp_fileheader
{
    unsigned char  fileMarker1; /* 'B' */
    unsigned char  fileMarker2; /* 'M' */
    unsigned int   bfSize; /* File's size */
    unsigned short unused1;
    unsigned short unused2;
    unsigned int   imageDataOffset; /* Offset to the start of image data */
}BMP_header,BMP_header_out;

struct bmp_infoheader
{
    unsigned int   biSize; /* Size of the info header - 40 bytes */
    signed int     width; /* Width of the image */
    signed int     height; /* Height of the image */
    unsigned short planes;
    unsigned short bitPix;
    unsigned int   biCompression;
    unsigned int   biSizeImage; /* Size of the image data */
    int            biXPelsPerMeter;
    int            biYPelsPerMeter;
    unsigned int   biClrUsed;
    unsigned int   biClrImportant;
}BMP_info_header,BMP_info_header_out;

#pragma pack()

好吧,我写了一些代码,用旧位图文件的原始尺寸写出了一个全黑的位图文件。

不能处理所有类型的位图。例如,16 色位图在我的程序没有考虑的信息 header 之后有一个调色板。每像素的位数也需要被 8 整除。如果满足这些先决条件,我相信它们在 24 位位图中,那么这个程序应该可以工作。

这里的主要代码在getNewImageData中。我们使用维基百科使用的相同公式计算图像的行大小 - 它计算所需的位,然后将其填充为四字节的倍数,然后将位转换为字节。然后我们将所有像素阵列内存设置为零(主要是我对在填充字节中保留值感到疑惑)。然后我们 运行 沿每一行编辑每个像素。最里面的for loop对应每个像素。通过中间 for loop 的一次迭代写入一个像素。

您显然可以修改此代码以将像素数据读入分配的内存部分,然后在适当的位置编辑像素数据,然后再将其写回。此示例代码不读取输入像素数据,只是写出与输入文件相同尺寸的黑色位图。

编辑:我想我应该提一下你做错了什么。

  • 您调用了 fopen 两次,在内存中的某处留下了一个打开的文件指针。
  • rows 可能应该重命名为 rowSize 因为它是以字节为单位的一行像素的大小
  • PixelArray 未被释放。
  • 您的 for loop 从偏移量(两个 header 的大小)开始,然后以像素数据的大小为界。 for loop 应该从 0 开始并达到像素数据的大小(除非您在执行此操作时正在读取像素数据......您可能不应该这样做)。
  • 您的 for loop 试图一次写入所有像素数据,但没有考虑到每行像素末尾有 0 到 31 位填充的事实(我的程序假定填充仅为 0、8、16 或 24 位)。

代码如下:

#include <stdio.h>
#include "bmp_header.h"
#include <stdlib.h>

int readBitmapHeaders(char* fileLocation, bmp_fileheader* fileheader, bmp_infoheader* infoheader)
{
    FILE* f;
    f = fopen(fileLocation, "rb");

    if (!f)
    {
        printf("Error opening file %s.\n", fileLocation);
        return 1;
    }

    fread(fileheader, sizeof(bmp_fileheader), 1, f);
    fread(infoheader, sizeof(bmp_infoheader), 1, f);

    fclose(f);
    return 0;
}

int writeBitmap(char* fileName, bmp_fileheader* fileheader, bmp_infoheader* infoheader, char* pixelArray, size_t pixelArraySize)
{
    FILE* out;
    out = fopen(fileName, "wb");
    if (!out)
    {
        printf("Error opening file %s.\n", fileName);
        return 1;
    }

    fwrite(fileheader, sizeof(bmp_fileheader), 1, out);
    fwrite(infoheader, sizeof(bmp_infoheader), 1, out);
    fwrite(pixelArray, pixelArraySize, 1, out);

    fclose(out);
    return 0;
}

char* getNewImageData(bmp_infoheader* infoheader, size_t* imageSize)
{
    //rowsize is padded to 4 bytes
    size_t rowSize = (infoheader->bitPix * infoheader->width + 31) / 32 * 4;

    size_t pixelArraySize = rowSize * abs(infoheader->height);

    char* pixelArray = (char*)malloc(pixelArraySize);
    if (!pixelArray)
    {
        return NULL;
    }

    memset(pixelArray, 0, pixelArraySize);

    size_t bytesPerPixel = infoheader->bitPix / 8;
    for (int i = 0; i < infoheader->height; i++)
    {
        for (int j = 0; j < infoheader->width; j++)
        {
            size_t offset = rowSize * i + bytesPerPixel * j;
            for (size_t k = 0; k < bytesPerPixel; k++)
            {
                pixelArray[offset + k] = 0;
            }
        }
    }

    if (imageSize)
    {
        *imageSize = pixelArraySize;
    }
    return pixelArray;
}

int main()
{
    char* fileLocation = "test.bmp";
    bmp_fileheader header;
    bmp_infoheader infoheader;

    int readResult = readBitmapHeaders(fileLocation, &header, &infoheader);
    if (readResult)
    {
        return readResult;
    }

    size_t pixelArraySize;
    char* pixelArray = getNewImageData(&infoheader, &pixelArraySize);
    if (!pixelArray)
    {
        printf("%s", "Failed to create the new image data. Exiting with fatal error.\n");
        return 1;
    }

    char* outFile = "out.bmp";
    int writeResult = writeBitmap(outFile, &header, &infoheader, pixelArray, pixelArraySize);

    free(pixelArray);
    return writeResult;
}

我稍微更改了位图 header 文件以对结构进行 typedef 并使生活更轻松(至少对我而言):

#pragma once
#pragma pack(1)

typedef struct _bmp_fileheader
{
    unsigned char  fileMarker1; /* 'B' */
    unsigned char  fileMarker2; /* 'M' */
    unsigned int   bfSize; /* File's size */
    unsigned short unused1;
    unsigned short unused2;
    unsigned int   imageDataOffset; /* Offset to the start of image data */
} bmp_fileheader;

typedef struct _bmp_infoheader
{
    unsigned int   biSize; /* Size of the info header - 40 bytes */
    signed int     width; /* Width of the image */
    signed int     height; /* Height of the image */
    unsigned short planes;
    unsigned short bitPix;
    unsigned int   biCompression;
    unsigned int   biSizeImage; /* Size of the image data */
    int            biXPelsPerMeter;
    int            biYPelsPerMeter;
    unsigned int   biClrUsed;
    unsigned int   biClrImportant;
} bmp_infoheader;

#pragma pack()