Skip to content
Adrian von Gegerfelt edited this page Mar 13, 2015 · 15 revisions

#[001] van Gogh by von Geg

##About Exhibition 1, yay! 🎉 my first go at vart. Seeing Jenn's examples with drawing on top of images, I somehow immediately thought of Vincent van Gogh and his nervous/erratic brush-stroke techniques of his most known paintings "Starry Night", "The Sower", and his self-portraits.

I thought I could easily turn an image into a series of brush strokes using HTML Canvas. Canvas can also save the image to disk with one command.

TD;DR It's done and you can play with it ➡️ here.

##Process #####Load image data First I need to load the image into the canvas. Simples: context.drawImage(myImage,0,0) then this nifty guy that gives me an array of all pixels of the canvas: context.getImageData(0, 0, w, h). Unfortunately, as the values come as RGBA, even a 32*32 image will give me an array of 4096 (red, green, blue, alpha, red, green, blue, alpha etc...).

Also, it's a one-dimensional array. So I have to first convert this back into a two-dimensional array so I can get [x,y] coordinates, then I can go on and...

####Group squares of pixels into chunks Using an annoying amount of loops, I divide the image into chunks of 10*10 pixels (variable). One chunk will later become one stroke's starting point with one colour. Now for every chunk, I take all the pixels and...

####Get the average colour Basically, adding up all the RGB values then dividing them with the number of pixels in the chunk. This gives us a beautiful colour that represents the average colour in the chunk mud.

It turns out that by the nature of RGB, averaging the colours just gives me 💩. What I really need to do is...

####Get the dominant colour in each chunk Now, to know the dominant colour, I need to weigh the colour of each pixel to every other pixel's colour and create a new array of that. I have to give my thanks to boazsender and his PHP script. The magic lies in this:

computedDistsances = computedDistsances + (  Math.sqrt( Math.pow(rgb2.r - rgb1.r,2) + Math.pow(rgb2.g - rgb1.g,2) + Math.pow(rgb2.b - rgb1.b,2) )  );

which gives me a weighted value of... value. Now let's think about this... I'm first going through each pixel and divide it into chunks, then, for every chunk, I'm comparing every frigging pixel to every frigging other pixel. Remember that a 3232 pixel image (a traditional icon) has 1024 pixels. What about an image that is 1024768? That's 786.432 pixels. Over and over again. No wonder my browser runs like a dog! Actually, it freezes until the operations are done! 😮 This is what native apps are for, right? Wrong! Because we have something called:

####Web Workers These guys "provide a simple means for web content to run scripts in background threads. The worker thread can perform tasks without interfering with the user interface." So I move half my code into a Worker, and it will run in a separate thread, while the main interface responds to scrolling, and even shows a progress bar (we totally live in the future).

My worker script finally returns a new, much smaller, two-dimensional array, with all new [x,y] coordinates and RGB values, and I paint them all to the canvas. Slightly random start and finish coordinates, of course, to transcend the uncanny valley of digital art.

Piece of 🍰!

Then I realised that test.jpg won't due because... well... my three visitors per decade would want to try their own image, right? So I need PHP to enable image uploading - security and all that. At least I can use this PHP script for future vartworks!

I also decided to use bootstrap for layout because I'm a design fascist and can't work with barebones HTML, yet I didn't want to spend time on design. I also can't stand the default input type="file" so I found a bootstrap plugin that makes them pretty. Which requires JQuery. Heck, I need AJAX anyway, so JQuery gives me at least two uses.

TD;DR It's done and you can play with it ➡️ here.

##How to make it better

  • Reduce image size first, then blow it up. With these huge pixels I might not need chunks at all!
  • Stroke angle as option
  • Stroke thickness as option (two options? so un-Apple-like)
  • Add rough outline to strokes to look more brush-y
  • Add background pattern and reduce opacity of strokes to make vartwork look more painting-y
  • Make brush strokes follow the path of the object, just like van Gogh's real works. But that would definitely be over-kill for this project!
Clone this wiki locally