考虑包含灰度值的二维数组,如下图所示:
我想找到红点之间的最佳路径。如果你认为明亮的区域是"高"和黑暗的区域是"低"在一个高程意义上,我想要一条沿着黑暗"山谷"从一个标记跑到另一个标记。
我所知道的两类算法是:
我的问题是:
是否有任何典型的,定义明确的操作或算法来解决这个问题,或者我应该从上面提到的一些通用技术中自己创建一个?
我会首先尝试骨架化,但我不知道如何在灰度图像上执行它。而且,如果我要尝试一个活动的轮廓,我想知道对于类似于所示图像的图像,内部和外部能量函数会是多么好(我怀疑图像渐变可以作为矢量场)。
编辑:这是我实施接缝雕刻算法(如维基百科中所述)和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;
}
答案 0 :(得分:3)
使用动态编程。 Seam Carving在边缘使用此方法。您需要在原始数据上使用它,但要确保暗区域的值较低,并且您只计算两个红点之间可能存在的路径。
根据您的目的调整接缝雕刻:
每个像素都有一个数字来表示能量。在你的情况下,黑暗或亮度。
在上面的红点下面开一行。
向下扫描。对于每个像素,他的能量加上来自他上方三个像素的最小能量总和(保存该值)。你还需要保存谁是他的父亲(能量比目前像素高的像素)。
需要对算法进行的另一项更改是,您必须标记源自上部第一个红点的像素(也标记上部点),并始终优先考虑标记的像素。 / p>
如果您执行所有这些步骤,则较低的红点像素将包含到上部点的最少能量路径。
备注:这可以提高性能。
答案 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