Shady.Linearization Sub-module
This module contains several utility functions related to linearization.
Some of these (ScreenNonlinearity
, Linearize
, ApplyLUT
)
are only useful in special circumstances where we want to re-create
“offline” what the shader is doing for us on every frame.
Others (LoadLUT
, SaveLUT
) are for general management of
look-up table arrays.
Others (BitStealingLUT
, ReportBitStealingStats
) are useful
for creating and examining a specific type of look-up table
that employs a bit-stealing technique (after Tyler 1997).
Note that everything exported from this module is also available in
the top-level Shady.*
namespace.
- Shady.Linearization.ApplyLUT(image, lut, noiseAmplitude=0, DACbits=8)
Translate an array of
image
pixel values via a look-up tablelut
.This allows us to emulate, on the CPU in Python, the look-up operation performed automatically by the GPU in the shader on every frame if a look-up table has been configured via the
Stimulus.lut
property.- Parameters:
image (numpy.array) – Source pixel values. If the array data type is floating-point, pixel values are assumed to refer to ideal luminances in the range 0 to 1, and are clipped to this range before scaling and rounding according to the size of the
lut
. If the array is of some integer type, the values are assumed to be direct indices into the look-up table.Note that, if the image has more than one color channel (i.e. it has a third dimension with extent > 1) then only the first channel (red) will be used.
lut (numpy.array) – Look-up table array, e.g. as output by
LoadLUT()
orBitStealingLUT()
.noiseAmplitude (float) – specifies the amplitude of an optional random signal that can be added to
image
pixel values before lookup. A negative value indicates a uniform distribution (in the range[noiseAmplitude, -noiseAmplitude]
) whereas a positive value is interpreted as the standard deviation of a Gaussian noise distribution.DACbits (int) – The number of bits per digital-analog converter in the graphics card for which the look-up table is intended. Floating-point image pixel values will be scaled accordingly.
- Returns:
An array of integer DAC values post-lookup. First two dimensions match those of the input
image
. Extent in the third dimension will match that oflut
.
- Shady.Linearization.BitStealingLUT(maxDACDeparture=2, Cmax=3.0, nbits=16, gamma='sRGB', cache_dir=None, DACbits=8)
Create an RGB look-up table that (a) linearizes pixel intensity values according to the specified
gamma
profile, and (b) increases dynamic range using a “bit-stealing” approach (after Tyler 1997).- Parameters:
maxDACDeparture (int) – Red, green and blue DAC values will be considered only up to +/- this value relative to the
R==G==B
linenbits (int) – The look-up table will have
2 ** nbits
entries. It doesn’t hurt to specify a high number here, like 16—however, note that, depending on the values ofmaxDACDeparture
andCmax
you may not get that many distinct or evenly-spaced luminance levels. The actual effective precision can be investigated usingReportBitStealingStats()
Cmax (float) –
[R,G,B]
triples will not be considered if the corresponding chroma, in percent (i.e. the third column ofRGB_to_YLCHab()
output) exceeds this.gamma (float) – screen non-linearity parameter (see
ScreenNonlinearity()
)cache_dir (str, None) – optional directory in which to look for a cached copy of the resulting LUT (or save one, after creation, if the appropriately-named file was not found there).
- DACbits (int):
The number of bits per digital-analog converter in the graphics card for which the look-up table is intended. LUT values will be scaled accordingly.
- Returns:
numpy
array of shape[2**nbits, 1, 3]
with the appropriate integer type (usuallyuint8
), containing integer RGB triplets.
- Shady.Linearization.Linearize(Y, gamma='sRGB')
Maps ideal luminance
Y
(in the domain 0 to 1) to normalized DAC valuesx
(in the range 0 to 1, which corresponds to DAC values 0 to 255 if we assume standard 8-bit DACs) given the screen non-linearity parametergamma
.Generally,
gamma
is numeric and strictly positive, in which case the relationship isx = Y ** (1/gamma)
. A special case isgamma='sRGB'
, which is also used if you pass agamma
value of 0 or less: this uses a slightly different piecewise equation, very close to thegamma=2.2
curve (even though the exponent used in it is actually 2.4).This allows us to emulate, on the CPU and in Python, the linearization operation performed automatically by the GPU in the fragment shader on every frame according to the value of of the
Stimulus.gamma
property.Inverse of
ScreenNonlinearity()
- Shady.Linearization.LoadLUT(source, DACbits=8)
Load or prepare a look-up table array.
- Parameters:
source (str, numpy.ndarray) – Usually this is a string denoting a filename. The file may be in numpy format - either a
npy
file in which the the lookup-table has been written withnumpy.save
, or anpz
file into which the look-up table array has been saved withnumpy.savez
as either the sole variable or a variable calledlut
. If the third-partypillow
package is installed, the file may alternatively be apng
image file in which look-up table entries have been saved as R,G,B pixel values in column- first order.The
source
may also be anumpy.ndarray
already.DACbits (int) – This is the number of bits per DAC in the graphics card for which the look-up table is intended. In this function it is used to verify that the lookup-table entries are in range and to cast the output as the appropriate numeric data-type.
- Returns:
Whether
source
is a filename or an array already, in either case, this function ensures that the returned result is a 3-dimensionalnumpy
array, of the appropriate integer type, with extent 3 (RGB) or 4 (RGBA) in its third dimension.
See also
- Shady.Linearization.ReportBitStealingStats(lut=None, image=None, gamma='sRGB', DACbits=8)
Prints to the console certain statistics about the look-up table
lut
, if supplied, and optionally also any givenimage
that has been through the look-up process (i.e. an output ofApplyLUT()
).Make sure that the
gamma
andDACbits
arguments match the values that were used for creatinglut
.
- Shady.Linearization.SaveLUT(filename, lut, luminance=(), DACbits=8)
Save a look-up table array, and optionally also the corresponding sequence of luminance values, in a file.
- Parameters:
filename (str) – name of the file to save. Determines the file format, and should end in
npy
,npz
orpng
lut (numpy.array) – look-up table array,
n
-by-3 orn
-by-1-by-3 as per the output ofLoadLUT()
orBitStealingLUT()
, wheren
is the number of entries. If the format isnpz
the array will be saved under the variable namelut
.luminance (numpy.array, list) – sequence of
n
ideal luminance values (i.e. luminance values in the range 0 to 1) corresponding to each of the look-up table entries. Only saved (under the variable nameluminance
) if the file format isnpz
DACbits (int) – This is the number of bits per DAC in the graphics card for which the look-up table is intended. In this function it is used to verify that the lookup-table entries are in range and to cast the output as the appropriate numeric data-type.
- Returns:
the filename
the look-up table array in standardized format
the sequence of luminance values
- Return type:
a tuple consisting of
See also
- Shady.Linearization.ScreenNonlinearity(x, gamma='sRGB')
Maps normalized DAC values
x
(in the domain 0 to 1, which corresponds to DAC values 0 to 255 if we assume standard 8-bit DACs) to ideal luminanceY
(in the range 0 to 1), given the screen non-linearity parametergamma
.Generally,
gamma
is numeric and strictly positive, in which case the relationship isY = x ** gamma
. A special case isgamma='sRGB'
, which is also used if you pass agamma
value of 0 or less: this uses a slightly different piecewise equation, very close to thegamma=2.2
curve (even though the exponent used in it is actually 2.4).Inverse of
Linearize()