| from numba import njit, prange
|
| import numpy as np
|
| from scipy.signal import convolve
|
|
|
|
|
| def log_scale_with_zero(range, n=65536, dtype=np.float32):
|
| scale = np.linspace(np.log10(range[0]), np.log10(range[1]), n-1)
|
| scale = np.hstack((0, 10**scale)).astype(dtype)
|
| return scale
|
|
|
|
|
| def log_quantize_with_zero(x, range, n=65536, dtype=np.uint16):
|
| scale = log_scale_with_zero(range, n=n, dtype=x.dtype)
|
| y = np.empty_like(x, dtype=dtype)
|
| log_quant_with_zeros(x, y, np.log10(scale[1:]))
|
| return (y, scale)
|
|
|
|
|
|
|
| @njit(parallel=True)
|
| def log_quant_with_zeros(x, y, scale):
|
| x = x.ravel()
|
| y = y.ravel()
|
| min_val = 10**scale[0]
|
|
|
| for i in prange(x.shape[0]):
|
|
|
| if x[i] < min_val:
|
| y[i] = 0
|
| continue
|
|
|
| lx = np.log10(x[i])
|
| if lx >= scale[-1]:
|
|
|
| y[i] = len(scale)
|
| else:
|
|
|
| k0 = 0
|
| k1 = len(scale)
|
| while k1-k0 > 1:
|
| km = k0 + (k1-k0)//2
|
| if lx < scale[km]:
|
| k1 = km
|
| else:
|
| k0 = km
|
|
|
| if k0 == len(scale)-1:
|
| q = k0
|
| elif k0 == 0:
|
| q = 0
|
| else:
|
| d0 = abs(lx-scale[k0])
|
| d1 = abs(lx-scale[k1])
|
| if d0 < d1:
|
| q = k0
|
| else:
|
| q = k1
|
|
|
| y[i] = q+1
|
|
|
|
|
| @njit(parallel=True)
|
| def average_pool(x, factor=2, missing=65535):
|
| y = np.empty((x.shape[0]//factor, x.shape[1]//factor), dtype=x.dtype)
|
| N = factor**2
|
| N_thresh = N//2
|
|
|
| for iy in prange(y.shape[0]):
|
| ix0 = iy * factor
|
| ix1 = ix0 + factor
|
| for jy in range(y.shape[1]):
|
| jx0 = jy * factor
|
| jx1 = jx0 + factor
|
| v = float(0.0)
|
| num_valid = 0
|
|
|
| for ix in range(ix0, ix1):
|
| for jx in range(jx0, jx1):
|
| if x[ix,jx] != missing:
|
| v += x[ix,jx]
|
| num_valid += 1
|
|
|
| if num_valid >= N_thresh:
|
| y[iy,jy] = v/num_valid
|
| else:
|
| y[iy,jy] = missing
|
|
|
| return y
|
|
|
|
|
| @njit(parallel=True)
|
| def mode_pool(x, num_values=256, factor=2):
|
| y = np.empty((x.shape[0]//factor, x.shape[1]//factor), dtype=x.dtype)
|
|
|
| for iy in prange(y.shape[0]):
|
| v = np.empty(num_values, dtype=np.int64)
|
| ix0 = iy * factor
|
| ix1 = ix0 + factor
|
| for jy in range(y.shape[1]):
|
| jx0 = jy * factor
|
| jx1 = jx0 + factor
|
| v[:] = 0
|
|
|
| for ix in range(ix0, ix1):
|
| for jx in range(jx0, jx1):
|
| v[x[ix,jx]] += 1
|
|
|
| y[iy,jy] = v.argmax()
|
|
|
| return y
|
|
|
|
|
| def fill_holes(missing=65535, rad=1):
|
| def fill(x):
|
|
|
| o = np.ones((2*rad+1,2*rad+1), dtype=np.uint16)
|
| n = np.prod(o.shape)
|
| valid = (x != missing)
|
| num_valid_neighbors = convolve(valid, o, mode='same', method='direct')
|
| mask = ~valid & (num_valid_neighbors > 0)
|
|
|
|
|
| fx = x.copy()
|
| fx[~valid] = 0
|
| mx = convolve(fx, o.astype(np.float64), mode='same', method='direct')
|
| mx = mx[mask] / num_valid_neighbors[mask]
|
| if np.issubdtype(x.dtype, np.integer):
|
| mx = mx.round().astype(x.dtype)
|
|
|
|
|
| fx = x.copy()
|
| fx[mask] = mx
|
| return fx
|
|
|
| return fill
|
|
|
|
|