import numpy as np
import matplotlib.colors as colors
from matplotlib.colors import Normalize
def _check_inputs(vmin, vcenter, vmax):
if vmax < vmin:
raise ValueError('Should not be: vmax < vmin')
if vcenter is not None:
if vcenter < vmin:
raise ValueError('Should not be: vcenter < vmin')
elif vmax < vcenter:
raise ValueError('Should not be: vmax < vcenter')
elif (not vcenter < vmax) or (not vmin < vcenter):
vcenter = None
return vmin, vcenter, vmax
class TwoSlopeLoggedNorm(Normalize):
def __init__(self, vcenter=None, base=2.0, **kwargs):
super().__init__(**kwargs)
self.base = base
self.vcenter = vcenter
def __call__(self, value, clip=None):
vmin = self.vmin
vmax = self.vmax
if vmin is None:
vmin = np.min(value)
if vmax is None:
vmax = np.max(value)
vcenter = self.vcenter
vmin, vcenter, vmax = _check_inputs(
vmin=vmin,
vcenter=vcenter,
vmax=vmax
)
base = self.base
if not base > 1.0:
raise ValueError('Base should be greater than 1.0')
log_base = np.log(base)
if vcenter is None:
xp, fp = [vmin, vmax], [1.0, base]
mapped_value = np.interp(x=value, xp=xp, fp=fp)
mapped_value = np.log(mapped_value) / log_base
else:
xp, fp = [vmin, vcenter, vmax], [-1.0, 0.0, 1.0]
mapped_value = 2.0 * np.interp(x=value, xp=xp, fp=fp)
for i in range(len(mapped_value)):
v = mapped_value[i]
if v > 0.0:
v = np.log((base-1.0)*v + 1.0) / log_base
elif v < 0.0:
v = -np.log(-(base-1.0)*v + 1.0) / log_base
mapped_value[i] = v / 2.0 + 0.5
return np.ma.masked_array(mapped_value)
class SigmoidNorm(Normalize):
@staticmethod
def exp_sigmoid(x, x_scale):
return 1.0 / (1.0 + np.exp(-12.0 * x_scale * (x-0.5)))
@staticmethod
def log_sigmoid(x, x_scale):
return -np.log(1.0/x - 1.0) / (12.0 * x_scale) + 0.5
def __init__(self, intensity=1.0, vmin=None, vmax=None, vcenter=0.0, **kwargs):
super().__init__(vmin=vmin, vmax=vmax, **kwargs)
self.intensity = intensity
self.vmin = vmin
self.vmax = vmax
self.vcenter = vcenter
def __call__(self, value, clip=None):
vmin = self.vmin
vmax = self.vmax
if vmin is None:
vmin = np.min(value)
if vmax is None:
vmax = np.max(value)
vcenter = self.vcenter
vmin, vcenter, vmax = _check_inputs(
vmin=vmin,
vcenter=vcenter,
vmax=vmax
)
intensity = self.intensity
if vcenter is None:
xp, fp = [vmin, vmax], [0.0, 1.0]
else:
xp, fp = [vmin, vcenter, vmax], [0.0, 0.5, 1.0]
values = np.interp(x=value, xp=xp, fp=fp)
if intensity < 0.0:
intensity = -intensity
if intensity < 1.0:
sigmoid = SigmoidNorm.log_sigmoid(x=values, x_scale=1.0)
values = values - intensity * (values - sigmoid)
else:
sigmoid = SigmoidNorm.log_sigmoid(x=values, x_scale=intensity)
values = values - 1.0 * (values - sigmoid)
else:
if intensity < 1.0:
sigmoid = SigmoidNorm.exp_sigmoid(x=values, x_scale=1.0)
values = values - intensity * (values - sigmoid)
else:
sigmoid = SigmoidNorm.exp_sigmoid(x=values, x_scale=intensity)
values = values - 1.0 * (values - sigmoid)
return np.ma.masked_array(values)