C语言怎么取出一张256色位的bmp图像的某个像素的颜色

时间:2021-09-27 22:15:45
我想实现的是:将一张256色位图的bmp图像(1.bmp)上下左右移动N个像素(比如向右移5个像素,最左边的5个像素全涂成黑色)生成另一张图像(2.bmp)。我现在写的有些问题:
代码:
#include <stdio.h>
#include <windows.h>

int main()
{
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
FILE *pfin = fopen("F:\\1.bmp","rb");
FILE *pfout = fopen("F:\\2.bmp","wb");
fread(&fileHeader,sizeof(BITMAPFILEHEADER),1,pfin);
fread(&infoHeader,sizeof(BITMAPINFOHEADER),1,pfin);
int size = 512*512;
int i;
unsigned char img[512][512];
fread(img,sizeof(byte),size,pfin);
if(infoHeader.biBitCount == 8)
{
for(i=1;i<512;i++)
{
img[i][50]=0;

}


fwrite(&fileHeader,sizeof(fileHeader),1,pfout);
fwrite(&infoHeader,sizeof(infoHeader),1,pfout);
fwrite(img,sizeof(byte),size,pfout);
}

12 个解决方案

#1


bmp每行像素数据的字节数必须是4的倍数。不够需要补1或2或3个字节。
256索引色的每个像素是用一个字节表示的(用0~255表示其颜色索引)。

#2


格式化读取文件就能完成

#3


我该怎么获取像素的颜色值?
#include <stdio.h>
#include <windows.h>
int main()
{
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
FILE *pfin = fopen("F:\\1.bmp","rb");
FILE *pfout = fopen("F:\\2.bmp","wb");
fread(&fileHeader,sizeof(BITMAPFILEHEADER),1,pfin);
fread(&infoHeader,sizeof(BITMAPINFOHEADER),1,pfin);
int size = infoHeader.biWidth*infoHeader.biHeight;
int i,n;
RGBQUAD RGB;
unsigned char *img[512][512];
fread(img,sizeof(unsigned char *),size,pfin);

if(infoHeader.biBitCount == 8)
{
for(n=0;n<256;n++)
{

}
}
fwrite(&fileHeader,sizeof(fileHeader),1,pfout);
fwrite(&infoHeader,sizeof(infoHeader),1,pfout);
fwrite(img,sizeof(unsigned char*),size,pfout);
}

#4


比如表示某个像素的值为0x12,找调色板中从0x00到0xff的第0x12项对应的RGB值。

#5


8位读文件需跳过调色板,紧接就是索引值,生成8位前面部父基本一致,只需改宽高大小

#6


我参考了很多资料,想了三四天了,还是写不出来,各位大神能否帮忙写一点代码

#7



//////////////////////////////////////////////////////////////////////////
// Bitmap 文件(压缩)格式
typedef enum tag_bmp_encode
{
bmp_encode_none = 0, // 未压缩
bmp_encode_rle8, // rle8压缩
bmp_encode_rle4, // rle4压缩
bmp_encode_mask, // 由掩码(color_mask_abgr_t)决定

// os2 压缩方式
bmp_encode_os2_index4, // OS/2 的 4 位编码
bmp_encode_os2_index8, // OS/2 的 8 位编码
bmp_encode_os2_24, // OS/2 的 24 位编码
}bmp_encode_e;

#pragma pack(push, 1)
//////////////////////////////////////////////////////////////////////////
// 简单的 Bitmap 文件头,用于识别 Bitmap 文件类型。
struct bmp_simple_header_t
{
uint_16 type;
uint_32 file_size;
uint_32 reserved;
uint_32 data_offset;
uint_32 info_size;
};

//////////////////////////////////////////////////////////////////////////
// Windows Bitmap 文件头
struct bmp_info_windows_t
{
uint_32 size;
int_32 width;
int_32 height;
uint_16 plane_count;
uint_16 bit_count;
bmp_encode_e encode;
uint_32 image_size;
int_32 ppm_x; // pels per meter
int_32 ppm_y;
uint_32 color_used;
uint_32 color_important;
};

struct bmp_header_windows_t
{
uint_16 type;
uint_32 file_size;
uint_32 reserved;
uint_32  data_offset;
};

bmp_simple_header_t + bmp_header_windows_t + bmp_info_windows_t
这是标准的 windows bitmap 的文件头结构。

下面是确认出 bitmap 文件的格式,

const uint_16 BMP_HEADER_ID = 0x4D42; // 'BM',header.type
bmp_simple_header_t * header = (bmp_simple_header_t *)buffer;
if(header->type != BMP_HEADER_ID)
return 1;

int_x width = 0;
int_x height = 0;
color_mode_e src_mode = color_mode_invalid;
byte_t * conv_palette = nullptr;
byte_t * conv_buffer = nullptr;
int_x flags = 0;
bmp_header_windows_t * header = (bmp_header_windows_t *)buffer;
buffer += sizeof(bmp_header_windows_t);
if(header->type != BMP_HEADER_ID)
return false;

bmp_info_windows_t * info = (bmp_info_windows_t *)buffer;
buffer += sizeof(bmp_info_windows_t);

if(info->size != sizeof(bmp_info_windows_t) && info->size != sizeof(bmp_info_windows_t)+sizeof(color_mask_abgr_t))
return false;

width = (int_x)info->width;
height = (int_x)info->height;
// 翻转行序
if(height < 0)
height = -height;
else
flags |= IMAGE_CONVERT_FLIP_Y;

// 调色板、颜色掩码等额外信息
int_x extern_size = header->data_offset - sizeof(bmp_info_windows_t)-sizeof(bmp_header_windows_t);

conv_palette = nullptr;
conv_buffer = buffer;
// 颜色掩码确定颜色类型
if(info->encode == bmp_encode_mask)
{
IMAGE_ASSERT(extern_size == sizeof(color_mask_abgr_t));
color_mask_abgr_t * mask = (color_mask_abgr_t *)buffer;
src_mode = color_mode_from_mask_abgr(mask, info->bit_count);
conv_buffer = buffer + extern_size;
conv_palette = nullptr;
}
// rle8 编码
else if(info->encode == bmp_encode_rle8)
{
src_mode = color_mode_index8_rle2_x8r8g8b8;
conv_buffer = buffer + extern_size;
conv_palette = buffer;
}
// rle4 编码
else if(info->encode == bmp_encode_rle4)
{
src_mode = color_mode_index4_rle2_x8r8g8b8;
conv_buffer = buffer + extern_size;
conv_palette = buffer;
}
// 有调色板
else if(extern_size)
{
src_mode = color_mode_invalid;
switch(info->bit_count)
{
case 1:
src_mode = color_mode_index1_x8r8g8b8;
break;
case 2:
src_mode = color_mode_index2_x8r8g8b8;
break;
case 4:
src_mode = color_mode_index4_x8r8g8b8;
break;
case 8:
src_mode = color_mode_index8_x8r8g8b8;
break;
default:
break;
}
conv_buffer = buffer + extern_size;
conv_palette = buffer;
}
else // 没有调色板
{
src_mode = color_mode_invalid;
switch(info->bit_count)
{
case 2:
src_mode = color_mode_gray2;
break;
case 4:
src_mode = color_mode_gray4;
break;
case 8:
src_mode = color_mode_gray8;
break;
case 16:
src_mode = color_mode_x1r5g5b5;
break;
case 24:
src_mode = color_mode_r8g8b8;
break;
case 32:
src_mode = color_mode_a8r8g8b8;
break;
default:
break;
}
conv_buffer = buffer;
conv_palette = nullptr;
}

其中,

case 8:
src_mode = color_mode_index8_x8r8g8b8;
break;

应该就是楼主的 8-bit 格式了,当然了读取就比较简单了
conv_palette 指向的是调色板,
conv_buffer 指向的是索引数据。
而将索引数据映射到调色板里就不用我说了吧,楼主自己会做了。
需要注意的是,调色板里的颜色格式是 x8r8g8b8。
跨度 pitch = align_to_4(width),需要 4 字节对齐。

#8


怎么实现图片的平移啊

#9


我这样写的代码,实现的效果有一点错误,但是我看不出到底是哪里不对,求大神帮忙看看
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main()
{
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
FILE *pfin = fopen("F:\\1.bmp","rb");
FILE *pfout = fopen("F:\\2.bmp","wb");
fread(&fileHeader,sizeof(BITMAPFILEHEADER),1,pfin);
fread(&infoHeader,sizeof(BITMAPINFOHEADER),1,pfin);
int bfsize = fileHeader.bfSize;//图片的大小 
int size = infoHeader.biWidth*infoHeader.biHeight;
int shuju = fileHeader.bfOffBits;//位图数据区的起始字节 
int height = infoHeader.biHeight;
int width = infoHeader.biWidth;
int i,j;
unsigned char *color[256][4];
fseek(pfin,54L,SEEK_SET);
fread(color,sizeof(unsigned char *),256,pfin);
unsigned char *img[512][512];
fseek(pfin,shuju,SEEK_SET);
int left = 0, right = 0, up = 0, down = 0;
fread(img,1,bfsize-shuju,pfin);
printf("w:up\n");
printf("s:down\n");
printf("a:left\n");
printf("d:right\n");
printf("请输入:\n");
char f;
scanf("%c",&f);
if(f=='w')
{
printf("请输入向上移动多少?\n");
scanf("%d",&up);
}
if(f=='s')
{
printf("请输入向下移动多少?\n");
scanf("%d",&down);
}
if(f=='a')
{
printf("请输入向左移动多少?\n");
scanf("%d",&left);
}
if(f=='d')
{
printf("请输入向右移动多少?\n");
scanf("%d",&right);
}
if(infoHeader.biBitCount == 8)
{
for(i=511;i>0+up;i--)//上 
{
for(j=0;j<512;j++)
{
img[i][j]=img[i-up][j];
}

for(i=0;i<512-down;i++)//下 
{
for(j=0;j<512;j++)
{
//*img[i][j]>>512;
img[i][j]=img[i+down][j];
}
}
for(i=0;i<512;i++)//左 
{
for(j=0;j<512-left;j++)
{
img[i][j]=img[i][j+left];

}
}
for(i=0;i<512;i++)//右 
{
for(j=511;j>0+right;j--)
{
img[i][j]=img[i][j-right];
}
}
}

fwrite(&fileHeader,sizeof(fileHeader),1,pfout);
fwrite(&infoHeader,sizeof(infoHeader),1,pfout);
fwrite(color,sizeof(unsigned char *),256,pfout);
fwrite(img,sizeof(unsigned char *),(bfsize-shuju)/4,pfout);

}

#10


请判断每个函数的返回值。

代码功能归根结底不是别人帮自己看或讲解或注释出来的;而是被自己静下心来花足够长的时间和精力亲自动手单步或设断点或对执行到某步获得的中间结果显示或写到日志文件中一步一步分析出来的。
提醒:再牛×的老师也无法代替学生自己领悟和上厕所!
单步调试和设断点调试(VS IDE中编译连接通过以后,按F10或F11键单步执行,按Shift+F11退出当前函数;在某行按F9设断点后按F5执行停在该断点处。)是程序员必须掌握的技能之一。

#11


恩,我知道了,谢谢!

#12


这个很多教材都有示例嘛

#1


bmp每行像素数据的字节数必须是4的倍数。不够需要补1或2或3个字节。
256索引色的每个像素是用一个字节表示的(用0~255表示其颜色索引)。

#2


格式化读取文件就能完成

#3


我该怎么获取像素的颜色值?
#include <stdio.h>
#include <windows.h>
int main()
{
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
FILE *pfin = fopen("F:\\1.bmp","rb");
FILE *pfout = fopen("F:\\2.bmp","wb");
fread(&fileHeader,sizeof(BITMAPFILEHEADER),1,pfin);
fread(&infoHeader,sizeof(BITMAPINFOHEADER),1,pfin);
int size = infoHeader.biWidth*infoHeader.biHeight;
int i,n;
RGBQUAD RGB;
unsigned char *img[512][512];
fread(img,sizeof(unsigned char *),size,pfin);

if(infoHeader.biBitCount == 8)
{
for(n=0;n<256;n++)
{

}
}
fwrite(&fileHeader,sizeof(fileHeader),1,pfout);
fwrite(&infoHeader,sizeof(infoHeader),1,pfout);
fwrite(img,sizeof(unsigned char*),size,pfout);
}

#4


比如表示某个像素的值为0x12,找调色板中从0x00到0xff的第0x12项对应的RGB值。

#5


8位读文件需跳过调色板,紧接就是索引值,生成8位前面部父基本一致,只需改宽高大小

#6


我参考了很多资料,想了三四天了,还是写不出来,各位大神能否帮忙写一点代码

#7



//////////////////////////////////////////////////////////////////////////
// Bitmap 文件(压缩)格式
typedef enum tag_bmp_encode
{
bmp_encode_none = 0, // 未压缩
bmp_encode_rle8, // rle8压缩
bmp_encode_rle4, // rle4压缩
bmp_encode_mask, // 由掩码(color_mask_abgr_t)决定

// os2 压缩方式
bmp_encode_os2_index4, // OS/2 的 4 位编码
bmp_encode_os2_index8, // OS/2 的 8 位编码
bmp_encode_os2_24, // OS/2 的 24 位编码
}bmp_encode_e;

#pragma pack(push, 1)
//////////////////////////////////////////////////////////////////////////
// 简单的 Bitmap 文件头,用于识别 Bitmap 文件类型。
struct bmp_simple_header_t
{
uint_16 type;
uint_32 file_size;
uint_32 reserved;
uint_32 data_offset;
uint_32 info_size;
};

//////////////////////////////////////////////////////////////////////////
// Windows Bitmap 文件头
struct bmp_info_windows_t
{
uint_32 size;
int_32 width;
int_32 height;
uint_16 plane_count;
uint_16 bit_count;
bmp_encode_e encode;
uint_32 image_size;
int_32 ppm_x; // pels per meter
int_32 ppm_y;
uint_32 color_used;
uint_32 color_important;
};

struct bmp_header_windows_t
{
uint_16 type;
uint_32 file_size;
uint_32 reserved;
uint_32  data_offset;
};

bmp_simple_header_t + bmp_header_windows_t + bmp_info_windows_t
这是标准的 windows bitmap 的文件头结构。

下面是确认出 bitmap 文件的格式,

const uint_16 BMP_HEADER_ID = 0x4D42; // 'BM',header.type
bmp_simple_header_t * header = (bmp_simple_header_t *)buffer;
if(header->type != BMP_HEADER_ID)
return 1;

int_x width = 0;
int_x height = 0;
color_mode_e src_mode = color_mode_invalid;
byte_t * conv_palette = nullptr;
byte_t * conv_buffer = nullptr;
int_x flags = 0;
bmp_header_windows_t * header = (bmp_header_windows_t *)buffer;
buffer += sizeof(bmp_header_windows_t);
if(header->type != BMP_HEADER_ID)
return false;

bmp_info_windows_t * info = (bmp_info_windows_t *)buffer;
buffer += sizeof(bmp_info_windows_t);

if(info->size != sizeof(bmp_info_windows_t) && info->size != sizeof(bmp_info_windows_t)+sizeof(color_mask_abgr_t))
return false;

width = (int_x)info->width;
height = (int_x)info->height;
// 翻转行序
if(height < 0)
height = -height;
else
flags |= IMAGE_CONVERT_FLIP_Y;

// 调色板、颜色掩码等额外信息
int_x extern_size = header->data_offset - sizeof(bmp_info_windows_t)-sizeof(bmp_header_windows_t);

conv_palette = nullptr;
conv_buffer = buffer;
// 颜色掩码确定颜色类型
if(info->encode == bmp_encode_mask)
{
IMAGE_ASSERT(extern_size == sizeof(color_mask_abgr_t));
color_mask_abgr_t * mask = (color_mask_abgr_t *)buffer;
src_mode = color_mode_from_mask_abgr(mask, info->bit_count);
conv_buffer = buffer + extern_size;
conv_palette = nullptr;
}
// rle8 编码
else if(info->encode == bmp_encode_rle8)
{
src_mode = color_mode_index8_rle2_x8r8g8b8;
conv_buffer = buffer + extern_size;
conv_palette = buffer;
}
// rle4 编码
else if(info->encode == bmp_encode_rle4)
{
src_mode = color_mode_index4_rle2_x8r8g8b8;
conv_buffer = buffer + extern_size;
conv_palette = buffer;
}
// 有调色板
else if(extern_size)
{
src_mode = color_mode_invalid;
switch(info->bit_count)
{
case 1:
src_mode = color_mode_index1_x8r8g8b8;
break;
case 2:
src_mode = color_mode_index2_x8r8g8b8;
break;
case 4:
src_mode = color_mode_index4_x8r8g8b8;
break;
case 8:
src_mode = color_mode_index8_x8r8g8b8;
break;
default:
break;
}
conv_buffer = buffer + extern_size;
conv_palette = buffer;
}
else // 没有调色板
{
src_mode = color_mode_invalid;
switch(info->bit_count)
{
case 2:
src_mode = color_mode_gray2;
break;
case 4:
src_mode = color_mode_gray4;
break;
case 8:
src_mode = color_mode_gray8;
break;
case 16:
src_mode = color_mode_x1r5g5b5;
break;
case 24:
src_mode = color_mode_r8g8b8;
break;
case 32:
src_mode = color_mode_a8r8g8b8;
break;
default:
break;
}
conv_buffer = buffer;
conv_palette = nullptr;
}

其中,

case 8:
src_mode = color_mode_index8_x8r8g8b8;
break;

应该就是楼主的 8-bit 格式了,当然了读取就比较简单了
conv_palette 指向的是调色板,
conv_buffer 指向的是索引数据。
而将索引数据映射到调色板里就不用我说了吧,楼主自己会做了。
需要注意的是,调色板里的颜色格式是 x8r8g8b8。
跨度 pitch = align_to_4(width),需要 4 字节对齐。

#8


怎么实现图片的平移啊

#9


我这样写的代码,实现的效果有一点错误,但是我看不出到底是哪里不对,求大神帮忙看看
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main()
{
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
FILE *pfin = fopen("F:\\1.bmp","rb");
FILE *pfout = fopen("F:\\2.bmp","wb");
fread(&fileHeader,sizeof(BITMAPFILEHEADER),1,pfin);
fread(&infoHeader,sizeof(BITMAPINFOHEADER),1,pfin);
int bfsize = fileHeader.bfSize;//图片的大小 
int size = infoHeader.biWidth*infoHeader.biHeight;
int shuju = fileHeader.bfOffBits;//位图数据区的起始字节 
int height = infoHeader.biHeight;
int width = infoHeader.biWidth;
int i,j;
unsigned char *color[256][4];
fseek(pfin,54L,SEEK_SET);
fread(color,sizeof(unsigned char *),256,pfin);
unsigned char *img[512][512];
fseek(pfin,shuju,SEEK_SET);
int left = 0, right = 0, up = 0, down = 0;
fread(img,1,bfsize-shuju,pfin);
printf("w:up\n");
printf("s:down\n");
printf("a:left\n");
printf("d:right\n");
printf("请输入:\n");
char f;
scanf("%c",&f);
if(f=='w')
{
printf("请输入向上移动多少?\n");
scanf("%d",&up);
}
if(f=='s')
{
printf("请输入向下移动多少?\n");
scanf("%d",&down);
}
if(f=='a')
{
printf("请输入向左移动多少?\n");
scanf("%d",&left);
}
if(f=='d')
{
printf("请输入向右移动多少?\n");
scanf("%d",&right);
}
if(infoHeader.biBitCount == 8)
{
for(i=511;i>0+up;i--)//上 
{
for(j=0;j<512;j++)
{
img[i][j]=img[i-up][j];
}

for(i=0;i<512-down;i++)//下 
{
for(j=0;j<512;j++)
{
//*img[i][j]>>512;
img[i][j]=img[i+down][j];
}
}
for(i=0;i<512;i++)//左 
{
for(j=0;j<512-left;j++)
{
img[i][j]=img[i][j+left];

}
}
for(i=0;i<512;i++)//右 
{
for(j=511;j>0+right;j--)
{
img[i][j]=img[i][j-right];
}
}
}

fwrite(&fileHeader,sizeof(fileHeader),1,pfout);
fwrite(&infoHeader,sizeof(infoHeader),1,pfout);
fwrite(color,sizeof(unsigned char *),256,pfout);
fwrite(img,sizeof(unsigned char *),(bfsize-shuju)/4,pfout);

}

#10


请判断每个函数的返回值。

代码功能归根结底不是别人帮自己看或讲解或注释出来的;而是被自己静下心来花足够长的时间和精力亲自动手单步或设断点或对执行到某步获得的中间结果显示或写到日志文件中一步一步分析出来的。
提醒:再牛×的老师也无法代替学生自己领悟和上厕所!
单步调试和设断点调试(VS IDE中编译连接通过以后,按F10或F11键单步执行,按Shift+F11退出当前函数;在某行按F9设断点后按F5执行停在该断点处。)是程序员必须掌握的技能之一。

#11


恩,我知道了,谢谢!

#12


这个很多教材都有示例嘛