Message Boards Message Boards

Find Length and Width of a line from an image?

GROUPS:

Hello, How do I measure the average length and the average width of a line from an image? For example, the length and the width of the river in the attached picture.enter image description here

Thank you

POSTED BY: Subbarao Raikar
Answer
4 months ago

Interesting problem. It's a bit harder than just finding a line though. In order to measure the river in the picture, you first need to isolate it from the rest of the image. I had a hard time isolating just the river but was able to make some progress with the white canyon area that the river is inside of. The first step was performing some filtering to smooth out the small scale features like the elevation lines. One filter that seemed to do a reasonable job was the PeronaMalikFilter function.

im = Import["fsbdev3_066534.jpg"]
filtered = PeronaMalikFilter[im, 50]

Filtered image

This also has the effect of smoothing out the colors so, say, the river appears to be roughly the same color of blue everywhere. This makes it easier to select pixels that are in that feature. Using the pixel selector tool on the image (the little crosshairs tool), we can see that the river has an RGB value of around (201,228,247). We can find all of the pixels that are very close to that by applying the cosine distance to each pixel and that value, and taking only pixels that are below some threshold.

pixval = {201/255, 228/255, 247/255};
binary = ImageApply[If[CosineDistance[pixval, #] < 0.0005, 1, 0] &, 
  filtered]

Binarized image

Unfortunately, due to the parts of the original image where lines cross the river, the river doesn't show up as a single contiguous region. The usual thing I'd do to try to merge it into one shape would be to combine a dilation with an erosion operator. Unfortunately, in this case this will likely cause some of the tiny dots scattered around the image to merge together, possibly merging with the river. Instead, I select the 15 largest components (by counting by hand that there are around 15 big river components present).

SelectComponents[binary, "Count", -15]

River binarized

Finally, we can run two algorithms on it that I believe get you close to your goal. The first is the DistanceTransform. This assigns to each pixel the distance to the nearest boundary of the segment that it belongs to. I haven't tried it, but I believe that the distance at the center of the river can be interpreted as the distance to the edge of the river - so, you can think of it as half of the width of the river at that point. Averaging those values should give a rough guess for the average width of the river.

DistanceTransform[riverBinary] // ImageAdjust

Distance transform

The second algorithm is the skeleton transform. This will allow you to label the pixels that are in the middle of the river, which would let you figure out which pixels to select from the distance transform as representing the center of the river. Furthermore, the length of the skeleton should be a rough approximation of the length of the river itself.

SkeletonTransform[riverBinary]

Skeletonized

You likely would need to run Pruning on the skeleton to remove spurious little branches.

Another issue here is that there appear to be a few little lakes off to one corner. You likely want to mask those out, or use some heuristic on the location of the regions relative to each other to exclude those.

You could probably clean up what I did above to make the river connected with additional preprocessing. For example, you might be able to use an inpainting method to remove the breaks in the river.

POSTED BY: Matthew Sottile
Answer
4 months ago

Hi

A straight and faster way is use Google Maps and measure the distance. enter image description here

The distance is about 5.41 mile.

EDITED:

Borrowing code from user Matthew Sottile:

im = Import["test.jpg"];
filtered = CurvatureFlowFilter[im, 3, 10]
pixval = {201/255, 228/255, 247/255};
binary = ImageApply[If[CosineDistance[pixval, #] < 0.0005, 1, 0] &, filtered]
sol = SelectComponents[binary, "Count", -10]
Export["test1.jpg", sol]

enter image description here

Exporting to test1.jpg and open in PAINT program(Windows). Using inpainting method to remove the bad staff and import back to mathematica file test2.jpg.

 im2 = Import["test2.jpg"]

enter image description here

 distPIXEL = ComponentMeasurements[Thinning[im2, Infinity, 0.6],  "CaliperLength", #Count > 800 &][[1]] // Values
 (* 847.574 pixels distance*)

Assuming a distance scale 1 mile in to 200 pixels.

 distance = distPIXEL*1/200 miles

and answer is 4.23787 miles

Regards, Mariusz

POSTED BY: Mariusz Iwaniuk
Answer
4 months ago

Group Abstract Group Abstract