Ordered dithering in a jitter matrix

Peechiz's icon

Hello!

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: (http://en.wikipedia.org/wiki/Ordered_dithering)

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.

Thanks!

3735.IMG1287.PNG
PNG
Jesse's icon

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.

Max Patch
Copy patch and select New From Clipboard in Max.

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

Peechiz's icon

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 8x8 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.

Jesse's icon

It's definitely doable - I just mocked it up 8x8 with 640x480 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 4x4 should have been * 17 (I think) to produce the "proper" threshold. * 10 does work, but perhaps not as the algorithm's creator intended.

Peechiz's icon

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?

Max Patch
Copy patch and select New From Clipboard in Max.

The way that I understand this is supposed to work is that you would cycle through the bayer 8x8 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...

3736.1bit2.maxpat
Max Patch
Peechiz's icon

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.

Max Patch
Copy patch and select New From Clipboard in Max.

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

Jesse's icon

The 8x8 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 1080x720 - I believe that's a typo, but it accounts for the banding in the output window. When I change things to 1280x720 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.

Max Patch
Copy patch and select New From Clipboard in Max.

Here's my 8x8 patch with more comments. See if that helps.

Peechiz's icon

oh! you're right. I always forget that half HD is 1280. I think I had one other typo, and my 8x8 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!

Peechiz's icon
Max Patch
Copy patch and select New From Clipboard in Max.

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.

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?

Jesse's icon

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

Peechiz's icon

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

vimeo.com/41754950