Forums > Jitter

Ordered dithering in a jitter matrix

Apr 17 2012 | 11:50 pm


I'm trying to effect video in a manner similiar to the "1-bit camera" app for iphone. As you can hopefully see from this photo of yoshi eating a burrito, all the gray values have been replaced with a black and white dither to approximate the tonal range.

Wikipedia says that you can achieve such a thing with psuedocode: (

for each y
for each x
oldpixel := pixel[x][y] + threshold_map_4x4[x mod 4][y mod 4]
newpixel := find_closest_palette_color(oldpixel)
pixel[x][y] := newpixel

I'm certain that I can do this in max, but I haven't figured out where to begin. I've been able to achieve black and white in my video through the planemap command, but I'm not sure how to go about resampling the matrix. I would really appreciate it if someone could point me in the right direction.



  1. IMG1287.PNG


Apr 18 2012 | 4:42 am

There are probably more elegant ways of doing this, but it does seem to work. This article was quite an interesting read.

I threw in random dithering as well.

You should generally use jit.rgb2luma to go from color to grayscale.

-- Pasted Max Patch, click to expand. --

Apr 18 2012 | 5:43 am

awesome! thanks.

i found that same article on wikipedia. it really helped me understand the math behind what was going on. However, I probably wouldn’t have gotten as far as this without help. I’m going to try and implement my own version based on what you’ve shown me with an 8×8 and a larger resolution video. Hopefully I can learn how to do that from your examples, but if I have any questions I’ll let you know.

Apr 18 2012 | 6:28 am

It’s definitely doable – I just mocked it up 8×8 with 640×480 input, but I’ll let you hack on yours.

You can also raise/lower the [jit.op @op *] value to play with contrast in the resulting output image.

Note that there was technically a bug in my first post — the 4×4 should have been * 17 (I think) to produce the "proper" threshold. * 10 does work, but perhaps not as the algorithm’s creator intended.

Apr 18 2012 | 2:51 pm

So for some reason, it’s creating a lot of vertical line artifacts. I don’t think it’s working correctly, because it should (in theory) have a bunch of plus sign looking artifacts.

Stuff I still haven’t figured out:

-What is the difference between a LONG matrix and a CHAR matrix and why are we switching between them?

-Why are we boosting the signal with the [jit.op @op * @val 10] again? I attached a number object to play with the multiplication, but I’m still not sure why it’s having the effect that it does.

-So i’m filling my 8 x 8 matrix with 1 plane of data (ok), but I’m not exactly sure what the [jit.repos] is doing with the [jit.noise] object. Shouldn’t we be positioning the bayer matrix in the [jit.qt] matrix and comparing those values somehow?

The way that I understand this is supposed to work is that you would cycle through the bayer 8×8 by taking a pixel’s xy value mod 8. Then you compare the movie pixel’s value with the value in that part of the bayer matrix and perform the [>] but it doesn’t seem like that’s what is going on exactly…

-- Pasted Max Patch, click to expand. --

  1. 1bit2.maxpat
Apr 18 2012 | 4:00 pm

so i did some digging, and a have a picture that is a little clearer. banging the expression twice *should* be working, according to this. We have a pre-bayer mapped matrix to compare the incoming data to. BUT, the incoming data (or the bayer) needs to be scaled somehow in order for the comparison to work.

If this weren’t jitter data, i’d use [scale 1 4 0 255] and call it a day, but it obviously doesn’t work on a matrix.

here is the model I worked in to see what was actually going on:

-- Pasted Max Patch, click to expand. --

Apr 18 2012 | 6:17 pm

The 8×8 filter works fine for me.

Make sure that the dimensions of your jit.window are the same aspect ratio as the content you’re dithering. So if it’s 16:9 footage then you use a 16:9 jit.window. Your have your ratio at 1080×720 – I believe that’s a typo, but it accounts for the banding in the output window. When I change things to 1280×720 and adjust the output window to 16:9 it works.

[jit.op @op*] is how you’d scale a matrix. You’re thinking of scaling the bayer matrix – I was scaling the input matrix so that its values are comparable to the bayer.

Here’s my 8×8 patch with more comments. See if that helps.

-- Pasted Max Patch, click to expand. --

Apr 19 2012 | 4:37 pm

oh! you’re right. I always forget that half HD is 1280. I think I had one other typo, and my 8×8 was a little different than yours (yours was better). I think I’m going to experiment with some different dither matrixes to see what else I can come up with now, but I should be good from here on out.

Awesome! Thanks for all the help!

May 04 2012 | 7:52 pm

Here is the final patch! It works great. I added it to a patch/film that I worked on that cycles through a 6 second clip and increases the dimensions each loop.

-- Pasted Max Patch, click to expand. --

I’ve used the Record object before to get a copy of the film, but it tends to leave nasty artifacts. Is there a way that I could run through each frame individually and save them as tiffs or jpegs instead of trying to do it in real time? That way I could save it as a movie later and edit the sound back in afterwards. Is that possible?

May 04 2012 | 9:31 pm

You can send the "exportimage" message to a jit.matrix, or try altering the codec you are using with jit.qt.record.

May 08 2012 | 2:34 pm

Figured out how to make it churn out frames. Here is the finished piece! Thanks for you help Jesse!

Viewing 11 posts - 1 through 11 (of 11 total)

Forums > Jitter