Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rotated picture looks like it's missing pixels

I was playing around with PIL and transformation matrices to understand what's behind simple 2D image manipulation.

In my attempt to rotate an image as "low level" as possible (that is, not using any rotate(degrees) function, but doing the math) I decided to rotate every pixel of the image using a clockwise rotation matrix:

enter image description here

The rotation went fine, but the image now looks like it's missing some pixels.

Original image, painted on a 435x353 black background:

enter image description here

Rotated 45° clockwise and moved to the right by 300 pixels:

enter image description here

Oddly enough, the issue doesn't occur when rotating the picture 90° clockwise (and moving 400 px to the right):

enter image description here

What could be causing this? Using Image.Image.rotate works just fine, so I guess the issue resides on my code. It's worth mentioning the original picture has a transparent background, which was lost in the compression when uploaded here. However, I've done the exact same operation to a jpeg (non-transparent) image and the result was the same.

Code used to do the rotation:

import Image, ImageDraw
from scipy import misc
import math

WHITE = (255,255,255)
BLACK = (0,0,0)
W, H = 435, 353
im = Image.new('RGBA', (W, H), BLACK)
draw = ImageDraw.Draw(im)
bitmap = misc.imread('Image.png')

def affine_t(x, y, a, b, c, d, e, f):
    """Returns ((a, b), (c, d))*((x), (y)) + ((e), (f)).""" 
    return a*x + b*y + e, c*x + d*y + f

def crotate(x, y, r):
    """Rotate (x, y) clockwise by r radians."""
    # And move 300 px to the right for this example
    return affine_t(
        x, y, math.cos(-r), math.sin(-r), -math.sin(-r), math.cos(-r), 300, 0
    )

x, y = 0, 0
angle = math.pi/4
for row in bitmap:
    for pt in row:
        draw.point([crotate(x, y, angle),],fill=tuple(pt))
        x+= 1
    x = 0
    y += 1

im.save('out.png')
like image 327
Alex Avatar asked Feb 07 '14 21:02

Alex


1 Answers

For each destination pixel you need to calculate the source pixel, but NOT the vice versa. Due to roundings you have several source pixels mapped into the same destination pixel. That's why you cannot actually obtain good 45° rotation without interpolation. My suggestion is actually a nearest neighbour interpolation.

like image 163
Mikhail Avatar answered Oct 03 '22 22:10

Mikhail