How to Get Length of Thick Lines from Image?

I would like to calculate the length of individual sections of the road.
Originally, I have a road map image. Using image segmentation, I have isolated sections of the road as seen below.
I would like to calculate the length of each individual white section in pixels (i.e. the length of each section of the road including bends). What's the best way to do this?
I've tried using 'bwlabel' & 'regionprops' to split it into sections and use 'MajorAxisLength' (explained here https://uk.mathworks.com/matlabcentral/fileexchange/25157-image-segmentation-tutorial). As the original image isn't perfect and has some letters overlaid onto it, this results in too many sections around the letters (on the right) seen here
Next, I tried to use 'houghlines' to find the length (explained here https://uk.mathworks.com/help/images/ref/houghlines.html). As the lines are thick, it doesn't seem to work well. Using the 'edge' function first, I get this
Without the 'edge' function, I get this
In both cases above, some white straight sections are not detected.
Ideally, I would like to get the result shown below, and subsequently measure the purple lines in pixels (Sorry, drawn in Paint, lines should be straight). What's the best way to do this?

2 Commenti

jonas
jonas il 3 Ago 2018
Modificato: jonas il 3 Ago 2018
Hough transform might be useful here
Yep, I did try using the 'houghlines' function as mentioned above. But I think due to the thickness of the lines, it isn't giving a good result as seen in the pictures.

Accedi per commentare.

 Risposta accettata

Use bwdist() to compute the Euclidean Distance Transform of each blob. Then take a histogram of that. The mode will be the most common radius so multiply that by 2 to get the mean blob width. Try it. If you need help, ask. Here's a start:
[labeledImage, numberOfBlobs] = bwlabel(binaryImage);
for k = 1 : numberOfBlobs
thisBlob = ismember(labeledImage, k);
edtImage = bwdist(thisBlob);
[counts, binValues] = histcounts(edtImage);
modeRadius(k) = .....
meanWidth(k) = 2 * modeRadius
etc.....
end
I'm confident in your ability to finish this code. Try it, but if you can't do it, ask for more help.

13 Commenti

Philip So
Philip So il 6 Ago 2018
Modificato: Philip So il 6 Ago 2018
Thanks for your reply. There's one line I don't understand - "The mode will be the most common radius so multiply that by 2 to get the mean blob width."
I'm not sure how the above statement relates to the blob length.
For example, let us examine blob no. 3, shown below. On the X&Y axis, it's approx. 55 pixels wide and 32 pixels high, so from Pythagoras Theorem, the length of the blob (length of the road) is 64 pixels.
So, from bwdist, I get the contour, shown below
Next, I use histcounts and find the mode. It turns out that the mode is 792 counts at the binValues of 97 to 98. Here's the histogram
I think the histogram makes sense, as values above 97 to 98 are (partially) cropped out of the image, so it peaks there.
But I'm not sure how 97 to 98 relates to the desired 64 pixels.
It looks like you probably need to invert the binary image before taking the distance transform.
Philip So
Philip So il 6 Ago 2018
Modificato: Philip So il 6 Ago 2018
I did think of that not long after my original reply. But it only gives the width of the line, not the length?
From the image above, I can see the max radius from centre (yellow lines) to the edge is about 4 pixels. So the width would be 4x2 = 8 pixels. But how would I calculate the length of the blob (the road)?
One method I thought of is to use the area.
  1. Count the number of non-zero values in the image above. This would give the area.
  2. Divide by the width of 8 pixels. This would give the length.
But I would like to avoid using the area, as at the bottom right of the image above, there were originally some letters overlaid on the road (see image of blob 3 in earlier post). So there are some missing parts to the rectangle, and the area of the road is understated.
Would there be a direct method to measure the length of the blob?
And thanks for your help so far. I think we're really close to the solution.
Call bwconvhull() to take the convex hull of the lines - won't work on the L-shapes though. Then use regionprops to get the area and divide by the width to get the average length. Why do you need those values?
Thanks for your reply. The bwconvhull() is an excellent idea. I will test it in a moment.
To answer your 2nd question, I am analyzing vehicle speed data from Google Maps. Eventually, I would like to get a speed vs. distance plot, so I'm calculating the length of the road.
Regarding the L-shapes, is there some technique to split it into two rectangle bars?
In addition, the roads you see in the earlier images are straight and perpendicular to each other. In future, I need to analyse curved roads. Is there any way to get the length of a curved road?
For a curved road you get the Euclidean distance transform of it. Then also get the skeleton of it. Multiply the skeleton by the EDT to get the half widths along the spine of the curve. Take the mean of them if you want.
Philip So
Philip So il 6 Ago 2018
Modificato: Philip So il 6 Ago 2018
Could you explain in more detail what you mean by 'skeleton' of Euclidean distance, and 'half widths along the spine of the curve'? Maybe you could say what the relevant Matlab commands are and I will look them up.
BTW, the bwconvhull() works perfectly for the non-L shapes, thanks.
I have another related question
In the above image, using the bwlabel command, Matlab splits it into 4 blobs, because the 4 white parts are not connected. As a human, we can see it's one rectangular block with some letters overlaid.
Is there some way to connect (or fill-in) the 4 blobs, for example, if 2 white parts are less than 2 pixels apart, then consider them as 1 blob.
I can't use the convex hull method as that method requires me to manually identify which 4 blobs should be interconnected, then code for that specific situation. Ideally, it should be automated.
The skeleton is basically the centerline of the shape. You use bwmorph() to get it.
The EDT is the distance from any pixel to the edge of the blob. So from the centerline (skeleton) to the outer edge would be only half the width, not the full width of the thick line.
You can try to use imclose() to merge nearby blobs.
Philip So
Philip So il 7 Ago 2018
Modificato: Philip So il 7 Ago 2018
Thanks for your reply. I will check them out.
I've thought hard about curved roads or non-straight road sections. Now, it's become a mathematical question. Could you help me check if this generalization is true?
From the techniques discussed above, we can find the area and width of road easily. Just not the length. My generalization is that we can find the length of the road by dividing area by width for any road shape.
A straight road is obvious. It's rectangular, so the length is area/width.
For a curved road attached (semi-circle), it works as well.
Take the area (outer - inner: (pi*R1^2 - pi*R2^2)/2 <--semi-circle), and divide by road width (R1 - R2).
Compare with the length of arc (s = r*theta = ((R1-R2)/2 + R2)*pi). It will give the same answer of road length.
So I'm thinking can "Length = Area/Width" be generalized to any random road shape?
You're overthinking this. Most likely you don't need that kind of mathematical precision. Remember, it's not mathematical theory. There is the practical aspect that your image is digitized so it's built of a bunch of rectangular blocks (pixels). So you should be more practical and just use the length = area/width estimate. It should be plenty good enough for whatever you want to do with it. No sense on getting anything more accurate. To further make the point, what if the blob is not stick-like but a random irregularly-shaped splat. What is the average width? It has lots of widths, and it actually depends on where you define the widths to be taken from.
Alright, thank you for your answer. I'll just stick to "Length = Area/Width".
I have another question on sorting binary images. As you can see from the previous diagrams, they are sections of road, with some parts missings.
The parts (roads) missing are on another binary image, and I would like to merge the two images.
Would there be a way to sort the resulting binary images?
For example, the first labelled binary image of 2 road sections would be
[ 0 0 0 0 0 2 2 2 0 0
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 1 1 0 0 ]
The second binary image of another 2 road sections would be
[ 0 0 0 0 9 0 0 0 0 0
0 0 0 0 9 0 0 0 0 0
0 0 0 0 9 0 0 0 8 8 ]
I would merge the images by summing the 2 matrices up (joining the different sections of the roads)
[ 0 0 0 0 9 2 2 2 0 0
0 0 0 0 9 0 0 0 0 0
0 0 0 0 9 1 1 1 8 8 ]
Then I would like to sort it, so the label number increases as we travel along the road, so it would look like this.
[ 0 0 0 0 2 1 1 1 0 0
0 0 0 0 2 0 0 0 0 0
0 0 0 0 2 3 3 3 4 4 ]
Would there be any way to sort the labels so I can get the above results? Note: the above matrices are simplified matrices. The actual binary matrices will look like the road pictures posted earlier.
Not sure. I think you may have to merge the labeled images and then relabel. Then figure out what component blobs went into making it. Then relabel and add them back in while not affecting any others. Seems like it would be a pain. I'd avoid it unless it's absolutely necessary. And I don't have code to do it so you're on your own.
Thanks for all your help. Yes, it's going to be a pain, so I'm thinking of another method at the moment.

Accedi per commentare.

Più risposte (0)

Categorie

Prodotti

Release

R2015b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by