A Road Map for Rivers
The goal of this project was to identify rivers on maps and satellite images, and to calculate each river's graph structure.
We produced two functions: one will return a highlighting of all the water in a satellite image given a location entity, and the other will return the graph structure of the water in the related street map image of a given location entity. Both functions start by taking the related street map of a location entity and stripping the image of every color other than one light shade of blue. Let's use Seoul, South Korea as an example:
waterhighlight[entity_] :=
Module[{streetmap, color, r, bluemap, mask, satmap},
streetmap =
RemoveAlphaChannel@
FirstCase[
GeoGraphics[entity,
GeoBackground -> "StreetMap"], _Image, $Failed, Infinity];
satmap =
ImageResize[
RemoveAlphaChannel@
FirstCase[
GeoGraphics[entity,
GeoBackground -> "Satellite"], _Image, $Failed, Infinity],
ImageDimensions[streetmap]];
color =
Apply[List,
ColorConvert[Interpreter["Color"]["RGB 158 197 226"], "RGB"]];
r = If[ImageColorSpace[streetmap] != "RGB",
ColorConvert[streetmap, "RGB"], streetmap];
bluemap =
SetAlphaChannel[r, Binarize[r, (Norm[# - color] < .15) &]];
mask = Closing[#, 7] &@FillingTransform@Binarize@bluemap;
HighlightImage[
satmap, {EdgeForm[{Red, AbsoluteThickness[1.5]}],
FaceForm[{Red, Opacity[.2]}], mask}]
];
The first function takes the blue image and uses it as a mask to highlight the related satellite image.
watergraph[entity_] := Module[{streetmap, color, r, bluemap, mask},
streetmap =
RemoveAlphaChannel@
FirstCase[
GeoGraphics[entity,
GeoBackground -> "StreetMap"], _Image, $Failed, Infinity];
color =
Apply[List,
ColorConvert[Interpreter["Color"]["RGB 158 197 226"], "RGB"]];
r = If[ImageColorSpace[streetmap] != "RGB",
ColorConvert[streetmap, "RGB"], streetmap];
bluemap =
SetAlphaChannel[r, Binarize[r, (Norm[# - color] < .15) &]];
mask = Closing[#, 7] &@FillingTransform@Binarize@bluemap;
MorphologicalGraph@SkeletonTransform[mask]
];
The second function smooths out the blue image, takes it's skeleton, and uses that to calculate the graph structure. The end product really is a graph object, so one could play with it using Wolfram's graph functionality to get some interesting results.
Since one of the original goals of the project was to identify whether or not an image contained a river, we did make an attempt. We tried to first cut up the satellite images, binarize them, and then sort the tiny images based on the percentage of black and white pixels. We did this because in a large number of the initial images we saw, the water was usually much darker than the surrounding area. However as we looked at more test cases, this method of classifying images became useless. Many pictures require different binarize thresholds, some rivers are actually lighter than their surroundings, they may even be indistinguishable from the background, cloud coverage either blocks the water or can even create dark spots on its own via shadows, rivers may appear too thin to be recognized as such, and so on. As the list of issues grew, we decided to use the street maps due to their reliability. The street maps may have gaps in the water due to bridges, but this can be largely solved with basic image processing.
In the future, we would like to implement a random river generation function and find a way to accurately automate the classification of satellite images as: "river" or "not river" to make a training set. From there, you could probably train a neural network to identify rivers from satellite images.
Github