寻找图像中的最小能量路径

时间:2014-09-01 21:29:08

标签: image-processing mathematical-optimization minimization watershed

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

enter image description here

我想找到红点之间的最佳路径。如果你认为明亮的区域是"高"和黑暗的区域是"低"在一个高程意义上,我想要一条沿着黑暗"山谷"从一个标记跑到另一个标记。

我所知道的两类算法是:

  1. 基于图像处理,"自下而上"操作(骨架化,分水岭,极端侵蚀等)
  2. 迭代最小化,"自上而下"操作(活动轮廓)。
  3. 我的问题是:

      

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

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

    Original data (CSV) here

    编辑:这是我实施接缝雕刻算法(如维基百科中所述)和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 个答案:

答案 0 :(得分:3)

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

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

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

  2. 在上面的红点下面开一行。

  3. 向下扫描。对于每个像素,他的能量加上来自他上方三个像素的最小能量总和(保存该值)。你还需要保存谁是他的父亲(能量比目前像素高的像素)。

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

  5. 如果您执行所有这些步骤,则较低的红点像素将包含到上部点的最少能量路径。

    备注:这可以提高性能。

答案 1 :(得分:2)

试着看看Seam Carving:

Seam Carving主要用于上下文感知图像的大小调整,但算法背后的基础是找到从图像顶部到底部的最佳路径。实际上,这几乎计算出当前搜索时从图像顶部到底部的最小能量路径。我们可以修改它,使图像顶部对应顶部红色标记,图像底部对应底部红色标记。


还可以试试看智能剪刀:

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


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

答案 2 :(得分:1)

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

答案 3 :(得分:1)

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