【问题标题】:C++: Read bitmap wrongC++:读取位图错误
【发布时间】:2016-04-05 13:35:08
【问题描述】:

我正在尝试使用 c++ 读取.bmp 文件,并将标准化的灰度值(RGB 值的平均值)保存到 Ubuntu 14.04 下的向量中。不知何故,向量的值最终完全错误。你能想象为什么吗?

std::vector<double> readBMP(const char* filename, int* width, int* height){
    std::vector<double> bmp;
    FILE* f = fopen(filename, "rb");

    if(f == NULL){
        std::cerr << "file not found!" << std::endl;
        std::vector<double> empty;
        width = NULL;
        height = NULL;
        return empty;
    }

    unsigned char info[54];
    fread(info, sizeof(unsigned char), 54, f); // read the 54-byte header

    // extract image height and width from header
    *width = *(int*)&info[18];
   *height = *(int*)&info[22];
    int data_offset = *(int*)(&info[0x0A]);
    fseek(f, (long int)(data_offset - 54), SEEK_CUR);

    int row_padded = (*width*3 + 3) & (~3);
    unsigned char* data = new unsigned char[row_padded];
    unsigned char tmp;

    for(int i = 0; i < *height; i++)
    {
        fread(data, sizeof(unsigned char), row_padded, f);
        for(int j = 0; j < *width*3; j += 3)
        {
            // Convert (B, G, R) to (R, G, B)
            tmp = data[j];
            data[j] = data[j+2];
            data[j+2] = tmp;
            bmp.push_back(((double)data[j]+(double)data[j+1]+(double)data[j+2])/(3*255));

            std::cout << "R: "<< (int)data[j] << " G: " << (int)data[j+1]<< " B: " << (int)data[j+2]<< std::endl;
        }
    }
    return bmp;
}

我打印了 rgb 值,并用一个有四个像素的示例图像进行了检查:

black | black | black
---------------------
grey  | grey  | grey
---------------------
white | white | white

预期的输出应该是(它被颠倒了):

R: 255 G: 255 B: 255
R: 255 G: 255 B: 255
R: 255 G: 255 B: 255
R: 128 G: 128 B: 128
R: 128 G: 128 B: 128
R: 128 G: 128 B: 128
R: 0 G: 0 B: 0
R: 0 G: 0 B: 0
R: 0 G: 0 B: 0

但它是:

R: 255 G: 255 B: 255
R: 255 G: 255 B: 255
R: 255 G: 255 B: 255
R: 128 G: 128 B: 255
R: 128 G: 255 B: 128
R: 255 G: 128 B: 128
R: 0 G: 0 B: 255
R: 0 G: 255 B: 0
R: 255 G: 0 B: 0

注意: 该代码是此问题答案的修改版本: read pixel value in bmp file

【问题讨论】:

  • 尺寸值是否与宽*高匹配? (标头中的地址 0x22)。还要确保除了 54 字节标头之外没有要考虑的偏移量 (0x0A)。
  • 我检查了宽度和高度,读取正确。你的意思是什么偏移量?
  • 有时标头不是标准的并且大于 54 字节。如果是这种情况,您需要使用fseek 将光标移动到数据块的开头。后续问题,您使用的是什么操作系统?
  • 我更新了问题,我使用的是 Ubuntu 14.04。我怎么看它的标题不标准?
  • int data_offset = *(int*)(&amp;info[0x0A]); fseek(f, (long int)(data_offset - 54), SEEK_CUR);

标签: c++ colors bitmap


【解决方案1】:

根据其编码方式,您的 BMP 标头可能不是标准的,其大小将大于 54 字节。如果是这种情况,您需要使用fseek 将光标移动到数据块的开头。

int data_offset = *(int*)(&info[0x0A]); 

if (data_offset > 54) {
    fseek(f, (long int)(data_offset - 54), SEEK_CUR);
} 

正如 Brandon 所指出的,您的图片按照格式规范的规定倒置编码(因为您指定在 Ubuntu 上):

像素数组是一个 32 位 DWORD 块,逐个像素地描述图像。通常,像素相对于正常图像光栅扫描顺序“倒置”存储,从左下角开始,从左到右,然后从图像的底部到顶部逐行存储。 [5]除非使用 BITMAPCOREHEADER,否则当 Image Height 值为负时,也可以从上到下存储未压缩的 Windows 位图。

来源:https://en.wikipedia.org/wiki/BMP_file_format

【讨论】:

  • 谢谢,这解决了 2x2 的问题,但现在我用 3x3 尝试了它,它再次读取错误的值。虽然前3个像素是对的,但后面有错误
【解决方案2】:

最后我的代码是正确的,以防有人偶然发现这个问题:我的错误是,图像在 ABGR 中,我假设为 BGR。

#include <cstdio>
#include <iostream>
#include <vector>

std::vector<double> readBMP(const char* filename, int* width, int* height){
    std::vector<double> bmp;
    FILE* f = fopen(filename, "rb");

    if(f == NULL){
        std::cerr << "file not found!" << std::endl;
        std::vector<double> empty;
        width = NULL;
        height = NULL;
        return empty;
    }

    unsigned char info[54];
    fread(info, sizeof(unsigned char), 54, f); // read the 54-byte header

    // extract image height and width from header
    *width = *(int*)&info[18];
    *height = *(int*)&info[22];

    int data_offset = *(int*)(&info[0x0A]);
    if(data_offset > 0)
        fseek(f, (long int)(data_offset - 53), SEEK_CUR);

    std::cout << "  Name: " << filename << std::endl;
    std::cout << " Width: " << *width << std::endl;
    std::cout << "Height: " << *height << std::endl;
    std::cout << "Offset: " << data_offset << std::endl;

    int row_padded = (*width*4 + 4) & (~4);
    unsigned char* data = new unsigned char[row_padded];
    //unsigned char tmp;

    for(int i = 0; i < *height; i++){
        fread(data, sizeof(unsigned char), row_padded, f);
        for(int j = 0; j < row_padded; j += 4)
        {
            // Convert (B, G, R) to (R, G, B)
            //tmp = data[j];
            //data[j] = data[j+2];
            //data[j+2] = tmp;
            bmp.push_back(((double)data[j+2]+(double)data[j+1]+(double)data[j+2])/(3*255));

            //std::cout << "R: "<< (int)data[j+1] << " G: " << (int)data[j+2]<< " B: " << (int)data[j+3]<< std::endl;
        }
    }
    free(data);

    //reverse order of the vector
    std::vector<double>bmp_final;
    std::cout << bmp.size() << std::endl;
    for(int i=*height-1; i>=0; --i){
        for(int j=0; j<*width; ++j){
            bmp_final.push_back(bmp.at(*width*i+j));
        }
    }

    return bmp_final;
}

感谢大家的帮助!

【讨论】:

    【解决方案3】:

    停止对偏移量进行硬编码。像素的偏移量实际上是在info[10] + (info[11] &lt;&lt; 8) 找到的,其中 info 是标头。

    #include <cstdio>
    #include <cstdlib>
    #include <cstdint>
    #include <vector>
    
    typedef struct bitmap
    {
        unsigned short bpp;
        unsigned int width, height;
        std::vector<unsigned char> pixels;
    } bitmap;
    
    bool LoadBmp(const char *filepath, bitmap *bmp)
    {
        FILE *f = fopen(filepath, "rb");
    
        if (f)
        {
            bmp->bpp = 0;
            bmp->width = 0;
            bmp->height = 0;
            bmp->pixels.clear();
    
            unsigned char info[54] = {0};
            fread(info, sizeof(unsigned char), 54, f);
    
            bmp->width = info[18] + (info[19] << 8); //Width
            bmp->height = info[22] + (info[23] << 8); //Height
    
    
            bmp->pixels.resize(((((bmp->width * bmp->height) + 31) & ~31) / 8) * bmp->height); //Size of the pixels in the bitmap.
    
            fseek(f, info[10] + (info[11] << 8), SEEK_SET); //Seek to Pixel Offset.
    
            fread(&bmp->pixels[0], sizeof(unsigned char), bmp->pixels.size(), f); //Read the pixels.
            fclose(f);
    
            //Do whatever with pixels.. Flip them.. Swap BGR to RGB, etc..
            return true;
        }
    
        return false;
    }
    

    最后,您的黑色像素低于白色像素,因为位图是颠倒存储的。您必须自己翻转或自下而上阅读。

    【讨论】:

    • 因为 info[11] 是 char 类型,因此包含 8 位,移动 8 位(
    猜你喜欢
    • 2014-12-25
    • 1970-01-01
    • 2013-08-12
    • 2012-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多