2

Consider a bidimensional array containing grayscale values, represented as the image below:

enter image description here

I want to find best path between the red dots. If you consider bright areas to be "high" and dark areas to be "low" in an elevation sense, then I want a line running along the dark "valley" running from one marker to another.

Two categories of algorithms that I know about are:

  1. Image-processing-based, "bottom up" operations (skeletonization, watershed, ultimate erosion, etc.)
  2. Iterative minimization, "top down" operations (active contours).

My question is:

Is there any typical and well-defined operation or algorithm to solve this problem, or should I create one myself from some of the generic techniques mentioned above?

I would try skeletonization first, but I don't know how to perform it upon a grayscale image. And, if I were to try an active contour, I wonder what would be good internal and external energy functions for images similar to the one shown (I suspect image gradient could act as a vector field).

Original data (CSV) here.

EDIT: this is my working code after implementing seam carving algorithm (as described in Wikipedia) and Shoham suggestions to force the path through the markers:

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;
}
heltonbiker
  • 26,657
  • 28
  • 137
  • 252

4 Answers4

3

Use dynamic programming. This method is used by Seam Carving on edges. You need to use it on the original data, but make sure that dark areas are given a low value and that you are calculating only the paths that are possible between the two red points.

Seam carving adjusted for your purpose:

  1. Each pixel is given a number to represent the energy. In your case, darkness or brightness.

  2. Start one row under the upper red dot.

  3. Scan downwards. For each pixel sum his energy plus the minimum sum of energy from the three pixels above him (save this value). You also need to save who was his father (the pixel that had the minimum energy above the current pixel).

  4. Another change that needs to be done to the algorithm is that you you must flag the pixels that originated in the upper first red dot (flag the upper dot also) and always give a priority to a flagged pixel.

If you follow all of this steps, the lower red dot pixel will contain the least energy route to the upper dot.

Remark: This can be performance improved.

rayryeng
  • 102,964
  • 22
  • 184
  • 193
shoham
  • 293
  • 1
  • 9
  • I edited your post so that the grammar flows better as well as fixing various spelling mistakes. Hope you don't mind. – rayryeng Sep 02 '14 at 04:25
  • Very pedagogic answer, thanks very much. I am working on it right now, got some papers, and hope (actually need) to get some result today. I'll post feedback. – heltonbiker Sep 02 '14 at 13:17
  • @heltonbiker - if this answers your question, please accept the answer. – shoham Sep 02 '14 at 14:52
  • @shoham - Be patient. He's going to try out everything he has learned here. Once he figures out which solution best works for him, he will accept whichever answer helped him the most. – rayryeng Sep 02 '14 at 16:06
  • 1
    The previous comment is right. The answers from shoham and rayryeng have a lot in common, to be honest I would accept both. But I am still currently working everything out, with promising results so far. – heltonbiker Sep 02 '14 at 17:12
  • @rayryeng I changed the accepted answer to this one, because it was necessary to solve my actual problem from beginning to end (including the marker part). The algorithm is almost exactely the one proposed in this answer. – heltonbiker Sep 16 '14 at 19:22
2

Try taking a look at Seam Carving:

Seam Carving is mostly used in Context-Aware Resizing of images, but the basis behind the algorithm is to find an optimal path from the top of the image to the bottom. Actually, this pretty much calculates the least energy path from the top of the image to the bottom as you are currently searching for. We can modify this so that the top of the image corresponds to the top red marker and the bottom of the image corresponds to the bottom red marker.


Also try taking a look at Intelligent Scissors:

Intelligent Scissors has been used widely in special effects and image editing. The goal is given a source point and a target point, this finds the most optimal path between the two points. This is also very similar to Dijkstra's shortest path algorithm but modified specifically for image segmentation.


Not sure if any of these methods will work for your application, but these are the two best methods that I can think of which find the most optimal path between two markers.

rayryeng
  • 102,964
  • 22
  • 184
  • 193
  • 1
    Those surely seem to be excellent contenders. I'm gonna take a look soon and come back to post some feedback. Thank you very much for now! – heltonbiker Sep 02 '14 at 04:12
  • 1
    Accepted this one because it contains the approach I used (seam carving) AND also an alternative approach, and also very useful links. – heltonbiker Sep 08 '14 at 15:15
2

Seam carving looks like a sensible approach. In case you are interested, I wrote a seam carving java implementation with a focus on speed (which means I take some shortcuts compared to the optimal solution). I use a Sobel filter on a approximation of the luminance as cost map (due to the filter speed). You may use a different filter. Then I use dynamic programming to find the best seams based on the cost map. It runs near real time on my computer for images of 'reasonable' sizes (EG. width and length less than 1024). It depends on the number of seams to compute obviously. Here: https://github.com/flanglet/kanzi-graphic/blob/master/java/src/kanzi/filter/seam/ContextResizer.java

flanglet
  • 564
  • 4
  • 11
1

The Fast Marching algorithm is typically used for vessel segmentation in medical imagery (it is similar to Dijkstra's shortest path).

user3146587
  • 4,250
  • 1
  • 16
  • 25