Image Warping Software

In the dark, dank, dismal, and pre-digital ages people would manually warp polaroid prints. I can see the attraction but it is one-off and rather unpredictable. We can do better now. 

The algorithm is rather simple:

for each point in the image:

       calculate the warped location of the point and put the value of the image from that point at the current location

Without much further ado

Starting from my headshot:

We can distort it to make me look like an alien

or a surrealist (I actually like this one a lot better):

(I can't for the life of me remember the name of the work that inspired this - it's a pen sketch of a famous critic)

Anyway here's the python code - which is surprisingly short.


#  (c) 2024 Treadco
#  read an image and a "warpmap" then output a distorted image.
#  meant to mimic the mechanical distortion of an SX70 polaroid image
#  warpmap defines the geometry of the distortion
#       height,cx,cy 
#   map the coordinate to a new position and put the color of that position in the coordinates.

import numpy as np
import sys,os
import math
from PIL import Image
from PIL import ImageChops
from PIL import ImageColor
#from pylab import * # not used
def warper( x,y, heights, centers):
    xx = x
    yy = y
    for i in range(0, len(heights)): #yes I could use a tuple of heights,.. but this is OK
        dx = xx - centers[i][0]
        dy = yy - centers[i][1]
        dr = dx*dx + dy*dy
        dr = math.sqrt(dr)
#        if dr < 0.001:
#            continue
        x += dx*heights[i]/(dr+abs(heights[i]))
        y += dy*heights[i]/(dr+abs(heights[i]))
#was        y += dy*heights[i]/(dr+1.)
    return (x,y)

def main():
      image =[1])
    except IndexError:
      print("Could not open the input \nUsage warp inputfile warpmap outputname")

      pushmap = open(sys.argv[2])
    except IndexError:
      print("Could not open the input \nUsage warp inputfile warpmap outputname")

    if len( sys.argv) < 4:
        outputfilename = "output.jpg"
        outputfilename = sys.argv[3]
    height = []
    centers = []
    for line in pushmap:
        s = line.split()
        height.append( float(s[0]))
        centers.append( (float(s[1]), float(s[2])) )

    iarray = np.array(image.convert("RGB"))
    nx = iarray.shape[0]
    ny = iarray.shape[1]
#    for i in range(0,int(centers[0][0])):
#        for j in range(0,int(centers[0][1])):
#            print( str(i)+' '+str(j)+' '+str(warper(i,j, height, centers) ) )
#    sys.exit()
    outarray = np.array("RGB",(ny,nx),ImageColor.getcolor("white","RGB")))
    print( outarray.shape, iarray.shape)
    for i in range(0,nx):
        for j in range(0,ny):
            dc = warper(i,j,height,centers)
            ii = int(dc[0])
            jj = int(dc[1])
            if( ii < 0 or ii >= nx):
            if( jj < 0 or jj >= ny):
            outarray[i,j] = iarray[ii,jj]
    inew = Image.fromarray( outarray)

There's more boilerplate about opening files and syntactical sugar than lines of code to implement the algorithm. That's rare in python.