Keywords: image raster resize rescale reproject Manhattan-grid regular-grid
We have a picture (image, raster) A, an array of colored pixels,
and want to warp it to a new picture O of a different size or shape.
Examples:
Map projection, flat maps <-> globe;
Image scaling.
There are hundreds of cases of these, and zillions of programs
with a huge range of capabilities, styles, and learning curves.
The purpose of this note is to describe a bare-bones warp.py
for students,
with pictures A and O on Manhattan grids -- simpler than general grids.
An example of a round-trip test, 4326 -> utm31 -> 4326, with warp.py
:
Think of a map on two layers:
- a flat paper map with continuous coordinates, say degrees or metres on a map of the earth's surface, or millimetres on a computer screen.
- a Manhattan grid: a transparent plastic sheet overlaying that,
with streets running east-west at
y[0] y[1] ...
and avenues north-south atx[0] x[1] ...
.x
andy
may be uniformly spaced,np.linspace
, or may not be.
Given a bunch of points on a map / in Manhatten, there's an important function
snaptogrid: each point -> the nearest streetcorner on the grid, indices `i j`.
We have pixels on the whole A grid, and warp transforms both ways, AtoO and OtoA.
(These may be nonlinear, but are very nearly inverses --
AtoO( OtoA( points )) ~= the same points.)
How do we warp the pixels on the A grid to an O grid
of different size or shape ?
One way is this pipeline, in warp.py
:
transform O gridpoints -> a cloud in A
| trim points ouside A.bbox
| snap to A gridpoints -- maybe dups, maybe missing
| Apix[I,J] at A gridpoints
| Opix at the corresponding O gridpoints
Type names may help to keep track of what's what (or may be mttiw). Most things in this implementation are numpy ndarrays, 1d or n × 2, of various kinds:
Pts = "nx2 floats" # a common idiom: y, x = yx.T
Gridpts = Pts on a Manhattan grid (street corners),
Y in y[0] y[1] .. y[ny-1], X in x[0] x[1] .. x[nx-1]
Cloud = Pts anywhere -- on Gridpts, between them, or outside the bounding box
Ij = "nx2 int indices" I, J = ij.T
Gridarray = "2d numpy array" values A[I,J] at Gridpts
snaptogrid: "Cloud -> Ij"
Transform = "Cloud -> Cloud" may be nonlinear, may go outside a bounding box
In pseudocode on paper, it's useful to think of arrays indexed by continuous y x
.
At the end of the day, real code will have to map floats y x
on the continuous layer
<-> int indices i j
on the grid; these maps are simple but really gum up code.
There are many ways to implement snaptogrid
.
warp.py
uses Near_rgrid
, just a page of code;
scipy RegularGridInterpolator;
also does bilinear interpolation;
scipy griddata ==
scipy KDTree
does points -> k nearest neighbors in an arbitrary point cloud.
See also:
rioxarray reproject UTM -> 4326 widens NaNs at the sides
-- bug, or feature ?
will trade the complete code, about 500 lines, for test cases.
cheers
-- denis-bz-py@t-online.de 1 Nov 2021