This tutorial shows how you can perform simple mathematical operations on the data stored in a Jitter matrix. We'll show you how to use the jit.op
object to perform arithmetic scaling of matrix cells, or of individual planes within those cells.
The tutorial patch is split into three simple examples of mathematical operations you can perform with the jit.op
object. The jit.op
object performs mathematical operations on entire matrices of data at a time rather than individual numbers.
Adding a constant value to all cells in a matrix.
The first example shows a jit.matrix
object hooked up to a jit.op
whose output is viewable by a jit.pwindow
object. Everytime you change the number
box hooked up to the right inlet of the jit.op
object a will put out a new matrix from the jit.matrix
object. As you can see from its arguments, the jit.matrix
object is generating a 4x3 matrix of single-plane char
data (i.e. data in the range 0-255). The jit.pwindow
object will visualize this matrix for you as a greyscale image. Dragging the number
box will change the level of grey shown in the jit.pwindow
from black ( ) to white ( ).
It's important to realize that the jit.matrix
object is putting out a Jitter matrix that has all its cells set to 0. If you were to connect the jit.matrix
objects together and bypass the jit.op
, you would see a black image, no matter how many times you send a message to the jit.matrix
object. The jit.op
object is adding
a value (as defined by the number
box) to all the cells in the Jitter matrix sent between the jit.matrix
and the jit.op
We said above that the jit.op
a value to all the cells in its input matrix. The jit.op
object adds a value (rather than, say, dividing or multiplying) because of the value of its attribute. The attribute is a symbol (or a list of symbols, as we'll see in a moment) that defines what math the jit.op
performs on its input matrix. In this case, we can see that the attribute is set to the value of , which means that it performs simple addition on any matrix that arrives in its left inlet. The integer value in the right inlet is added to all the cells in the matrix. This value is referred to as a scalar
, because it adds the same value to the entire matrix (in Tutorial 9
we show how jit.op
can do math using two Jitter matrices as well).
Changing the scalar value in the right inlet of the jit.op
object does not output a new matrix. If you were to disconnect the patch cord between the number
box and the button
object, the jit.pwindow
object would stop showing you anything new. The reason for this is that as with most Max objects, most Jitter objects only output data when something comes into their leftmost inlet. In the case above, each time you change the number box
, the jit.op
object stores the new scalar value. As soon as that happens, the button
object sends a to the jit.matrix
object, causing it to send a new Jitter matrix (with all its values set to 0) into the left inlet of the jit.op
object, triggering an output matrix which you can see. If you choose the Enable
command from the Trace menu, and then step through the message order with the Step
command (�-T), you will see how this plays out. (See the "Debugging" chapter of the Max 4.0 Tutorials and Topics
manual for details about how to trace Max messages with the Trace feature.)
The scalar value can also be supplied as a constant by using the jit.op
. For example, if we always wanted to add to all the cells of an incoming Jitter matrix, we could use this object and dispense with the number
Setting a scalar as an attribute.
Similarly, if we wanted to change the mathematical operation performed by any given jit.op
object, we could send the message followed by the relevant mathematical symbol into the object's left inlet.
The second example shows a more complicated instance of using jit.op
to add values to an incoming matrix.
Using separate scalars for each plane of a matrix
This patch is similar to the first one, with the important difference that we are now working with a 4-plane matrix. This is shown by the first argument to the jit.matrix
object that generates the matrix we're using. The jit.pwindow
now shows things in color, interpreting the four planes of Jitter matrix data as separate color channels of alpha, red, green, and blue. Our jit.op
object in this example has a list of four symbols for its attribute: each symbol sets the mathematical operation for one plane of the incoming matrix. In this patch we're going to pass the first (alpha) plane through unchanged, and add numbers to each of the other planes. (You can mix and match operators like this to your heart's content.)
object feeding the right inlet of our jit.op
object takes four integers and packs them into a list. The only difference between pak
and the Max pack
object is that pak
will output a new list when any
number is changed (unlike the pack
object, which needs a new number or a in the left inlet to output a new list). The four numbers in the list generated by pak
determine the scalars for each plane of the matrix coming into the jit.op
object. In the example above, plane 0 will have nothing added to it (the first argument of the op attribute is pass). Planes 1, 2, and 3, will have 161, 26, and 254 added to them, respectively. Our jit.pwindow
object will interpret the cells of the output matrix as lovely shades of magenta (even though we see only one color, there are in fact 12 different cells in the matrix, all set to the same values).
If we decided to use only one value for the attribute of the jit.op
object above (and only used one number as a scalar), jit.op
would use that mathematical operator and scalar value for all
planes of the incoming matrix.
The third example shows a use of jit.op
on a matrix that already has relevant data stored in it:
Multiplying individual planes with scalars
• Click the message
box . The message to jit.matrix
loads a single frame of an image from a picture or QuickTime movie file into the Jitter matrix stored by the object. It will scale the picture on the disk to the dimensions of its own matrix (in this case, 320 by 240).
Clicking the button
object shows you image calibration colorbars in the jit.pwindow
on the right of the patch. In this case, our jit.op
object has its arithmetic operators set to for the alpha plane and (multiply) for the other planes. Since we're working with a 4-plane image, we set each of the scalars using a list of 4 floating-point numbers. Values of 1. in planes 1 through 3 will show you the image as it appears originally:
If you set the scalars to 1., 0., and 0., you should see the following image:
All of the planes (except plane 1) of the matrix containing the colorbars have been multiplied by 0. This will eliminate the alpha, green, and blue planes of the matrix, leaving only the red (plane 1) behind.
Setting intermediate values (such as 0., 0., 1. and 0.5) as the scalars for jit.op
will give you an image where the colorbars look different:
In this case, the alpha channel is ignored and the red channel is zeroed. The blue plane's values are all half of what they were. The green channel (plane 2) is left untouched.
Some mathematical scalars in jit.op
are expressed as floating-point numbers, and some are expressed as integers. This depends on the relevant operator (defined by the attribute), as well as the of the input matrix. Since all the examples in this tutorial use char
matrices, it makes sense to use integers when adding to them (any floating-point numbers will be truncated, as the matrix data needs to remain as integers in the range 0-255). If we were using a matrix as our input, it would make perfect sense to add floating-point numbers to it. Similarly, it's reasonable to multiply a char
matrix by a floating-point scalar (240 * 0.5 = 120, an integer). However, since the matrix output by jit.op
will still be a char matrix (see note below), you will still only get values in the range of 0-255.
If you experiment with the scalar values you will see that you can easily make some of the colorbars disappear or merge with neighboring bars. This is because the colorbars are all set to standard color values with similar ranges. If you show only one channel at a time (by setting all planes but one to 0), four of the seven bars along the top will show color.
We have demonstrated the jit.op
object can perform a great many other math operations. For a complete list of the possible operators, see the reference page, or double-click on the p
subpatch in the jit.op
and operators in this tutorial, but in fact the
When you create a jit.pwindow
object, it will appear in the Max window as 80 pixels wide by 60 pixels tall. You can change its size using its grow box
, just like many of the user interface objects in Max. If you want to change its size precisely, you can do so using its Inspector or by sending it the message followed by a width and height, in pixels:
Changing the size of a jit.pwindow
If you send a jit.pwindow
object of a certain size (in pixels) a matrix with a different size (in cells), the jit.pwindow
object will scale the incoming matrix to show you the entire matrix. If you send a very small matrix to a very large jit.pwindow
, you will see pixelation
(rectangular regions in the image where the color stays exactly the same). If you send a small jit.pwindow
a large matrix, varying degrees of detail may be lost in what you see.
in the example above, our jit.matrix
holding the colorbars had a size (specified by its list) of 320 by 240 cells, a of 4, and a type of . The jit.op
object (and most Jitter objects you will encounter) recognizes that information and adapts
to perform its calculation on the entire matrix and output a matrix of the same specifications. If we were to change the jit.matrix
object to some different size, the jit.op
object would instantly recognize the change and re-adapt. The jit.pwindow
object also adapts to the incoming matrix, but in a slightly different way. If the incoming matrix is smaller than its own dimensions, it uses duplicate data to fill all of its pixels. (This results in the pixelation effect described in the previous paragraph.) If the incoming matrix is larger than its own dimensions, it will be obliged to ignore some of the data, and will only display what it can. So, even though the jit.pwindow
objects in the Tutorial patch never match the size (in cells) of their matrix input, they do their best to adapt to the size of the jit.op
object's matrix. The jit.pwindow
in the last example shows you as much as it can of the entire matrix output by the jit.op
object, but it has to ignore every other row and column in order to fit the 320x240 matrix it receives into its own 160x120 display area.
object lets you perform mathematical operations on all the data in a Jitter matrix at once. You can perform calculations on the matrix cells in their entirety or on each plane separately. The mathematical operation that jit.op
will perform is determined by its attribute, which can be typed in as an attribute argument or provided by an message in the left inlet. For multiple-plane matrices (such as color pictures and video), you can specify the operation for each plane by providing a list of operators (e.g ), and you can provide different scalar values for each plane. In Tutorial 9
you will see how you can use a second Jitter matrix to act in place of a simple scalar.
You can set the size of a jit.pwindow
object with a message. The jit.pwindow
will do its best to adapt to the size of any matrix it receives. It will duplicate data if the incoming matrix is smaller than its dimensions, and it will ignore some data if the incoming matrix is larger than its own dimensions. Most Jitter objects do their best to adapt to the dimensions, type, and planecount of the matrix they receive. In the case of jit.op
, it does not have specified dimensions of its own, so it adapts to characteristics of the incoming matrix.
The Jitter Matrix!
Apply binary or unary operators
Output a combined list when any element changes