pept.Pixels#

class pept.Pixels(pixels_array, xlim, ylim, **kwargs)[source]#

Bases: object

A class managing a 2D pixel space with physical dimensions, including tools for pixel manipulation and visualisation.

The .pixels attribute is simply a numpy.ndarray[ndim=2, dtype=float64]. If you think of Pixels as an image, the origin is the top left corner, the X-dimension is the left edge and the Y-dimension is the top edge, so that it can be indexed as .pixels[ix, iy].

The .attrs dictionary can be used to store extra information.

See also

konigcell.Voxels

A class managing a physical 3D voxel space.

konigcell.dynamic2d

Rasterize moving particles’ trajectories.

konigcell.static2d

Rasterize static particles’ positions.

konigcell.dynamic_prob2d

2D probability distribution of a quantity.

Notes

The class saves pixels as a contiguous numpy array for efficient access in C / Cython functions. The inner data can be mutated, but do not change the shape of the array after instantiating the class.

Examples

Create a zeroed 4x4 Pixels grid:

>>> import konigcell as kc
>>> pixels = kc.Pixels.zeros((4, 4), xlim = [0, 10], ylim = [0, 5])
>>> pixels
Pixels
------
xlim = [ 0. 10.]
ylim = [0. 5.]
pixels =
  (shape: (4, 4))
  [[0. 0. 0. 0.]
   [0. 0. 0. 0.]
   [0. 0. 0. 0.]
   [0. 0. 0. 0.]]
attrs = {}

Or create a Pixels instance from another array (e.g. an image or matrix):

>>> import numpy as np
>>> matrix = np.ones((3, 3))
>>> pixels = kc.Pixels(matrix, xlim = [0, 10], ylim = [-5, 5])
>>> pixels
Pixels
------
xlim = [ 0. 10.]
ylim = [-5.  5.]
pixels =
  (shape: (3, 3))
  [[1. 1. 1.]
   [1. 1. 1.]
   [1. 1. 1.]]
attrs = {}

Access pixels’ properties directly:

>>> pixels.xlim             # ndarray[xmin, xmax]
>>> pixels.ylim             # ndarray[ymin, ymax]
>>> pixels.pixel_size       # ndarray[xsize, ysize]
>>> pixels.pixels.shape     # pixels resolution - tuple[nx, ny]

You can save extra attributes about the pixels instance in the attrs dictionary:

>>> pixels.attrs["dpi"] = 300
>>> pixels
Pixels
------
xlim = [ 0. 10.]
ylim = [-5.  5.]
pixels =
  (shape: (3, 3))
  [[1. 1. 1.]
   [1. 1. 1.]
   [1. 1. 1.]]
attrs = {
  'dpi': 300
}

The lower left and upper right corners of the pixel grid, in physical coordinates (the ones given by xlim and ylim):

>>> pixels.lower
array([ 0., -5.])
>>> pixels.upper
array([10.,  5.])

You can access the underlying NumPy array directly:

>>> pixels.pixels
array([[1., 1., 1.],
       [1., 1., 1.],
       [1., 1., 1.]])

Indexing is forwarded to the NumPy array:

>>> pixels[:, :]
array([[1., 1., 1.],
       [1., 1., 1.],
       [1., 1., 1.]])

Transform physical units into pixel indices:

>>> pixels.from_physical([5, 0])                    # pixel centres
array([1., 1.])
>>> pixels.from_physical([5, 0], corner = True)     # lower left corners
array([1.5, 1.5])

Transform pixel indices into physical units:

>>> pixels.to_physical([0, 0])                      # pixels centres
array([ 1.66666667, -3.33333333])
>>> pixels.to_physical([0, 0], corner = True)       # lower left corners
array([ 0., -5.])

Save Pixels instance to disk, as a binary archive:

>>> pixels.save("pixels.pickle")
>>> pixels = kc.load("pixels.pickle")

Create deep copy of a Pixels instance:

>>> Pixels.copy()

Matplotlib plotting (optional, if Matplotlib is installed):

>>> fig, ax = pixels.plot()
>>> fig.show()

Plotly trace (optional, if Plotly is installed):

>>> import plotly.graph_objs as go
>>> fig = go.Figure()
>>> fig.add_trace(pixels.heatmap_trace())
>>> fig.show()
Attributes
pixels(M, N) np.ndarray[ndim=2, dtype=float64]

The 2D numpy array containing the pixel values. This class assumes a uniform grid of pixels - that is, the pixel size in each dimension is constant, but can vary from one dimension to another.

xlim(2,) np.ndarray[ndim=1, dtype=float64]

The lower and upper boundaries of the pixellised volume in the x-dimension, formatted as [x_min, x_max].

ylim(2,) np.ndarray[ndim=1, dtype=float64]

The lower and upper boundaries of the pixellised volume in the y-dimension, formatted as [y_min, y_max].

pixel_size(2,) np.ndarray[ndim=1, dtype=float64]

The lengths of a pixel in the x- and y-dimensions, respectively.

pixel_gridslist[(M+1,) np.ndarray, (N+1,) np.ndarray]

A list containing the pixel gridlines in the x- and y-dimensions. Each dimension’s gridlines are stored as a numpy of the pixel delimitations, such that it has length (M + 1), where M is the number of pixels in a given dimension.

lower(2,) np.ndarray[ndim=1, dtype=float64]

The lower left corner of the pixel rectangle; corresponds to [xlim[0], ylim[0]].

upper(2,) np.ndarray[ndim=1, dtype=float64]

The upper right corner of the pixel rectangle; corresponds to [xlim[1], ylim[1]].

attrsdict[Any, Any]

A dictionary storing any other user-defined information.

__init__(pixels_array, xlim, ylim, **kwargs)[source]#

Pixels class constructor.

Parameters
pixels_array3D numpy.ndarray

A 3D numpy array, corresponding to a pre-defined pixel space.

xlim(2,) numpy.ndarray

The lower and upper boundaries of the pixellised volume in the x-dimension, formatted as [x_min, x_max].

ylim(2,) numpy.ndarray

The lower and upper boundaries of the pixellised volume in the y-dimension, formatted as [y_min, y_max].

**kwargsextra keyword arguments

Extra user-defined attributes to be saved in .attrs.

Raises
ValueError

If pixels_array does not have exactly 2 dimensions or if xlim or ylim do not have exactly 2 values each.

Notes

No copies are made if pixels_array, xlim and ylim are contiguous NumPy arrays with dtype=float64.

Methods

__init__(pixels_array, xlim, ylim, **kwargs)

Pixels class constructor.

add_lines(lines[, verbose])

Pixellise a sample of lines, adding 1 to each pixel traversed, for each line in the sample.

copy([pixels_array, xlim, ylim])

Create a copy of the current Pixels instance, optionally with new pixels_array, xlim and / or ylim.

from_lines(lines, number_of_pixels[, xlim, ...])

Create a pixel space and traverse / pixellise a given sample of lines.

from_physical(locations[, corner])

Transform locations from physical dimensions to pixel indices.

heatmap_trace([colorscale, transpose, xgap, ...])

Create and return a Plotly Heatmap trace of the pixels.

load(filepath)

Load a saved / pickled Pixels object from filepath.

plot([ax])

Plot pixels as a heatmap using Matplotlib.

save(filepath)

Save a Pixels instance as a binary pickle object.

to_physical(indices[, corner])

Transform indices from pixel indices to physical dimensions.

zeros(shape, xlim, ylim, **kwargs)

Attributes

attrs

lower

pixel_grids

pixel_size

pixels

upper

xlim

ylim

property pixels#
property xlim#
property ylim#
property attrs#
property pixel_size#
property pixel_grids#
property lower#
property upper#
static zeros(shape, xlim, ylim, **kwargs)[source]#
save(filepath)[source]#

Save a Pixels instance as a binary pickle object.

Saves the full object state, including the inner .pixels NumPy array, xlim, etc. in a fast, portable binary format. Load back the object using the load method.

Parameters
filepathfilename or file handle

If filepath is a path (rather than file handle), it is relative to where python is called.

Examples

Save a Pixels instance, then load it back:

>>> import numpy as np
>>> import konigcell as kc
>>>
>>> grid = np.zeros((640, 480))
>>> pixels = kc.Pixels(grid, [0, 20], [0, 10])
>>> pixels.save("pixels.pickle")
>>> pixels_reloaded = kc.Pixels.load("pixels.pickle")
static load(filepath)[source]#

Load a saved / pickled Pixels object from filepath.

Most often the full object state was saved using the .save method.

Parameters
filepathfilename or file handle

If filepath is a path (rather than file handle), it is relative to where python is called.

Returns
pept.Pixels

The loaded pept.Pixels instance.

Examples

Save a Pixels instance, then load it back:

>>> import numpy as np
>>> import konigcell as kc
>>>
>>> grid = np.zeros((640, 480))
>>> pixels = kc.Pixels(grid, [0, 20], [0, 10])
>>> pixels.save("pixels.pickle")
>>> pixels_reloaded = kc.Pixels.load("pixels.pickle")
copy(pixels_array=None, xlim=None, ylim=None, **kwargs)[source]#

Create a copy of the current Pixels instance, optionally with new pixels_array, xlim and / or ylim.

The extra attributes in .attrs are propagated too. Pass new attributes as extra keyword arguments.

from_physical(locations, corner=False)[source]#

Transform locations from physical dimensions to pixel indices. If corner = True, return the index of the bottom left corner of each pixel; otherwise, use the pixel centres.

Examples

Create a simple konigcell.Pixels grid, spanning [-5, 5] mm in the X-dimension and [10, 20] mm in the Y-dimension:

>>> import konigcell as kc
>>> pixels = kc.Pixels.zeros((5, 5), xlim=[-5, 5], ylim=[10, 20])
>>> pixels
Pixels
------
xlim = [-5.  5.]
ylim = [10. 20.]
pixels =
  (shape: (5, 5))
  [[0. 0. ... 0. 0.]
   [0. 0. ... 0. 0.]
   ...
   [0. 0. ... 0. 0.]
   [0. 0. ... 0. 0.]]
attrs = {}
>>> pixels.pixel_size
array([2., 2.])

Transform physical coordinates to pixel coordinates:

>>> pixels.from_physical([-5, 10], corner = True)
array([0., 0.])
>>> pixels.from_physical([-5, 10])
array([-0.5, -0.5])

The pixel coordinates are returned exactly, as real numbers. For pixel indices, round them into values:

>>> pixels.from_physical([0, 15]).astype(int)
array([2, 2])

Multiple coordinates can be given as a 2D array / list of lists:

>>> pixels.from_physical([[0, 15], [5, 20]])
array([[2. , 2. ],
       [4.5, 4.5]])
to_physical(indices, corner=False)[source]#

Transform indices from pixel indices to physical dimensions. If corner = True, return the coordinates of the bottom left corner of each pixel; otherwise, use the pixel centres.

Examples

Create a simple konigcell.Pixels grid, spanning [-5, 5] mm in the X-dimension and [10, 20] mm in the Y-dimension:

>>> import konigcell as kc
>>> pixels = kc.Pixels.zeros((5, 5), xlim=[-5, 5], ylim=[10, 20])
>>> pixels
Pixels
------
xlim = [-5.  5.]
ylim = [10. 20.]
pixels =
  (shape: (5, 5))
  [[0. 0. ... 0. 0.]
   [0. 0. ... 0. 0.]
   ...
   [0. 0. ... 0. 0.]
   [0. 0. ... 0. 0.]]
attrs = {}
>>> pixels.pixel_size
array([2., 2.])

Transform physical coordinates to pixel coordinates:

>>> pixels.to_physical([0, 0], corner = True)
array([-5., 10.])
>>> pixels.to_physical([0, 0])
array([-4., 11.])

Multiple coordinates can be given as a 2D array / list of lists:

>>> pixels.to_physical([[0, 0], [4, 3]])
array([[-4., 11.],
       [ 4., 17.]])
heatmap_trace(colorscale='Magma', transpose=True, xgap=0.0, ygap=0.0)[source]#

Create and return a Plotly Heatmap trace of the pixels.

Parameters
colorscalestr, default “Magma”

The Plotly scheme for color-coding the pixel values in the input data. Typical ones include “Cividis”, “Viridis” and “Magma”. A full list is given at plotly.com/python/builtin-colorscales/. Only has an effect if colorbar = True and color is not set.

transposebool, default True

Transpose the heatmap (i.e. flip it across its diagonal).

Examples

Create a Pixels array and plot it as a heatmap using Plotly:

>>> import konigcell as kc
>>> import numpy as np
>>> import plotly.graph_objs as go
>>> pixels_raw = np.arange(150).reshape(10, 15)
>>> pixels = kc.Pixels(pixels_raw, [-5, 5], [-5, 10])
>>> fig = go.Figure()
>>> fig.add_trace(pixels.heatmap_trace())
>>> fig.show()
plot(ax=None)[source]#

Plot pixels as a heatmap using Matplotlib.

Returns matplotlib figure and axes objects containing the pixel values colour-coded in a Matplotlib image (i.e. heatmap).

Parameters
axmpl_toolkits.mplot3D.Axes3D object, optional

The 3D matplotlib-based axis for plotting. If undefined, new Matplotlib figure and axis objects are created.

Returns
fig, ax

Matplotlib figure and axes objects.

Examples

Pixellise an array of lines and plot them with Matplotlib:

>>> lines = np.array(...)                   # shape (N, M >= 7)
>>> lines2d = lines[:, [0, 1, 2, 4, 5]]     # select x, y of lines
>>> number_of_pixels = [10, 10]
>>> pixels = pept.Pixels.from_lines(lines2d, number_of_pixels)
>>> fig, ax = pixels.plot()
>>> fig.show()
add_lines(lines, verbose=False)#

Pixellise a sample of lines, adding 1 to each pixel traversed, for each line in the sample.

Parameters
lines(M, N >= 5) numpy.ndarray

The sample of 2D lines to pixellise. Each line is defined as a timestamp followed by two 2D points, such that the data columns are [time, x1, y1, x2, y2, …]. Note that there can be extra data columns which will be ignored.

verbosebool, default False

Time the pixel traversal and print it to the terminal.

Raises
ValueError

If lines has fewer than 5 columns.

static from_lines(lines, number_of_pixels, xlim=None, ylim=None, verbose=True)#

Create a pixel space and traverse / pixellise a given sample of lines.

The number_of_pixels in each dimension must be defined. If the pixel space boundaries xlim or ylim are not defined, they are inferred as the boundaries of the lines.

Parameters
lines(M, N>=5) numpy.ndarray

The lines that will be pixellised, each defined by a timestamp and two 2D points, so that the data columns are [time, x1, y1, x2, y2]. Note that extra columns are ignored.

number_of_pixels(2,) list[int]

The number of pixels in the x- and y-dimensions, respectively.

xlim(2,) list[float], optional

The lower and upper boundaries of the pixellised volume in the x-dimension, formatted as [x_min, x_max]. If undefined, it is inferred from the boundaries of lines.

ylim(2,) list[float], optional

The lower and upper boundaries of the pixellised volume in the y-dimension, formatted as [y_min, y_max]. If undefined, it is inferred from the boundaries of lines.

Returns
pept.Pixels

A new Pixels object with the pixels through which the lines were traversed.

Raises
ValueError

If the input lines does not have the shape (M, N>=5). If the number_of_pixels is not a 1D list with exactly 2 elements, or any dimension has fewer than 2 pixels.