【问题标题】:Finding least-energy path across an image在图像中寻找能量最小的路径
【发布时间】:2014-09-01 21:29:07
【问题描述】:

考虑一个包含灰度值的二维数组,如下图所示:

我想找到红点之间的最佳路径。如果您认为在海拔高度上明亮区域是“高”而黑暗区域是“低”,那么我想要一条沿着黑暗“山谷”的线从一个标记延伸到另一个标记。

我知道的两类算法是:

  1. 基于图像处理的“自下而上”操作(骨架化、分水岭、最终侵蚀等)
  2. 迭代最小化,“自上而下”操作(活动轮廓)。

我的问题是:

是否有任何典型且定义明确的操作或算法来解决这个问题,或者我应该根据上面提到的一些通用技术自己创建一个?

我会先尝试骨架化,但我不知道如何在灰度图像上执行它。而且,如果我要尝试一个主动轮廓,我想知道对于与所示图像相似的图像,什么是好的内部和外部能量函数(我怀疑图像梯度可以充当矢量场)。

Original data (CSV) here.

编辑:这是我在实现接缝雕刻算法(如 Wikipedia 中所述)和 Shoham 建议强制路径通过标记后的工作代码:

private double[] MinimumEnergyBetweenMarkers(double[,] array, Point upper, Point lower)
{
    int rows = array.GetLength(0);
    int cols = array.GetLength(1);

    // Points might come in another format, whatever
    int iupper = upper.Y;
    int jupper = upper.X;

    int ilower = lower.Y;
    int jlower = lower.X;            


    // First, scan down from upper marker,
    // storing temp results in a second array
     double[,] new_array = new double[rows, cols];
    FillArrayWithNans(ref new_array, double.NaN);
    new_array[iupper, jupper] = array[iupper, jupper];
    int i = iupper;

    while (i++ < ilower + 1)
    {
        for (int j = 1; j < cols - 1; j++)
        {
            var valid_neighbors = new List<double>()
            {
                new_array[i-1, j-1],
                new_array[i-1, j],
                new_array[i-1, j+1]
            }.Where(v => !Double.IsNaN(v));
            if (valid_neighbors.Count() > 0)
                new_array[i,j] = array[i,j] + valid_neighbors.Min();
        }
    }

    double[] shortest_path = new double[rows];
    FillArrayWithNans(ref shortest_path, double.Nan)

    shortest_path[ilower] = jlower;
    i = ilower;
    int jj = jlower;

    // offsets might be wider to allow for "steeper" paths
    var offsets = new int[]{-1,0,1};

    while (i-- > iupper)
    {
        double minimum = double.MaxValue;
        int jtemp = jj;
        foreach (int offset in offsets)
        {
            if (jj > 0 && jj < cols - 1)
            {
                double candidate = array[i-1, jj+offset];
                if (candidate < minimum)
                {
                    minimum = candidate;
                    jtemp = jj+offset;
                }
            }
        }
        jj = jtemp;
        shortest_path[i] = jj;
    }

    return shortest_path;
}

【问题讨论】:

    标签: image-processing mathematical-optimization minimization watershed


    【解决方案1】:

    使用动态编程。这种方法用于边缘的Seam Carving。您需要在原始数据上使用它,但要确保暗区的值较低,并且您只计算两个红点之间可能的路径。

    根据您的目的调整接缝雕刻:

    1. 每个像素都有一个数字来表示能量。在你的情况下,黑暗或光明。

    2. 在上方红点下方开始一行。

    3. 向下扫描。对于每个像素,他的能量加上他上方三个像素的最小能量总和(保存这个值)。您还需要保存他的父亲是谁(当前像素上方能量最小的像素)。

    4. 需要对算法进行的另一项更改是,您必须标记源自上方第一个红点的像素(也标记上方的点)并始终优先考虑已标记的像素。

    如果您遵循所有这些步骤,则下方的红点像素将包含到上方点的最少能量路径。

    备注:这可以提高性能。

    【讨论】:

    • 我编辑了您的帖子,以便语法更好地流畅并修复各种拼写错误。希望你不要介意。
    • 非常有教育意义的回答,非常感谢。我现在正在研究它,得到了一些论文,并希望(实际上需要)今天得到一些结果。我会发布反馈。
    • @heltonbiker - 如果这回答了您的问题,请接受答案。
    • @shoham - 耐心点。他将尝试他在这里学到的一切。一旦他找出最适合他的解决方案,他就会接受对他最有帮助的答案。
    • 前面的评论是对的。 shoham 和 rayryeng 的回答有很多共同点,老实说,我都接受。但我目前仍在努力解决所有问题,到目前为止取得了可喜的成果。
    【解决方案2】:

    试试看Seam Carving:

    Seam Carving 主要用于图像的 Context-Aware Resizing,但该算法背后的基础是找到从图像顶部到底部的最佳路径。实际上,这几乎可以计算出您当前正在搜索的从图像顶部到底部的最小能量路径。我们可以对其进行修改,使图像顶部对应顶部红色标记,图像底部对应底部红色标记。


    还可以尝试看看智能剪刀:

    智能剪刀已广泛用于特效和图像编辑。目标是给定一个源点和一个目标点,这会找到两点之间的最佳路径。这也与 Dijkstra 的最短路径算法非常相似,但专门针对图像分割进行了修改。


    不确定这些方法是否适用于您的应用程序,但这是我能想到的两种最佳方法,它们可以找到两个标记之间的最佳路径。

    【讨论】:

    • 那些肯定是优秀的竞争者。我会尽快看一下,然后回来发布一些反馈。非常感谢您!
    • @heltonbiker - 酷!让我知道事情的后续。祝你好运!
    • 接受了这个,因为它包含我使用的方法(接缝雕刻)以及另一种方法,以及非常有用的链接。
    • @heltonbiker - 酷。谢谢!
    【解决方案3】:

    缝雕看起来是一种明智的做法。 如果您有兴趣,我编写了一个专注于速度的接缝雕刻 Java 实现(这意味着与最佳解决方案相比,我采取了一些捷径)。我在亮度的近似值上使用 Sobel 滤波器作为成本图(由于滤波器速度)。您可以使用不同的过滤器。然后我使用动态编程根据成本图找到最佳接缝。它在我的计算机上几乎实时运行以获取“合理”尺寸的图像(例如,宽度和长度小于 1024)。这显然取决于要计算的接缝数量。 这里:https://github.com/flanglet/kanzi-graphic/blob/master/java/src/kanzi/filter/seam/ContextResizer.java

    【讨论】:

    • 链接好像坏了。 :-(
    • 链接已修复
    【解决方案4】:

    Fast Marching 算法通常用于医学图像中的血管分割(类似于 Dijkstra 的最短路径)。

    【讨论】:

      猜你喜欢
      • 2012-11-26
      • 1970-01-01
      • 2021-12-18
      • 1970-01-01
      • 2021-07-05
      • 2012-10-24
      • 2013-09-04
      • 2012-04-08
      • 1970-01-01
      相关资源
      最近更新 更多