【问题标题】:Writing to .BMP - distorted image写入 .BMP - 扭曲的图像
【发布时间】:2013-12-07 17:00:12
【问题描述】:

我想将法线贴图写入 .bmp 文件,所以我先实现了一个简单的 .bmp 写入器:

void BITMAPLOADER::writeHeader(std::ofstream& out, int width, int height)
{
    BITMAPFILEHEADER tWBFH;
    tWBFH.bfType = 0x4d42;
    tWBFH.bfSize = 14 + 40 + (width*height*3);
    tWBFH.bfReserved1 = 0;
    tWBFH.bfReserved2 = 0;
    tWBFH.bfOffBits = 14 + 40;

    BITMAPINFOHEADER tW2BH;
    memset(&tW2BH,0,40);
    tW2BH.biSize = 40;
    tW2BH.biWidth = width;
    tW2BH.biHeight = height;
    tW2BH.biPlanes = 1;
    tW2BH.biBitCount = 24;
    tW2BH.biCompression = 0;

    out.write((char*)(&tWBFH),14);
    out.write((char*)(&tW2BH),40);
}

bool TERRAINLOADER::makeNormalmap(unsigned int width, unsigned int height)
{
    std::ofstream file;
    file.open("terrainnormal.bmp");

    if(!file)
    {
        file.close();
        return false;
    }

    bitmaploader.writeHeader(file,width,height);


    for(int y = 0; y < height; y++)
    {
        for(int x = 0; x < width; x++)
        {
            file << static_cast<unsigned char>(255*x/height);    //(unsigned char)((getHeight(float(x)/float(width),float(y)/float(height))));
            file << static_cast<unsigned char>(0);               //(unsigned char)((getHeight(float(x)/float(width),float(y)/float(height))));
            file << static_cast<unsigned char>(0);               //(unsigned char)((getHeight(float(x)/float(width),float(y)/float(height))));
        };
    };

    file.close();

    return true;
};

writeHeader(...) 函数来自 SO,来自已解决的工作帖子。 (忘记名字了)

getHeight(...) 使用双三次插值,因此我可以将其写入大分辨率图像,并且保持平滑。它还将用于碰撞检测,现在用作我的剪辑贴图的 LOD 因子。

现在的问题是这会输出失真的图像。图片会说明我的想法:

预期/扭曲的结果:

  • 对于高度图:我有描述网格的函数:getHeight(x,z)。它会返回正确的结果,因为我也使用着色器(通过将高度作为顶点属性发送)对其进行了测试。从网上下载的图片:

  • 并且将 y(x,z) 函数值写入 .BMP:(代码中被注释掉的部分):

  • 用一个简单的函数:file &lt;&lt; static_cast&lt;unsigned char&gt;(255*(float)x/height)

这应该是从黑色到白色到右侧的简单混合。

我使用了 256 x 256 的图像大小,因为我读过它应该是 4 的倍数。我可以使用库,但我想在没有库的情况下解决这个问题。那么,是什么造成了这种失真呢?

编辑: 在最后一张图片上,一些线条也是彩色的,但它们不应该是彩色的。这篇文章很相似,但我的高度图并没有像这篇文章那样线性扭曲:Image Distortion with Lock Bits

编辑: 另一个奇怪的问题是,当我没有使所有颜色都相同时,它的颜色也会失真。例如,仅将 RED 设置为高度,而将 G 和 B 保留为 0,它不仅变成了 RED,而且变成了嘈杂的彩色高度图。

编辑 /cmets/ 如果我理解正确,那就是标题的大小,然后是我的像素数据。现在在像素数据之前必须有 4 * n 个字节。因此,填充意味着在标题之后我放置了更多填充该位置的数据。

例如假设(我会查找热点以准确获取它)我的标题是 55 个字节,那么我应该再添加 1 个字节,因为 55+1 = 56 和 4|56。

所以

file << static_cast<unsigned char>('a');

for(int y = 1; y <= width; y++)
{
    for(int x = 1; x <= height; x++)
    {
        file << static_cast<unsigned char>(x);
        file << static_cast<unsigned char>(x);
        file << static_cast<unsigned char>(x);
    };
};

应该是正确的。

但我意识到了真正的问题(正如 Jigsore 评论的那样)。当我从int 转换为char 时,似乎1 位数字变为1 字节,2 位数字变为2,3 位数字变为3 字节将高度限制为 3 位数效果很好,但图像有点白,因为“最暗”颜色变为 (100,100,100) 而不是 (0,0,0)。此外,这也是不规则失真的原因,因为它取决于一行中有多少“山”或“山”。我该如何解决这个问题,我希望是最后一个问题?我不想将图像压缩到 100-256 范围内。;)

【问题讨论】:

  • 几年前我也遇到过类似的问题。您需要将行对齐到某个边界,但是我不记得确切的数字。
  • 我无法从您的图像中确切看出哪里出了问题,但绝对可以肯定这是一个“步幅”问题。也就是说,您存储的一行像素与下一行之间的距离与绘图软件的“预期”不匹配。
  • 我认为位图文件需要将每一行对齐到 32 位边界。对于 24 位图像,您需要在某些情况下进行填充。例如,宽度为 39 为您提供 117 字节行,这不是 32 位对齐的。对于这些情况,您需要进行填充以获得正确的对齐方式。
  • file 以文本模式打开,因此如果您使用 Windows (0D 0A),file &lt;&lt; (unsigned char)10; 实际上会向您的文件写入 2 个字节。 BMP 图像中的每行字节数始终是4的倍数(因此可能需要填充字节)。
  • 图像的每个扫描线必须从 4 的倍数的偏移量开始。您通过使用 256x256 的位图部分避免了该问题。但是您忽略的是在 x4 的偏移量处开始。这通常由颜色表自动处理,它具有将对齐到 4 的条目。但是您没有使用 BITMAPINFO,因此无法获得正确的标题大小的帮助。

标签: c++ image bitmap


【解决方案1】:

以二进制模式打开文件。

在 Windows 下,如果您以默认文本模式打开文件,它会在每个写入的 0x0a(换行符)之后写入一个额外的 0x0d(回车)字符。第一次发生这种情况时,它将改变以下像素的颜色,因为 RGB 顺序不对齐。发生 3 次后,您将偏离一个完整像素。

【讨论】:

  • @PnDs,抱歉 - 我很着急,否则我会提供更多详细信息。不过我意识到了这个问题,很高兴它有帮助!
  • 更详细一点?
  • @luckydonald 如你所愿。
猜你喜欢
  • 2021-04-26
  • 1970-01-01
  • 1970-01-01
  • 2014-08-13
  • 2016-03-18
  • 2016-01-02
  • 2017-02-12
  • 2013-01-12
  • 1970-01-01
相关资源
最近更新 更多