位图颜色 - C
Bitmap color - C
我仍然无法访问位图图像的颜色位。问题是,将位图的内容保存到缓冲区后,我不知道:
从哪里开始循环(如果我从 0 开始我认为它会删除 headers)?
如何访问字节并进行更改(将 BMP 中的颜色转换为输出中所需的颜色)?
以及,如何将缓冲区插入到新的位图文件中?
我要修改的所有图像都有可被 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()
我仍然无法访问位图图像的颜色位。问题是,将位图的内容保存到缓冲区后,我不知道:
从哪里开始循环(如果我从 0 开始我认为它会删除 headers)?
如何访问字节并进行更改(将 BMP 中的颜色转换为输出中所需的颜色)?
以及,如何将缓冲区插入到新的位图文件中?
我要修改的所有图像都有可被 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()