ZtoRGBpy

Version 1.0.1 Documentation

Custom Scaling

The scaling of the color mapping is controlled by providing a subclass of the abstract class Scale. Two building scales are provided. LinearScale provides a linear scaling between 0 and an upper limit, and LogScale provides a logarithmic scaling. To demonstrate creation of a custom scaling in this tutorial we’ll implement a normalized scale, which results in a phase image by normalizing the magnitude (generally LogScale would be sufficient while maintaining magnitude information); we’ll also implement a split linear scale which is just a pair of linear scales with a central point mapped to a specified luminance value.

Normalized Scale

This scale is very simple. We simply divide the complex value by its magnitude, however we need to handle 0 specially, since we can’t recover the phase information for 0 value anyway we just leave them as zero i.e. divide by 1 instead of the magnitude i.e. we use the expression (magnitude > 0)*(magnitude) + (magnitude == 0) to find the normalization factor and avoid a division by zero error. Also since such a scale can’t support a colorbar only the __call__ method needs to be implemented.

import ZtoRGBpy

class NormalizedScale(ZtoRGBpy.Scale):
    """Normalize the magnitude to 1"""

    def __call__(self, value):
        """Transform value with scaling function"""
        magnitude = abs(value)
        value /= (magnitude > 0)*(magnitude) + (magnitude == 0)
        return value

import numpy as np
import matplotlib.pyplot as plt

r = np.linspace(-5,5, 2001)
x,y = np.meshgrid(r,r)
z = x + 1j*y

ZtoRGBpy.imshow(np.cos(z),
                scale=NormalizedScale,
                extent=[-5,5,5,-5])
plt.show()

(png, hires.png, pdf)

../../_images/custom_scale-1.png

Split Linear Scale

In this case we want to make small values more visible while still using a linear scale such as mapping the point that is 10% of the value range to 50% of the luminance range. Either side of this point the scale should be linear. This Scale has 3 parameters, the maximum value, the value midpoint fraction (10%), and the luminance midpoint fraction (50%), however remap when passed a subclass of Scale which accepts more than one positional argument assumes the first two are the minimum and maximum magnitudes of the data respectively, hence the later two must be defined as keyword only arguments.

import ZtoRGBpy
import numpy as np

class SplitLinearScale(ZtoRGBpy.Scale):
    """Normalize the magnitude to 1"""

    def __init__(self, max, *, vmid=0.1, lmid=0.5):
        self.max = max
        self.vmid = vmid
        self.lmid = lmid

    def __call__(self, value):
        """Transform value with scaling function"""
        result = np.zeros_like(value)
        cutoff = self.max*self.vmid
        upper_factor = (1.0 - self.lmid)/(self.max - cutoff)
        lower_factor = self.lmid/cutoff
        if np.any(np.iscomplex(value)):
            angle = np.angle(value)
            zcutoff = cutoff*np.exp(1j*angle)
            zmid = self.lmid*np.exp(1j*angle)
        else:
            zcutoff = cutoff
            zmid = self.lmid
        result += (abs(value) >= cutoff)*(zmid + upper_factor*(value - zcutoff))
        result += (abs(value) < cutoff)*(lower_factor*value)
        return result

    def ticks(self):
        return [0.0, self.lmid, 1.0], ["0.0",
                                       "{0:.2g}".format(self.max*self.vmid),
                                       "{0:.2g}".format(self.max)]

    def __repr__(self):
        return super(SplitLinearScale,self).__repr__("{0:g}, "
                "vmid={1:g}, lmid={2:g}".format(
                    self.max, self.vmid, self.lmid))



import matplotlib.pyplot as plt

r = np.linspace(-5,5, 2001)
x,y = np.meshgrid(r,r)
z = x + 1j*y

ZtoRGBpy.imshow(np.cos(z),
                scale=SplitLinearScale,
                extent=[-5,5,5,-5])
ZtoRGBpy.colorbar()
plt.show()

(png, hires.png, pdf)

../../_images/custom_scale-2.png