2

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

在此处输入图像描述

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

我知道的两类算法是:

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

我的问题是:

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

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

原始数据 (CSV) 在这里

编辑:这是我在实现接缝雕刻算法(如维基百科中所述)和 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;
}
4

4 回答 4

3

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

根据您的目的调整缝雕:

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

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

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

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

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

备注:这可以提高性能。

于 2014-09-02T04:19:28.870 回答
2

尝试看一下接缝雕刻:

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


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

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


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

于 2014-09-02T02:51:13.777 回答
2

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

于 2014-11-03T01:55:59.617 回答
1

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

于 2014-09-01T21:40:58.350 回答