8bpp BMP - 将像素引用到颜色 table;只想读取一行像素; C++
8bpp BMP - refering pixels to the color table; want to read only one row of pixels; C++
我在读取 8 位灰度 bmp 时遇到问题。我能够从 header 获取信息并读取调色板,但我无法将像素值引用到调色板条目。 Here 我已经找到了如何读取像素数据,但实际上并不知道如何在带有调色板的 bmp 的情况下使用它。我是初学者。我的目标是一次只读取一行像素。
代码:
#include <iostream>
#include <fstream>
using namespace std;
int main(int arc, char** argv)
{ const char* filename="Row_tst.bmp";
remove("test.txt");
ofstream out("test.txt",ios_base::app);//file for monitoring the results
FILE* f = fopen(filename, "rb");
unsigned char info[54];
fread(info, sizeof(unsigned char), 54, f); // read the header
int width = *(int*)&info[18];
int height = *(int*)&info[22];
unsigned char palette[1024]; //read the palette
fread(palette, sizeof(unsigned char), 1024, f);
for(int i=0;i<1024;i++)
{ out<<"\n";
out<<(int)palette[i];
}
int paletteSmall[256]; //1024-byte palette won't be needed in the future
for(int i=0;i<256;i++)
{ paletteSmall[i]=(int)palette[4*i];
out<<paletteSmall[i]<<"\n";
}
int size = width;
//for(int j=0;j<height;j++)
{ unsigned char* data = new unsigned char[size];
fread(data, sizeof(unsigned char), size, f);
for(int i=0;i<width;i++)
{ cout<<"\n"<<i<<"\t"<<paletteSmall[*(int*)&data[i]];
}
delete [] data;
}
fclose(f);
return 0;
}
我在 test.txt 中得到的内容看起来不错 - 第一个值从 0 0 0 0 到 255 255 255 0(调色板),下一个值从 0 到 255(paletteSmall)。
问题是我无法将像素值引用到颜色 table 条目。我的应用程序崩溃了,其症状可能表明它试图使用 table 中一些不存在的元素。如果我理解正确,bmp 中颜色为 table 的像素应该包含一些颜色 table 元素,所以我不知道为什么它不起作用。我请求你的帮助。
您强制将 8 位值读取为 int
:
cout<<"\n"<<i<<"\t"<<paletteSmall[*(int*)&data[i]];
转换的数量表明您在这里遇到了问题,并且可能解决了一个接一个地添加转换直到 "it compiled"。事实证明,没有错误的编译与没有错误的工作是不同的。
这里发生的是你 强制 数据指针读取 4 个字节(或者与你的本地 int
大小一样多,无论如何)等等值几乎总是会超过 paletteSmall
的大小。 (另外,在所有情况下,最后一对值将无效,因为你读取的字节超出了data
的有效范围。)
因为图像数据本身是8位的,这里只需要
cout<<"\n"<<i<<"\t"<<paletteSmall[data[i]];
无需强制转换; data
是一个 unsigned char * 所以它的值限制在 0 到 255 之间,paletteSmall
是正确的大小。
关于选角
casting 的问题在于,如果您告诉编译器将某种类型的值 当作 处理,编译器会报错这完全是另一种类型。通过使用强制转换,你告诉它 "Trust me. I know what I am doing."
如果您确实不知道,这可能会导致几个问题:)
例如:你自己的一行
int width = *(int*)&info[18];
似乎有效,因为它 returns 正确的信息,但实际上这是一个快乐的意外。
数组 info
包含几个不相关的 unsigned char
值,您告诉编译器 是 一个 int
存储在起始位置#18 – 它信任你并读取一个整数。它假设 (1) 您想要组合成一个整数的字节数 实际上是它本身用于一个整数的字节数int
(sizeof(int)
),以及 (2) 各个字节的 order 与它在内部使用的相同 order (Endianness).
如果这些假设中的任何一个不成立,您会得到令人惊讶的结果;几乎肯定不是你想要的。
正确的过程是扫描 BMP file format 以了解 width
的值是如何存储的,然后使用该信息来获取您想要的数据。在这种情况下,width
是 "stored in little-endian format" 并且在偏移量 18 处为 4 个字节。有了它,您可以改用它:
int width = info[18]+(info[19]<<8)+(info[20]<<16)+(info[21]<<24);
不假设 int
有多大(除非它需要 至少 4 个字节),不假设顺序(移动值 'internally' 不依赖字节顺序)。
那么为什么它仍然有效(至少在您的计算机上)?在这十年中,int
最常见的大小是 4 个字节。最流行的 CPU 类型恰好以与它们在 BMP 中存储相同的顺序存储多字节值。将它们加在一起,你的代码在这十年内可以在大多数计算机上运行。一个快乐的意外。
如果您想在另一种类型的计算机(例如使用另一种字节顺序的嵌入式 ARM 系统)上编译您的代码,或者当使用编译器有一个更小的(.. 到现在应该是 very 旧的编译器)或更大的 int
(再等 10 年左右),或者如果你想要调整您的代码以读取其他类型的文件(它们将具有自己的参数,使用的字节顺序就是其中之一)。
我在读取 8 位灰度 bmp 时遇到问题。我能够从 header 获取信息并读取调色板,但我无法将像素值引用到调色板条目。 Here 我已经找到了如何读取像素数据,但实际上并不知道如何在带有调色板的 bmp 的情况下使用它。我是初学者。我的目标是一次只读取一行像素。
代码:
#include <iostream>
#include <fstream>
using namespace std;
int main(int arc, char** argv)
{ const char* filename="Row_tst.bmp";
remove("test.txt");
ofstream out("test.txt",ios_base::app);//file for monitoring the results
FILE* f = fopen(filename, "rb");
unsigned char info[54];
fread(info, sizeof(unsigned char), 54, f); // read the header
int width = *(int*)&info[18];
int height = *(int*)&info[22];
unsigned char palette[1024]; //read the palette
fread(palette, sizeof(unsigned char), 1024, f);
for(int i=0;i<1024;i++)
{ out<<"\n";
out<<(int)palette[i];
}
int paletteSmall[256]; //1024-byte palette won't be needed in the future
for(int i=0;i<256;i++)
{ paletteSmall[i]=(int)palette[4*i];
out<<paletteSmall[i]<<"\n";
}
int size = width;
//for(int j=0;j<height;j++)
{ unsigned char* data = new unsigned char[size];
fread(data, sizeof(unsigned char), size, f);
for(int i=0;i<width;i++)
{ cout<<"\n"<<i<<"\t"<<paletteSmall[*(int*)&data[i]];
}
delete [] data;
}
fclose(f);
return 0;
}
我在 test.txt 中得到的内容看起来不错 - 第一个值从 0 0 0 0 到 255 255 255 0(调色板),下一个值从 0 到 255(paletteSmall)。
问题是我无法将像素值引用到颜色 table 条目。我的应用程序崩溃了,其症状可能表明它试图使用 table 中一些不存在的元素。如果我理解正确,bmp 中颜色为 table 的像素应该包含一些颜色 table 元素,所以我不知道为什么它不起作用。我请求你的帮助。
您强制将 8 位值读取为 int
:
cout<<"\n"<<i<<"\t"<<paletteSmall[*(int*)&data[i]];
转换的数量表明您在这里遇到了问题,并且可能解决了一个接一个地添加转换直到 "it compiled"。事实证明,没有错误的编译与没有错误的工作是不同的。
这里发生的是你 强制 数据指针读取 4 个字节(或者与你的本地 int
大小一样多,无论如何)等等值几乎总是会超过 paletteSmall
的大小。 (另外,在所有情况下,最后一对值将无效,因为你读取的字节超出了data
的有效范围。)
因为图像数据本身是8位的,这里只需要
cout<<"\n"<<i<<"\t"<<paletteSmall[data[i]];
无需强制转换; data
是一个 unsigned char * 所以它的值限制在 0 到 255 之间,paletteSmall
是正确的大小。
关于选角
casting 的问题在于,如果您告诉编译器将某种类型的值 当作 处理,编译器会报错这完全是另一种类型。通过使用强制转换,你告诉它 "Trust me. I know what I am doing."
如果您确实不知道,这可能会导致几个问题:)
例如:你自己的一行
int width = *(int*)&info[18];
似乎有效,因为它 returns 正确的信息,但实际上这是一个快乐的意外。
数组 info
包含几个不相关的 unsigned char
值,您告诉编译器 是 一个 int
存储在起始位置#18 – 它信任你并读取一个整数。它假设 (1) 您想要组合成一个整数的字节数 实际上是它本身用于一个整数的字节数int
(sizeof(int)
),以及 (2) 各个字节的 order 与它在内部使用的相同 order (Endianness).
如果这些假设中的任何一个不成立,您会得到令人惊讶的结果;几乎肯定不是你想要的。
正确的过程是扫描 BMP file format 以了解 width
的值是如何存储的,然后使用该信息来获取您想要的数据。在这种情况下,width
是 "stored in little-endian format" 并且在偏移量 18 处为 4 个字节。有了它,您可以改用它:
int width = info[18]+(info[19]<<8)+(info[20]<<16)+(info[21]<<24);
不假设 int
有多大(除非它需要 至少 4 个字节),不假设顺序(移动值 'internally' 不依赖字节顺序)。
那么为什么它仍然有效(至少在您的计算机上)?在这十年中,int
最常见的大小是 4 个字节。最流行的 CPU 类型恰好以与它们在 BMP 中存储相同的顺序存储多字节值。将它们加在一起,你的代码在这十年内可以在大多数计算机上运行。一个快乐的意外。
如果您想在另一种类型的计算机(例如使用另一种字节顺序的嵌入式 ARM 系统)上编译您的代码,或者当使用编译器有一个更小的(.. 到现在应该是 very 旧的编译器)或更大的 int
(再等 10 年左右),或者如果你想要调整您的代码以读取其他类型的文件(它们将具有自己的参数,使用的字节顺序就是其中之一)。