def denorm(xy=None, y=None, nodata=None): if xy is None: return A stacked = y is None x, y = xy if stacked else (xy, y) missing_mask = None if nodata is not None: if np.isnan(nodata): missing_mask = np.isnan(x) + np.isnan(y) else: missing_mask = (x == nodata) + (y == nodata) if x.dtype.kind != 'f': x = from_fixed_point(x) y = from_fixed_point(y) x, y = apply_affine(A, x, y) if missing_mask is not None: x[missing_mask] = np.nan y[missing_mask] = np.nan if stacked: return np.stack([x, y]) else: return x, y
def xy_from_gbox(gbox: GeoBox) -> Tuple[np.ndarray, np.ndarray]: """ :returns: Two images with X and Y coordinates for centers of pixels """ h, w = gbox.shape xx, yy = np.meshgrid( np.arange(w, dtype='float64') + 0.5, np.arange(h, dtype='float64') + 0.5) return apply_affine(gbox.transform, xx, yy)
def test_apply_affine(): A = mkA(rot=10, scale=(3, 1.3), translation=(-100, +2.3)) xx, yy = np.meshgrid(np.arange(13), np.arange(11)) xx_, yy_ = apply_affine(A, xx, yy) assert xx_.shape == xx.shape assert yy_.shape == xx.shape xy_expect = [A * (x, y) for x, y in zip(xx.ravel(), yy.ravel())] xy_got = [(x, y) for x, y in zip(xx_.ravel(), yy_.ravel())] np.testing.assert_array_almost_equal(xy_expect, xy_got)
def test_xy_from_geobox(): gbox = GeoBox(3, 7, Affine.translation(10, 1000), epsg3857) xx, yy = xy_from_gbox(gbox) assert xx.shape == gbox.shape assert yy.shape == gbox.shape assert (xx[:, 0] == 10.5).all() assert (xx[:, 1] == 11.5).all() assert (yy[0, :] == 1000.5).all() assert (yy[6, :] == 1006.5).all() xx_, yy_, A = xy_norm(xx, yy) assert xx_.shape == xx.shape assert yy_.shape == yy.shape np.testing.assert_almost_equal((xx_.min(), xx_.max()), (0, 1)) np.testing.assert_almost_equal((yy_.min(), yy_.max()), (0, 1)) assert (xx_[0] - xx_[1]).sum() != 0 assert (xx_[:, 0] - xx_[:, 1]).sum() != 0 XX, YY = apply_affine(A, xx_, yy_) np.testing.assert_array_almost_equal(xx, XX) np.testing.assert_array_almost_equal(yy, YY)
def xy_norm(x: np.ndarray, y: np.ndarray, deg: float = 33.0) -> Tuple[np.ndarray, np.ndarray, Affine]: """ Transform output of xy_from_geobox with a reversible linear transform. On output x,y are in [0,1] range. Reversible Affine transform includes rotation by default, this is to ensure that test images don't have symmetries that are aligned to X/Y axis. 1. Rotate x,y by ``deg`` 2. Offset and scale such that values are in [0, 1] range :returns: (x', y', A) - (x, y) == A*(x', y') - [x|y]'.min() == 0 - [x|y]'.max() == 1 """ def norm_v(v): vmin = v.min() v -= vmin s = 1.0 / v.max() v *= s return (s, -vmin * s) A_rot = Affine.rotation(deg) x, y = apply_affine(A_rot, x, y) sx, tx = norm_v(x) sy, ty = norm_v(y) A = Affine(sx, 0, tx, 0, sy, ty) * A_rot return x, y, ~A