0

我在读取 8 位灰度 bmp 时遇到问题。我能够从标题中获取信息并读取调色板,但我无法将像素值引用到调色板条目。在这里,我找到了如何读取像素数据,但实际上并没有找到如何在 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(调色板小)。

问题是我无法将像素值引用到颜色表条目。我的应用程序崩溃了,症状可能表明它试图使用表格中一些不存在的元素。如果我理解正确,带有颜色表的 bmp 像素应该包含许多颜色表元素,所以我不知道为什么它不起作用。我请求你的帮助。

4

1 回答 1

1

您正在强制将 8 位值读取为int

cout<<"\n"<<i<<"\t"<<paletteSmall[*(int*)&data[i]];

演员表的数量表明您在这里遇到了问题,并且可能已决定在“编译”之前一个接一个地添加演员表。事实证明,没有错误的编译与没有错误的工作不同。

这里发生的情况是您强制数据指针读取 4 个字节(或者与本地int大小一样多),因此该值几乎总是超过paletteSmall. (此外,最后几个值在任何情况下都是无效的,因为您读取的字节超出了 的有效范围data。)

因为图像数据本身是 8 位的,所以这里只需要

cout<<"\n"<<i<<"\t"<<paletteSmall[data[i]];

无需强制转换;data是一个 unsigned char * 所以它的值被限制在 0 到 255 之间,并且paletteSmall是完全正确的大小。


关于铸造

强制转换的问题在于,如果您明确告诉编译器将某种类型的值视为完全是另一种类型,您的编译器会抱怨。通过使用演员表,您是在告诉它“相信我。我知道我在做什么。”

如果您实际上不知道,这可能会导致几个问题:)

例如:一行如你自己

int width = *(int*)&info[18];

似乎有效,因为它返回了正确的信息,但实际上这是一个快乐的意外。

该数组info包含几个不连贯的unsigned char值,您告诉编译器int位置 #18 开始存储了一个- 它信任您并读取一个整数。它假设(1)您要组合成整数的字节数实际上是本身用于int( sizeof(int)) 的字节数,并且 (2) 各个字节的顺序与其内部使用的顺序相同(字节序)。

如果这些假设中的任何一个是错误的,您都会得到令人惊讶的结果;几乎可以肯定不是你想要的。

正确的过程是扫描BMP 文件格式以了解值的width存储方式,然后使用该信息获取所需的数据。在这种情况下,width“以 little-endian 格式存储”并且在偏移量 18 处为 4 个字节。有了它,您可以改用它:

int width = info[18]+(info[19]<<8)+(info[20]<<16)+(info[21]<<24);

没有关于 an 有多大的假设int(除了它需要至少4 个字节),没有关于顺序的假设(“内部”移动值不依赖于字节序)。

那么为什么它仍然有效(至少在您的计算机上)?这十年中最常见的大小int是 4 个字节。最流行的 CPU 类型恰好以与存储在 BMP 中的顺序相同的顺序存储多字节值。在这十年中,将它们加在一起,您的代码可以在大多数计算机上运行。一个快乐的意外。

如果您想在另一种类型的计算机(例如使用另一种字节序的嵌入式 ARM 系统)上编译代码,或者当使用的编译器具有较小的(.. 这现在已经 老了),上述情况可能正确编译器)或更大的大小(只需再等 10 年左右),或者如果您想调整代码以读取其他类型的文件(这些文件将具有自己的参数,并且使用的字节序就是其中之一)。int

于 2015-05-29T09:23:44.433 回答