GrabCut¶
import numpy as np
import panel as pn
import xarray as xr
import holoviews as hv
import geoviews as gv
import cartopy.crs as ccrs
from earthsim.annotators import PolyAnnotator, PolyExporter, paths_to_polys
from earthsim.grabcut import GrabCutPanel, SelectRegionPanel
gv.extension('bokeh')
The GrabCut algorithm provides a way to annotate an image using polygons or lines to demark the foreground and background. The algorithm estimates the color distribution of the target object and that of the background using a Gaussian mixture model. This is used to construct a Markov random field over the pixel labels, with an energy function that prefers connected regions having the same label, and running a graph cut based optimization to infer their values. This procedure is repeated until convergence, resulting in an image mask denoting the foreground and background.
In this example this algorithm is applied to map tiles to automatically extract a coast- and shoreline contour. First we specify a region to download the map tiles in using the SelectRegionPanel
, then we can declare the GrabCutPanel
to annotate the region and let the algorithm compute a contour.
select_region = SelectRegionPanel(hv.Bounds((-77.5, 34.45, -77.3, 34.75)), magnification=1)
pn.Row(select_region.param, select_region.view())
The toolbar in the plot on the left contains two polygon/polyline drawing tools to annotate the image with foreground and background regions respectively. To demonstrate this process in a static notebook there are already two polygons declared, one marking the sea as the foreground and one marking the land as the background.
background = np.array([
[-77.3777271 , 34.66037492], [-77.35987035, 34.62251189], [-77.34130751, 34.64016586],
[-77.35563287, 34.65360275], [-77.36083954, 34.66560481], [-77.3777271 , 34.66037492]
])
foreground = np.array([
[-77.46585666, 34.66965009], [-77.46451121, 34.62795592], [-77.43105867, 34.64501054],
[-77.41376085, 34.62573423], [-77.37886112,34.63780581], [-77.41283172, 34.6800562 ],
[-77.46585666, 34.66965009]
])
dashboard = GrabCutPanel(select_region.get_tiff(), fg_data=[foreground], bg_data=[background], minimum_size=500, tolerance=0.001)
pn.Row(dashboard.param, dashboard.view())
We can trigger an update in the extracted contour by pressing the Update contour
button and filter smaller contours using the Filter Contour
button. To speed up the calculation we can also downsample the image before applying the Grabcut algorithm.
Next we can further edit the extracted contour using the PolyAnnotator
class:
annotator = PolyAnnotator(polys=dashboard.result)
annotator.panel()
Once we are done we can convert the paths to a polygon and view the result in a separate cell:
gv.tile_sources.ESRI * paths_to_polys(annotator.poly_stream.element).options(width=500, height=500, color_index=None)