如何解决如何快速有效地在 BMP 中收集每个像素的 4 个字节的数据并将它们组装成一个结构体?
我发现了这个 Loading a tga/bmp file in C++/OpenGL,它完美无缺,我想在我的项目中使用它。但是我的项目需要一个 RGB 结构来修改像素缓冲区。
glDrawPixels(win_height,win_width,GL_RGB,GL_FLOAT,myBuffer);
需要进行一些更改才能使其在我的项目中工作。
typedef union PixelInfo
{
std::uint32_t Colour;
struct
{
float b,g,r,a;
};
} *PPixelInfo;
class BMP
{
private:
std::uint32_t width,height;
std::uint16_t BitsPerPixel;
std::vector<std::uint8_t> Pixels;
public:
BMP(const char* FilePath);
std::vector<std::uint8_t> GetPixels() const {return this->Pixels;}
std::uint32_t GetWidth() const {return this->width;}
std::uint32_t GetHeight() const {return this->height;}
bool HasAlphaChannel() {return BitsPerPixel == 32;}
};
BMP::BMP(const char* FilePath)
{
std::fstream hFile(FilePath,std::ios::in | std::ios::binary);
if (!hFile.is_open()) throw std::invalid_argument("Error: File Not Found.");
hFile.seekg(0,std::ios::end);
std::size_t Length = hFile.tellg();
hFile.seekg(0,std::ios::beg);
std::vector<std::uint8_t> FileInfo(Length);
hFile.read(reinterpret_cast<char*>(FileInfo.data()),54);
if(FileInfo[0] != 'B' && FileInfo[1] != 'M')
{
hFile.close();
throw std::invalid_argument("Error: Invalid File Format. Bitmap Required.");
}
if (FileInfo[28] != 24 && FileInfo[28] != 32)
{
hFile.close();
throw std::invalid_argument("Error: Invalid File Format. 24 or 32 bit Image Required.");
}
BitsPerPixel = FileInfo[28];
width = FileInfo[18] + (FileInfo[19] << 8);
height = FileInfo[22] + (FileInfo[23] << 8);
std::uint32_t PixelsOffset = FileInfo[10] + (FileInfo[11] << 8);
std::uint32_t size = ((width * BitsPerPixel + 31) / 32) * 4 * height;
Pixels.resize(size);
hFile.seekg (PixelsOffset,std::ios::beg);
hFile.read(reinterpret_cast<char*>(Pixels.data()),size);
hFile.close();
}
int main()
{
BMP info = BMP("Fixedsys16x28.bmp");
//info.
std::cout << "getwidth: " << info.GetWidth() << endl;
std::cout << "getheight: " << info.GetHeight()<< endl;
// for(auto i : info.GetPixels()){
// //float i_float = float(i);
// //float res = i_float / 15.0;
// //std::cout << std::hex << (int)i << " " << endl;
// std::cout << setprecision(2) << fixed << (float) i / 255 << " " << endl;
// }
std::cout << endl;
std::cout << "size: " << info.GetPixels().size() << endl;
std::cout << "alpha: " << info.HasAlphaChannel() << endl;
std::cout << setprecision(2) << fixed;
for(int i = 0; i < static_cast<int>(info.GetPixels().size()); i++){
for(int j = 0; j < 3; j++){
if(j == 0){
std::cout << (float) info.GetPixels()[i]/ 255 << " ";
}
else if(j == 1){
std::cout << (float) info.GetPixels()[i] / 255 << " ";
}
else{
std::cout << (float) info.GetPixels()[i] / 255;
}
}
std::cout << endl;
}
getchar();
GLuint texture = 0;
glGenTextures(1,&texture);
glBindTexture(GL_TEXTURE_2D,texture);
glTexImage2D(GL_TEXTURE_2D,info.HasAlphaChannel() ? GL_RGBA : GL_RGB,info.GetWidth(),info.HasAlphaChannel() ? GL_BGRA : GL_BGR,GL_UNSIGNED_BYTE,info.GetPixels().data());
}
这行代码怎么改?
hFile.read(reinterpret_cast<char*>(Pixels.data()),size);
这样矢量就被归档了...
typedef union PixelInfo
{
std::uint32_t Colour;
struct
{
float B,G,R,A;
};
} *PPixelInfo;
收藏。
在更改矢量设置后或使用另一个。
什么样的方法/函数,可以代替Pixels.data()
,所以它收集这4个字节并在结构PixelInfo
的类型中相应地组装它们?
因为 Pixels.data()
基本上提供了一个指向像素向量的指针。
可以执行请求的任务的方法是什么样的?
收集 4 个字节的数据并相应地对其进行格式化,然后将它们放入 vector<PixelInfo>
同时将“std::uint8_t”转换为浮点数。快速高效。
它应该考虑到这一点“info.HasAlphaChannel()”。
附带的图片数据为:
512×84,像素矢量大小为“129024”。
43008×3 字节 = 129024。
我在像素向量上做了这个循环:
std::cout << setprecision(2) << fixed;
for(int i = 0; i < static_cast<int>(info.GetPixels().size()); i++){
for(int j = 0; j < 3; j++){
if(j == 0){
std::cout << (float) info.GetPixels()[i]/ 255 << " ";
}
else if(j == 1){
std::cout << (float) info.GetPixels()[i] / 255 << " ";
}
else{
std::cout << (float) info.GetPixels()[i] / 255;
}
}
std::cout << endl;
}
看起来工作正常,甚至最后一行也包含 3 个“字节”的数据。
对于 400 x 400 像素的图像,此例程需要 20 秒。
vector<PixelInfo> pixelStore (info->GetPixels().size() / 3);
int counter = 0;
for(int i = 0; i < size; i+=3){
PixelInfo temp;
temp.r = 0;
temp.g = 0;
temp.b = 0;
for(int j = 0; j < 3; j++){
if(j == 0){
temp.r = info->GetPixels()[i + j];
}
else if(j == 1){
temp.g = info->GetPixels()[i + j];
}
else{
temp.b = info->GetPixels()[i + j];
}
if(j == 2){
//pixelStore.push_back(temp);
pixelStore[counter] = temp;
counter++;
}
}
}
解决方法
您的代码显示 std::vector<std::uint8_t> Pixels;
,我想这是一个 8 位像素序列。你想要一个 PixelInfo
的向量吗?现在该结构是否与文件中的字节格式匹配?如果是这样,那有什么问题?
如果文件中的字节与您的结构布局(包括机器的 Endian!)相匹配,只需按照相同的方式进行操作:
vector<PixelInfo> Pixels;
size_t pixels_to_read = ....??? ;
Pixels.resize(pixels_to_read);
hFile.read (reinterpret_cast<char*>(Pixels.data()),pixels_to_read*sizeof(PixelInfo));
更新
收集 4 个字节的数据并相应地格式化它们并将它们放入向量中,同时将“std::uint8_t”转换为浮点数。快速高效。
std::byte buf[4];
hFile.read (reinterpret_cast<char*>(buf),4); // read 4 bytes from file
PixelInfo px;
px.R = buf[0]; // converts 8-bit integer to float
px.G = buf[1];
px.B = buf[2];
px.A = buf[3];
请注意,您的 PixelInfo
并集没有意义——32 位颜色如何与 4 个 32 位组件的布局相匹配?此外,我不认为这个定义是合法的,因为没有匿名结构这样的东西。如果可以编译,则它必须是编译器扩展。
至于快速: 一次读取很多,而不是一个像素。如果将整个文件读入内存不切实际,那么整行就很好。然后您只需沿着您加载的缓冲区前进。
您可以希望优化器对 4 个单独的语句进行自动向量化。如果您只围绕该部分(处理整行)有一个紧凑而简单的循环,您可能会让编译器对其进行矢量化。
由于左侧使用不同的名称而不是索引,因此您必须编写 4 个单独的语句,这一事实不应影响整体速度。
400×400 像素的图像需要 20 秒?我认为它会是瞬间的。我有加载/转换/解码图形文件的经验,早在 90 年代初就为 DOS 和裸机(16 位 x86 代码)编写了一个库。您可以尝试对仅执行上面列出的这种转换的代码进行基准测试,看看这是否是您的瓶颈。查看生成的代码(在调试器中,或使用编译器资源管理器)以查看它是否正在矢量化。如果您的编译器可用,请考虑直接调用向量原语内部指令。看看您是否使用了所有正确的编译器优化标志。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。