【问题标题】:How can you stitch multiple heightmaps together to remove seams?如何将多个高度图拼接在一起以消除接缝?
【发布时间】:2013-05-08 05:13:06
【问题描述】:

我正在尝试编写一个算法(在 c# 中),它将两个或多个不相关的高度图拼接在一起,因此地图之间没有可见的接缝。基本上我想模仿这个页面上的功能: http://www.bundysoft.com/wiki/doku.php?id=tutorials:l3dt:stitching_heightmaps

(你可以看看图片来了解我在说什么)

我还希望能够获取单个高度图并对其进行更改,使其可以平铺,以创建一个无尽的世界(所有这些都是为了在 Unity3d 中使用)。但是,如果我可以将多个高度图拼接在一起,我应该能够轻松地修改算法以作用于单个高度图,所以我不担心这部分。

任何形式的指导都将不胜感激,因为我已经搜索并搜索了没有成功的解决方案。只需向正确的方向轻推,将不胜感激!我知道许多图像处理技术可以应用于高度图,但一直无法找到产生我正在寻找的结果的图像处理算法。例如,image stitching 似乎只适用于具有重叠视野的图像,而对于不相关的高度图则不然。

以某种方式使用 FFT 低通滤波器会起作用,还是仅在生成单个可平铺高度图时有用?

因为该算法将在 Unit3d 中使用,所以任何 c# 代码都必须限于 .Net 3.5,因为我相信这是 Unity 使用的最新版本。 感谢您的帮助!

【问题讨论】:

    标签: c# tile image-stitching heightmap


    【解决方案1】:

    好的,看来我之前尝试解决这个问题的方法是正确的。我最初尝试将高度图拼接在一起涉及高度图上每个点的以下步骤:

    1) 求高度图上的点与其相对点之间的平均值。对立点只是在 x 轴(如果缝合水平边缘)或 z 轴(对于垂直边缘)上反射的第一个点。

    2) 使用以下公式找到该点的新高度:

    newHeight = oldHeight + (average - oldHeight)*((maxDistance-distance)/maxDistance);
    

    其中距离是从高度图上的点到最近的水平或垂直边缘的距离(取决于您要缝合的边缘)。任何距离小于 maxDistance 的点(这是一个影响地形改变程度的可调整值)都会根据此公式进行调整。

    这是旧公式,虽然它在大多数地形上产生了非常好的结果,但它在改变高度图点的区域和未改变高度图点的区域之间的区域中创建了明显的线。我几乎立即意识到这是因为与未改变区域相比,改变区域的坡度太陡峭,从而在两者之间形成了明显的对比。不幸的是,我以错误的方式解决了这个问题,寻找如何将对比区域模糊或平滑在一起以去除线条的解决方案。

    在平滑技术取得了很小的成功后,我决定尝试降低改变区域的坡度,希望它能够更好地与未改变区域的坡度融合。我很高兴地报告,这极大地改进了我的拼接算法,删除了上面报告的 99% 的行。

    旧公式的罪魁祸首是这部分:

    (maxDistance-distance)/maxDistance
    

    它根据点到最近边缘的距离线性产生一个介于 0 和 1 之间的值。随着高度图点和边缘之间的距离增加,高度图点将使用越来越少的平均值(如上定义),并且越来越接近其原始值。这种线性插值是导致斜率过大的原因,但幸运的是,我在 Unity API 的 Mathf 类中找到了一个内置方法,该方法允许进行二次(我相信是三次)插值。这是SmoothStep Method

    使用这种方法(我相信类似的方法可以在 Xna 框架中找到 here),在中距离中用于确定高度图值的平均值的变化变得非常严重,但这种严重性距离越接近 maxDistance 成指数地减小,创建一个不太严重的坡度,更好地与未改变区域的坡度融合。新的论坛看起来像这样:

    //Using Mathf - Unity only?
    float weight = Mathf.SmoothStep(1f, 0f, distance/maxDistance);
    
    //Using XNA
    float weight = MathHelper.SmoothStep(1f, 0f, distance/maxDistance);
    
    //If you can't use either of the two methods above
    float input = distance/maxDistance;
    float weight = 1f + (-1f)*(3f*(float)Math.Pow(input, 2f) - 2f*(float)Math.Pow(input, 3f));
    
    //Then calculate the new height using this weight
    newHeight = oldHeight + (average - oldHeight)*weight;
    

    可能有更好的插值方法可以产生更好的拼接。如果我找到这样的方法,我肯定会更新这个问题,所以其他任何想要进行高度图拼接的人都可以找到他们需要的信息。感谢 rincewound 通过线性插值走上正轨!

    【讨论】:

      【解决方案2】:

      对我来说,您发布的图像中所做的看起来很像简单的线性插值。 所以基本上:您拍摄两张图像(左,右)并定义一个拼接区域。对于线性插值,您可以获取左侧图像的最左侧像素(在拼接区域中)和右侧图像的最右侧像素(也在拼接区域中)。然后用插值填充中间的空间。

      以这个例子为例——我在这里用一行来展示这个想法:

      Left = [11,11,11,10,10,10,10]
      Right= [01,01,01,01,02,02,02]
      

      假设我们的重叠是 4 像素宽:

       Left = [11,11,11,10,10,10,10]
       Right=          [01,01,01,01,02,02,02]
                         ^  ^  ^  ^ overlap/stitiching region.
      

      左图最左边的值为 10 右图最右边的值为 1。 现在我们分两步在 10 和 1 之间进行线性插值,我们的新拼接区域如下所示

      stitch = [10, 07, 04, 01]
      

      我们最终得到以下缝合线:

      line = [11,11,11,10,07,04,01,02,02,02]
      

      如果您将其应用于两个完整的图像,您应该得到与您之前发布的结果相似的结果。

      【讨论】:

      • 好的,我明白你在说什么,但是你如何将它应用到高度图上呢?在您的示例中,生成的缝合线的长度为 10,但组合后,初始左右线的长度为 14。我需要生成与原始左/右线长度相同的新左/右线, 左行的最右边的值与右行的最左边的值匹配(如果它们不是相同的值,则高度图之间会有间隙)。
      • 感谢您的帮助,插值绝对是在正确的轨道上。我添加了一个答案,它产生了迄今为止最好的结果。是否可以将自己的答案标记为问题的答案,还是通常不赞成这样做?
      猜你喜欢
      • 2011-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多