Combining OpenCV and leaflet for simple web mapping

leaflet

I am always on the look out for an easy way to build simple web maps. Ideally I would like to perform OpenCV in the browser but I am not aware of that possibility at present. I work with computer vision and satellite imagery a great deal and have written several blogs on the subject. Have a look here for some examples.

I am going to use the Folium python library to send my computer vision results, via GDAL, to build a web map in Leaflet. As a by product I will also display the newly created web map in a Jupyter Notebook. This is very useful; as long as you have an internet connection you should be able to plot your work directly in Leaflet with a notebook. That should be a very powerful resource for presentations.

What is Folium?

Folium builds on the data wrangling strengths of the Python ecosystem and the mapping strengths of the Leaflet.js library. Manipulate your data in Python, then visualize it in a Leaflet map via Folium.

Installing folium

The two easiest ways are either:

pip install folium

or using Conda (which I am doing in this example).

conda install -c conda-forge folium

It is pretty straightforward with either of these two methods.

Getting an image

I am going to use one of the images I downloaded using this guide, on the western edge of Sardinia. The main requirement is to reproject your image to web mercator projection so that folium knows where to place it. You can do this in QGIS 3 using the ‘assign projection’ toolbox which can be found in the new processing toolbox or through Raster – Projections – Assign projections.

Perform a very simple edge detection

Perhaps the simplest thing to do is edge detection in OpenCV

import cv2
img = cv2.imread('...band2_mercator.jpg')
edges = cv2.Canny(img,50,50)
edges = cv2.imwrite('...edges.jpg', edges)

Read in the correctly projected image, apply a Canny edge detection on it and then save it as edges.jpg. Below is a zoomed in screen shot of edges.jpg; adjust the parameters in the code to best fit your image.

 

I want to keep the projection from my raw image in the associated .wld file and the simplest way of doing that is to copy the .wld to this newly created edges.jpg file. The shutil library makes that process easy in Python.

import shutil
src = "...band2_mercator.wld"
dst = "...edges.wld"
shutil.copy(src,dst) #copy file

Image bounds

Next up we need to use gdal to get the image bounds so that we can parse them into Folium so it can plot on Leaflet our images. I just need to slightly adjust the code from my raster boundary example here.

from osgeo import gdal

raw_lyr = ('...band2_mercator.jpg')

ds = gdal.Open(raw_lyr)
if ds is None:
    print('Could not open')

#cols and rows
geotransform = ds.GetGeoTransform()
cols = ds.RasterXSize
rows = ds.RasterYSize

#Get the extent
xmin=geotransform[0]
ymax=geotransform[3]
xmax=xmin+cols*geotransform[1]
ymin=ymax+rows*geotransform[5]

bounds = [[ymin, xmin], [ymax, xmax]]
print (bounds)

The variable bounds is ultimately what we need here and it will be the same result whether we parse edges.jpg or band2_mercator.jpg to Folium.

Create a web map

We have done the computer vision part, we have ensured any outputs keep the projection (OpenCV doesn’t know about ‘Geospatial’) and we have extracted the bounds of the image using GDAL. All that is left is to create the web map. That is surprisingly simple as well.

## tell folium where to load and what background map to use
m = folium.Map([40, 7], zoom_start=8, tiles='stamenterrain')


## add my edge detection layer
folium.plugins.ImageOverlay(
    name='My edge detection layer',
    image=edge_lyr,
    bounds=[[ymin, xmin], [ymax, xmax]], ### bounds from above
    opacity=1,
).add_to(m)


## add the raw satellite image below it
folium.plugins.ImageOverlay(
    name='raw satellite',
    image=raw_lyr,
    bounds=[[ymin, xmin], [ymax, xmax]], ## same bounds
    opacity=1,
).add_to(m)

folium.LayerControl().add_to(m) ## add to the control

## save the map

m.save('D:/cv_leaflet/Computer_vision_leaflet_eg.html')

## display in jupyter notebook

m

## that's right just m!

If you want to change the background tiles there is an excellent blog post highlighting some of them here.

Here’s an example of the Python 3 Jupyter Notebook:

You will also have a .html created that you can open up in your browser or run it on localhost and style it to your needs, or embed it on your website.

I think that Folium offers an incredibly easy way to create webmaps with Python, fast. Even more compelling is that it allows us to share raw satellite imagery or processed imagery online with others while keeping the position of the image, providing context for any results.

All the code is available as a Python 3.6 Jupyter Notebook on my GitHub page.

Recap

  • We took a downloaded open source satellite image and applied computer vision edge detect on it.
  • We made sure we were using the correct projection for web mapping and copied the .wld file to any processed images.
  • We used GDAL to obtain the bounds of the image.
  • We used folium to load this image into a webmap powered by Leaflet.
  • And it displayed in Jupyter Notebooks so we could interact with the data and display it in a meaningful context.

I am a freelancer able to help you with your projects. I offer consultancy, training and writing. I’d be delighted to hear from you.

I have grouped all my previous blogs (technical stuff / tutorials / opinions / ideas) at http://gis.acgeospatial.co.uk.

Feel free to connect or follow me; I am always keen to talk about Earth Observation.

I am @map_andrew on twitter