【问题标题】:Working with BMP - loading and saving使用 BMP - 加载和保存
【发布时间】:2016-02-14 09:45:08
【问题描述】:

与朋友一起,我们正在尝试编写应用程序来处理 BMP 文件,并且我们将使其尽可能简单,因为我们才刚刚开始学习 C 和 C++。使用新的实际线条尺寸复制效果很好,但现在我想添加灰度效果并遇到另一个问题:图片的右侧向左移动 - 查看图片。是什么导致了这个问题?

#include <iostream>
#include <fstream>
#include <stdio.h>
#include <unistd.h>


using namespace std;

void ReadBMP()
{
    FILE* f = fopen("test2.bmp", "rb");
    FILE* w = fopen("zapis.bmp", "wb");

    if(f == NULL)
        throw "Argument Exception";

    unsigned char info[54];
    fread(info, sizeof(unsigned char), 54, f);
    fwrite(info, sizeof(unsigned char), 54, w);

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

    cout << endl;
    cout << "Width: " << width << endl;
    cout << "Height: " << height << endl;



    int realwidth = 3*width+(4 - ((3*width)%4))%4;
    int volume = height * realwidth;
    unsigned char* data = new unsigned char[volume];

    fwrite(info, sizeof(unsigned char), 54, w);

    fread(data, sizeof(unsigned char), volume, f);
    unsigned char color = 0;

    for(int i = 0; i < volume; i+=3)
    {
        color = 0;
        color+=data[i]*0.114;
        color+=data[i+1]*0.587;
        color+=data[i+2]*0.299;
        data[i] = color;
        data[i+1] = color;
        data[i+2] = color;
    }


    fwrite(data, sizeof(unsigned char), volume, w);



    fclose(f);
    fclose(w);
    delete(data);
}

int main()
{
    ReadBMP();
    return 0;
}

Input image

Output image

【问题讨论】:

  • 要写入二进制数据使用write()&lt;&lt; 操作符会进行文本格式化,即使文件是以二进制模式打开的。
  • 改成fwrite,还是有些问题
  • 其实我的意思是ofstream::write(),但是好吧...
  • w.write(info, 54);错误:从 'unsigned char*' 到 'const char*' 的无效转换 [-fpermissive]|
  • 如果你在 Windows 下运行它,你还需要指定“二进制”模式来写入输出文件:fopen("zapis.bmp", "wb");。没有这个,每次你的程序尝试写入一个值为 0x0a 的八位字节时,文件中都会在它之前插入一个额外的 0x0d。

标签: c++ bmp


【解决方案1】:

您的图像数据大小公式是错误的。首先,您需要通过将宽度乘以每个像素的字节数(对于 24 位图像为 3),然后四舍五入到最接近的 4 倍数来找到间距。然后将间距乘以高度;

int byte_width = width * 3;
int pitch = byte_width + (4 - byte_width % 4) % 4;
int volume = pitch * height;

【讨论】:

  • @Ratka:您忽略了 BMP 文件格式中的许多其他信息。例如,标题和像素数据之间可能存在其他数据。您应该查看偏移量(4 字节整数,距离开头 10 字节),它告诉您从文件开头开始的像素数据的确切位置。
  • 我已经更改了公式,但遇到了另一个问题。
【解决方案2】:
unsigned char info[54];
fread(info, sizeof(unsigned char), 54, f);
// fwrite(info, sizeof(unsigned char), 54, w); --- comment this line !!!!!!! 

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

您不必要地将标题写入文件两次。

【讨论】:

    【解决方案3】:

    正如我在自己的代码(大约 20 年前编写)中看到的那样,图像的每一行都由 0 个或多个空字节补充以对齐起始字节。看来,您计算的对齐方式有误。

    在此处复制并粘贴:

    unsigned short paddingSize;
    unsigned short bitsPerLine = width * bitsPerPixel;
    
    if(1 == bitsPerPixel || 4 == bitsPerPixel)
    {
      if(bitsPerLine % 8)
        bitsPerLine += 8;
    
      paddingSize = (bitsPerLine/8) % 2;
    }
    else if(8 == bitsPerPixel)
      paddingSize = 0x0003 & ~((bitsPerLine/8) % 4 - 1);
    else
      paddingSize = (bitsPerLine/8) % 2;
    

    每行的实际大小是calculatedSize + paddingSize,其中calculatedSize 是行的确切大小(以字节为单位),即ceil(bitsPerLine/8)(bitsPerLine + 7)/8 ic C/C++。

    关于代码,我可以说的是它已经过调试并且可以工作。但我不记得为什么要在这里进行所有这些检查。

    【讨论】:

    • 我知道每一行最后都加了“0”字节,但我不明白你的意思——为什么这么复杂?
    • 因为没有简单的规则来检测填充。它因每个平面的不同位而不同。
    猜你喜欢
    • 1970-01-01
    • 2019-09-18
    • 2015-06-03
    • 2017-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-25
    相关资源
    最近更新 更多