Imagebusters: Image Filter I

In this episode of Imgbusters we're going to look at how to do this effect:

image

Notice how the pixels are smeared to the right?

If we look closely we notice that pixels are "smeared" horizontally. We can also see that the direction is probably left to right rather than right to left. This effect seems to pick a color and then repeat the same color for a few pixels to the right. Let's try this:

from PIL import Image
import math
import sys

im = Image.open(sys.argv[1]).convert("RGB")
w, h = im.size

This code just loads the images using PIL.

y = 0
x = 0

while y < h:
	x = 0
	p = im.getpixel((0,y))

	while x < w:
		p_ = im.getpixel((x,y))
		i = 0
		while x < w and i < 8:
			output.putpixel((x,y), p_)
			i += 1
			x += 1
	y += 1
image

Looks more like "pixelize". It's a neat effect, but not what we want!

Well... that didn't really work. What the effect we want is actually doing is group pixels of the same colour together and then colour them exactly the same way. In essence, to get this effect we need to run from left to right remembering the colour of a reference pixel then check if the next pixel has about the same colour, if yes, we replace that pixel with the colour from the earlier pixel, if not we keep the pixel and then repeat. Let's try it:

def pos(rgb):
	return int((math.sqrt(rgb[0]*rgb[0] + 
		rgb[1]*rgb[1] + 
		rgb[2]*rgb[2])/442.0)*255.0)

x = 0
y = 0
d = 64

while y < h:
	x = 0
	p = im.getpixel((0,y))

	while x < w:
		p_ = im.getpixel((x,y))

		if abs(pos(p) - pos(p_)) > d:
			p = p_

		output.putpixel((x,y), p)
		x += 1

	y += 1
image

Now we overdid it. It extends too much to the right.

What's the mysterious pos function? Simple. An RGB value can be considered a point in a 3D space (x,y,z) and the distance from such a point (x,y,z) to (0,0,0) is sqrt $ x*x + y*y + z*z. The maximum distance is about 442. /442.0*255.0 scales the distance to 0..255. To prevent extending too far to the right we need to slowly fade out. We can do this by introducing a counter to count how many pixels we've replaced or we constantly decrease d. d defines how much a pixel can differ in colour from the reference pixel. You can experiment by using different values for d.

x = 0
y = 0
d = 64
d_ = d

while y < h:
	x = 0
	p = im.getpixel((0,y))

	while x < w:
		p_ = im.getpixel((x,y))

		if abs(pos(p) - pos(p_)) > d_:
			p = p_
			d_ = d
		else:
			d_ /= 1.1

		output.putpixel((x,y), p)
		x += 1

	y += 1

... and here we are!