Tutorial 3: Math Operations on a Matrix
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.
The first example shows a jit.matrix object hooked up to a jit.op whose output is viewable by a jit.pwindow object. Every time you change the number box hooked up to the right inlet of the jit.op object a bang
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 (0
) to white (255
).
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 and jit.pwindow objects together and bypass the jit.op, you would see a black image, no matter how many times you send a bang
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 objects.
Operation @-Sign
We said above that the jit.op object adds 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 argument to its op
attribute (written @op
in the object). The argument following @op
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 op
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).
The scalar value can also be supplied as a constant by using the val
attribute of jit.op. For example, if we always wanted to add 134
to all the cells of an incoming Jitter matrix, we could use this object and dispense with the number box:
Similarly, if we wanted to change the mathematical operation performed by any given jit.op object, we could send the message op
followed by the relevant mathematical symbol into the object's left inlet.
Math Operations on Multiple Planes of Data
The second example shows a more complicated instance of using jit.op to add values to an incoming 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 op
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.)
The pak 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 bang
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).****:::info
Important Note: If we decided to use only one value for the op
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.
:::
Modifying the Colors in an Image
The third example shows a use of jit.op on a matrix that already has relevant data stored in it:
- Click the message box
importmovie``colorbars.pict
. Theimportmovie
message to jit.matrix loads a single frame of an image from a picture or 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 pass
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.
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 +
and *
operators in this tutorial, but in fact 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 pop_list
subpatch in the jit.op help file.
Sizing it Up
When you create a jit.pwindow object, it will appear in the Max Console 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 size
message followed by a width and height, in pixels:
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.
Summary
The jit.op 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 op
attribute, which can be typed in as an @op``[operator]
attribute argument or provided by an op [operator]
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 . op pass * * *
), 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 size [width] [height]
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.