In this tutorial we will explore how to use color lookup tables to remap data inside a Jitter matrix. We'll also look at different strategies for generating lookup tables as matrices.
Lookup tables are simply arrays of numbers where an input number is treated as an address (or position) in the array. The table then outputs the number stored at that address. Any function—a graph where each x
value (address) has a corresponding y
value (output)—can be used as a lookup table. Max objects such as funbuff
, and the MSP buffer~
object are common candidates for use as lookup tables. In this tutorial, we'll be using Jitter matrices in much the same way.
The tutorial patch shows two new objects: jit.charmap
, which maps input cell values to new output values according to a lookup table matrix, and jit.gradient
, which generates color gradients.
The top left of the patch shows a jit.qt.movie
object in which you can read two different files. The object is initialized (via loadbang
) with the file colorwheel.pct
loaded into it. You can also load in the movie rain.mov
by clicking on the message
box that says . You should feel free to alternate between the two image sources throughout the tutorial.
• Start the metro
by clicking the toggle
box at the top of the patch. You will see the color wheel appear in both the jit.pwindow
at the top and the jit.pwindow
at the bottom of the patch. In addition, you will see a gradient appear in a third (rectangular) jit.pwindow
at the bottom.
The bottom of the patch contains the jit.charmap
object, which we will use in this tutorial to remap cell values in the image. The object has two inlets, the left one of which is connected from our jit.qt.movie
object at the top of the patch. The right inlet has a one-dimensional, four-plane char jit.matrix
connected to it with a ( ) and a width of cells. This is the lookup table that jit.charmap
uses to remap the color values of the cells in the lefthand matrix. The receive
object (abbreviated r
) with the name receives data from elsewhere in the patch and sends it to the jit.matrix
. For example, at the top of the patch, turning on the toggle
box will send a to the jit.matrix
, causing it to send out its matrix message ( ) to both the jit.pwindow
and the right inlet of jit.charmap
Lookup tables (which are often called transfer functions) are arrays of numbers where an input number is 'looked up' as an index in the table. The number stored at that index (or address, or position) is then retreived to replace the original number. The jit.charmap
object replaces every value in every plane in every cell of its (leftmost) input matrix with values stored at the relevant indices in the lookup table that arrives as a matrix in its right inlet.
For example, assume that the matrix we send to jit.charmap
contains a cell with the values in its four planes. The object looks up each plane individually at the relevant position in its lookup table matrix and replaces it. If our lookup table has the value at cell in the first plane, at cell in the second plane, at cell in the third plane, and at cell in the fourth plane, our output cell will contain the values .
Lookup tables for jit.charmap
should be one-dimensional matrices of cells with the same number of planes as the matrix you want to remap. This is because the possible range of values for char matrices is 0-255, so 256 numbers are need to cover the full range of the lookup table.
The upper-right side of the tutorial patch contains three multislider
objects that let you design the transfer functions for planes 1-3 of the lookup table matrix :
Filling the lookup table matrix with values from a multislider
objects (which have 256 integer sliders in the range 0-255) send their lists to the jit.fill
objects below them. These objects replace the values currently stored in planes 1-3 (i.e. Red, Green, and Blue) of the matrix with the values from the multislider
objects. (See Tutorial 11.
) When the matrix has been edited with the new values, the jit.fill
objects send out a , which we send
to the jit.matrix
on the right of the patch that connects to the right inlet of jit.charmap
. We're ignoring plane 0 in this tutorial because it only contains Alpha values when we treat 4-plane Jitter matrices as video.
objects in our patch share the same ( ). As a result, the two objects read from and write to the same matrix, allowing one object (jit.fill
) to generate data that the other object (jit.matrix
) can read from without having to copy data between two separate matrices. This is similar to how many MSP objects (e.g. peek~
) can share sample data stored in a single buffer~
. See Tutorials 11, 16
, and 17
for more information on using named matrices.
• Do some freehand drawing in the multislider
objects, to see how this affects both the lookup table (the smaller of the jit.pwindow
objects) and the output image from the jit.qt.movie
object. Remember to switch back and forth between the two image sources.
If you want to reset any of the planes to a y="x" transfer function (i.e. a straight ascending line that leaves all the values unchanged), you can click the button
object above the relevant multislider
object. The subpatchers called p
initialize the multislider
objects with an uzi
Important note: Like many Max objects, Jitter objects retain matrices stored in one inlet even if a new matrix arrives in another inlet. The metro
object in this patch, therefore, only needs to trigger the jit.qt.movie
object. The jit.matrix
that contains the lookup table to jit.charmap
only needs to output its value to the object when the data stored in it actually changes.
Here are some lookup tables and their results:
Three sets of multislider
objects and their resulting color lookup tables and output color wheels
In the first example, the red and blue transfer functions are approximately inverted while the green is normal. The result is that high values of red and blue in the input image yield low values on the output, and vice versa. This is why the white background of the color wheel now looks green (the cell values of
have been mapped to ).
The second example has the green plane completely zeroed (the transfer function set to 0 across the entire span of input values). The red and blue planes are also set to 0 up to a threshold, at which point they ramp up suddenly (the red more suddenly than the blue). As a result the majority of the colorwheel is black (especially in the 'green' area). The red plane only becomes visible in very high values (i.e. the magenta in the background of the color wheel).
The third example has the red plane mapped normally. The green plane has a parabolic shape to it, where the extreme values are mapped high and the medium shades are mapped low. The blue plane is normal except for a range in the midtones, where it is zeroed. This nonlinearity is visible as a red 'fault line' across the top and down the right side of the colorwheel.
As you can see, there are infinite ways to remap the cell values of these matrices. We'll now investigate another object, which lets us remap the color values in a more precise manner.
The lower right area of the tutorial patch shows a method for generating lookup tables using the jit.gradient
object generates single dimension char matrices that transition smoothly between two specified cell values. The and attributes are lists that specify these cell values. For example, a attribute of and an attribute of will generate a gradient that goes from black (at cell in the matrix) to pale green (at the last cell in the matrix). We've given our jit.gradient
object the relevant arguments to make it cells wide, so that it can be stored in our jit.matrix
when it changes. Note that jit.gradient
takes floating point numbers in its attribute lists to specify char values (i.e. a value of in the attribute specifies a char value of ).
The attributes are formatted by taking the RGB list output of the Max swatch
objects and converting them to ARGB floats. After the attribute has been sent to the jit.gradient
object, the object is given a from the trigger
object to cause it to output its matrix into the jit.matrix
object on the left of the patch.
• Try selecting some colors in the swatch
objects. The and attributes will specify the boundaries of the lookup table, so values in the input image will have a duotone appearance, morphing between those two colors. The multislider
objects at the top of the patch will reflect the correct lookup tables generated by the jit.gradient
Using color gradients as lookup tables
The first example shows an inverted image. The start of the lookup table is
) and the end of the lookup table is black ( ). As a result, input values of are mapped to , and vice versa (y="255-x)."
The second and third examples show duotone gradients that remap the color wheel's spectrum to between red and orange (example 2) and olive and cyan (example 3). Notice how, depending on the original colors at different points in the color wheel, the gradient curve becomes steeper or more gradual.
The third attribute of the jit.gradient
object is the attribute, which specifies a curve to follow when morphing between the and values in the matrix. The attribute takes a list of floating point numbers as arguments. These arguments are the amplitudes of different orders of Chebyshev polynomials
(see below). These special function curves create different effects when used in lookup tables. The multislider
in the tutorial patch that sets the attribute lets you specify the relative amplitude of the first eight Chebyshev polynomial curves, which have the following shapes (if you view them as progressing from black to white):
Gradients generated using Chebyshev orders 1-4 (top row) and 5-8 (bottom row)
Technical note: Chebyshev polynomials are commonly used as transfer functions in waveshaping audio signals in digital synthesis algorithms (they have special properties that allow them to distort sinusoidal waveforms into harmonic spectra equivalent to the amplitudes of different orders). The MSP lookup~
object can be used with a function loaded into a buffer~
to do the equivalent process in audio signal processing that we're doing in this tutorial with image. See Tutorial 12: Synthesis: Waveshaping
in the MSP
manual for more details.
• Reset the message
boxes above them) and slowly change the multislider
that controls the attribute. Watch how the color wheel changes as colors disappear and reappear in different regions.
and points of the gradient (by clicking the
When you use the jit.gradient
object, you can get some very interesting color warping effects even if you leave the and points of the gradient at black and white. Here are some examples with our movie clip rain.mov
attribute in the
The effect of different gradient curves on the color spectrum of the rain
The lefthand image shows an unprocessed still image from the rain movie. The middle image shows what happens to the color spectrum when the gradient is generated using a second order Chebyshev polynomial (the darkest area in the image is now in the middle of the color spectrum). The righthand image shows a more complex gradient, where the color spectrum shows numerous peaks and troughs.
• The multislider
objects at the top of the patch reflect the current state of our lookup table (the matrix generated by ourjit.gradient
object is sent to a jit.iter
object inside of the p
subpatch, where the numbers are grouped to set the state of the multislider
objects). Try generating a gradient and then modifying the lookup table by hand by changing the multislider
objects. This lets you use the jit.gradient
object as a starting point for a more complicated lookup table.
You can map cell values in char
Jitter matrices using the jit.charmap
object. The right inlet of jit.charmap
takes a -cell matrix that defines the lookup table (or transfer function) to be applied to the incoming matrix data. You can define the lookup table using several strategies, including using jit.fill
to generate the matrix from Max lists, or using the jit.gradient
object to generate color gradients between a and cell value according to a curve shape specified by the attribute.
256 point input to output map
Fill a matrix with a list
Generate Chebyshev gradients
The Jitter Matrix!
Play or edit a QuickTime movie
Output a bang message at regular intervals
Multiple slider and scrolling display
Color swatch for RGB color selection and display
Send a specific number of bang messages
Evaluate a math expression for a list of different inputs