Forums > Jitter

Ordered dithering in a jitter matrix

April 17, 2012 | 11:50 pm

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!

[attachment=191400,3735]

Attachments:
  1. IMG1287.PNG

April 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.

http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT

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. –

April 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.


April 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.


April 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. –
Attachments:
  1. 1bit2.maxpat

April 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. –

April 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. –

April 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 4, 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 4, 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 8, 2012 | 2:34 pm

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

vimeo.com/41754950


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