Public Lab Research note


If you don't know about georectification...

by sashadev-sky | March 28, 2020 11:37 28 Mar 11:37 | #23224 | #23224

Leaflet.DistortableImage (LDI) -

"A Leaflet extension to distort images -- "rubbersheeting" -- for the MapKnitter.org (src) image georectification service by Public Lab. Leaflet.DistortableImage allows for perspectival distortions of images, client-side, using CSS3 transformations in the DOM." - README

Now that we've introduced LDI, this post should help those that are not GIS mapping enthusiasts get to know the plugin (and potentially make you want to become mapping enthusiasts - highly recommend).

Key concepts

LDI allows for perspectival distortions of aerial images over a flat, projected map. It is key for georectification.

Perspectival distortions?

Try out the distort tool below (3rd tool from the left).

Aerial images typically have some level of distortion resulting from the camera perspective. A perfectly perpendicular photo is difficult to achieve due to many factors, and significantly more so with balloon mapping as you might envision. (wind, camera swinging, etc.)

Projective transformation

We correct for camera perspective distortion by using a projective transformation. Specifically, we calculate a 4*4 projective transformation matrix mapping each corner of the aerial photograph to a geographic coordinate on the map.

An image can be transformed in 2D (planar) using various transformation rules:

image description

These transformations can all be described by transformation matrices which map the untransformed image to the transformed one.

An affine transformation would have been sufficient for rotation and scaling, but distortion requires a projective transformation which does not preserve parallelism.

Preserving parallelism limits affine transformations in a way. If you want to do perspective, you should do a projective transformation.

Georectification

Ultimately, this transformation allows us to neatly fit images taken from multiple perspectives into a common map coordinate system, a process called georectification.

Below is a great visual example of an image being rectified over a plane:

Set of 2D images. The original images are taken from different perspectives (row 1). Using systematic transformations (rows 2 and 3), we are able to transform both images such that corresponding points are on the same horizontal scan lines (row 4).

image description

Translated to code

The projective transformation functionality achieved via the #_calculateProjectiveTransform method is the core of LDI:

 _calculateProjectiveTransform: function(latLngToCartesian) {
    var offset = latLngToCartesian(this.getCorner(0));
    var w = this.getElement().offsetWidth || 500;
    var h = this.getElement().offsetHeight || 375;
    var c = [];

    for (j = 0; j < 4; j++) {
        c.push(latLngToCartesian(this.getCorner(j))._subtract(offset));
    }

    return L.MatrixUtil.general2DProjection(
            0, 0, c[0].x, c[0].y,
            w, 0, c[1].x, c[1].y,
            0, h, c[2].x, c[2].y,
            w, h, c[3].x, c[3].y
    );
  },

The process coded above is as follows:

  1. We remove the core Leaflet translate3d transformation.
  2. We use the cartesian coordinates of the image's top-left corner as the base offset.
  3. We subtract the base offset from the cartesian coordinates of each of the image's 4 corners, placing the image at a standard 0, 0 coordinate in the top-left corner of the map.
  4. With the image being projected at the origin of the map, we compute the homogenous transformation matrix. Using homogenous coordinates allows us to compose multiple transformations (rotation, scaling, distortion) all in a single matrix multiplication.
  5. This matrix is then handed off to the matrix3d CSS3 function, which applies the transformation. It maps a 3x3 projective matrix to 4x4 homogenous coordinates.
  6. We re-apply the core Leaflet translate3d transformation we stripped in step 1.
  7. Every time you distort the image or change the zoom level of the map, this function is triggered again to recalculate.

Contributing:

You can help document this functionality by writing specs for it - please visit this issue.


1 Comments

Love this, and great diagrams!!! Thanks, Sasha!!

Reply to this comment...


Login to comment.