# How to place path lines on an image

I have a "sugarcane crop" image taken from above (with a Drone) which was pre-processed with the MorphologicalPerimeter and EdgeDetection functions of Mathematica, resulting in binary image with a series of features for which I'd like to find/place some path-lines with specific parameters. Here is the original image:

With some simple image processing functions within Mathematica, I generated the following picture:

EdgeDetect[MorphologicalPerimeter[
ColorSeparate[aImg][[1]], 0.35], 20]


The idea is to find all the free paths between the series of shapes such that they appear like "streets" for the shapes. In other words, the lines should only be drawn over the black background, never touching or crossing the white features (unless that is impossible). The spacing in between the lines should be almost constant, but may vary slightly from one street to the next. In this example they will be almost straight lines, but that is not necessarily the general case, for the "streets" may curve as well.

Here is a bad example of what a result would be. It is a bad example because the lines are evenly spaced, and therefore they end up going over the white features in the image, which shouldn't be allowed. But it illustrates what is to be achieved. Look at the left lower corner of the image to see what is expected for the solution.

To me, this looks like an AI / Computer Vision problem, but I am wondering whether someone would give me a clever Mathematica idea for a starting point to solve this problem in an efficient manner. Bear in mind that in reality I will be dealing with images a lot larger that this.

An alternative solution is to draw the path-lines exactly over the white features, maximizing the "crossings" over the features, creating a series of "sugar cane lines" with as evenly spacing as possible. Please, refer only to the left lower corner of the image below for an idea of the alternate solution.

These two problems seem to be dual, and either solution suffices. Any help will be greatly appreciated.

I am adding information here as I manage to get better preprocessed images. For instance,

EdgeDetect[GradientFilter[
MorphologicalPerimeter[ColorSeparate[aImg][[1]], 0.35], 1], 20]


gives me a very nice preprocessed image,

which I can then add to the original image just to show off the nice results:

(source: dccs.com.br)

However, none of this helps me in determining the best paths for the "street" lines, not to mention that they should not be considered to be straight lines. They just happen to be in this example.

Here goes an original image with the suggested curved path. Unfortunately, in this case, the sugarcane is still in its infancy, but it represents a real case problem which should be dealt with. Thanks.

• Have a look at ImageLines. GaborFilter might be useful too. Sep 13, 2016 at 13:53
• I had used ImageLines with a small degree of success. As for GaborFilter, I didn't get anything useful at all. Actually, I can improve on the "features" image a lot, with different techniques and functions. For instance, EdgeDetect[GradientFilter[MorphologicalPerimeter[ColorSeparate[aImg][[1]], 0.35], 1], 20] gives me a very nice preprocessed features image, but none of this helps in getting the actual "street" lines figured out. Sep 13, 2016 at 20:23
• There is maze-solving example in the documentation to WatershedComponents(see under Neat Examples). Maybe some similar technique can be applied here as well. Sep 14, 2016 at 7:18
• I would have tried to not only use the edge but Areas of the plants. Then I would try a 2D Fourier transform. That should give an pronounced Peak in some direction. From the Position of the Peak frequency and direction of the lines could be detemained. Unfortunately, I can not test it right. Sep 15, 2016 at 8:09
• In reality, none of the given answers were complete enough to the point that one can select either one of them as the correct and complete answer. As a matter of fact, both @C.E's and yode's answers address only the case where the lines are assumed to be straight, while the problem explicitly asks for the general case where the lines can be curved as well. Nonetheless, C.E.'s solution is more elaborated, and more promising of yielding a complete solution with further elaboration. Therefore I select C.E.'s answer as the correct one, so that he can collect all the bounty points. Thank you all. Sep 23, 2016 at 1:57

This is what a Fourier approach could look like.

img = Import["https://i.sstatic.net/KfoXJ.png"];
gray = ColorConvert[RemoveAlphaChannel[img], "Grayscale"];
data = ImageData[gray];

ft = Fourier[data];
ft = RotateLeft[ft, Floor[Dimensions[ft]/2]];

ft // Abs // Log // Rescale // Image


We're interested in the maximum of the Fourier transform, as this corresponds to the strongest frequency. However we're not interested in the frequency zero, so before we look for a maximum we blot out that frequency.

pos = Position[
Abs[ft],
Max[Abs[ft] (CenterArray[DiskMatrix[10], Dimensions[ft]] /. {0 -> 1, 1 -> 0})]
];

invft = InverseFourier[SparseArray[pos -> 1, Dimensions[ft]] ft];

invimg = invft // Abs // Rescale // Image;
ImageMultiply[invimg, img]


It doesn't look like there's one line per path, it's more like one line on each side of each path. In any case these lines encapsulate at least some information about the paths and the rows of plants.

The position of a maximum gives the direction of a line that runs orthogonally to the rows:

ArcTan[207, 254] // N


0.886999

• Very neat C.E., your approach will help me in getting to a complete solution of the problem. However, I wonder how this approach would behave on a plantation where the lines were not straight, but curved and still parallel. I might add a picture like that in here, with my attempts to replicating your calculation there. In any event, would you care to comment about such case? Thank you very much for you time. Sep 20, 2016 at 22:35
• C.E., would you mind to explain why you used RotateLeft just after computing the Fourier transform? I cannot understand the rationale behind this step. Thanks again. Sep 21, 2016 at 1:35
• @FACamargo Try not using RotateLeft and then plot the Fourier transform, you will see that the intensity around the zero frequency is spread out among several corners. The centering is usually done to make the visualization better and in this case it also make it easier to remove the intensity around the zero frequency. I can't say exactly how useful this will be for the curved case, probably not as useful, if you post that image I'll take a look at it but can't promise anything. Sep 21, 2016 at 5:28
• Would you mind to explain why we get black and white picture(invft) when we select that max frequency values??
– yode
Sep 21, 2016 at 5:47
• @yode Because the filtered Fourier transform only contains two frequencies and they have the same intensity. The background has zero intensity. Since there are only two intensities in invft Rescale makes on of the intensities 0 and the other 1. Sep 21, 2016 at 6:08

Too long illustration,sorry for comprehensive ability.:)Just provide a non-perfect solution:

pic = Import["http://www.dccs.com.br/images/dudu1.png"];
bin = MaxDetect[
ColorNegate@ColorSeparate[ColorConvert[pic, "CMYK"]][[2]], .1]


As you see,not all the path

lines = ImageLines[Dilation[bin, IdentityMatrix[15]] // Thinning];
HighlightImage[pic, {Thick, Line /@ lines}]


• Dear Yode, your approach in "treating" the image is very similar to the many attempts I made, before posting this question. Nonetheless, you were very successful at getting the lines over the sugarcane. The fact that there some misses is not very important, because one can always extrapolate the missing lines by looking at the most frequent distance between two lines (in a histogram). However, you approach will not work with a curved path, because of the function ImageLines, which only yields straight lines. Nonetheless, I am certain that your method will help me to improve on mine. Thank you. Sep 20, 2016 at 22:44
• For some reason the second image in this post will not upload to imgur. I will try again later. If it still doesn't work there may be a bug and I shall bring it to the attention of the SE developers. -- EDIT: it worked this time so I guess it was just a transient error. Mar 13, 2017 at 1:48

This is not a full answer, but may be a starting point for further optimization.

sep = DominantColors[img, Automatic, {"CoverageImage", "Color"}]


This gives two dominat brown colors of the soil.

opt = Pruning[Thinning[Erosion[sep[[1, 1]], 1]]]


This uses the main dominat color and applies Erosion, Thinning and Pruning to get like "walking paths" on the soil.

ImageMultiply[img, opt]


ImageAdd[img, opt]


You may use further optimization algorithms to remove small lines or play with the Pruning function etc.

• Dear AKM, I am very glad to learn of your approach. Although it did not solve the problem, it gave me ideas on how to improve on my own methods. Nonetheless, your words about optimization were really what I was looking for, because that far I had gone already. I was really looking for someone telling me how to draw the lines like C.E. and Yode did. In any event, I learned some more with you, and for that I thank you. Sep 20, 2016 at 22:50