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.

#!/usr/bin/python3
#  (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():
    try:
      image = Image.open(sys.argv[1])
    except IndexError:
      print("Could not open the input \nUsage warp inputfile warpmap outputname")
      sys.exit()

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

    if len( sys.argv) < 4:
        outputfilename = "output.jpg"
    else:
        outputfilename = sys.argv[3]
    
    height = []
    centers = []
    for line in pushmap:
        s = line.split()
        print(s)
        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(Image.new("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):
                continue
            if( jj < 0 or jj >= ny):
                continue
            outarray[i,j] = iarray[ii,jj]
    inew = Image.fromarray( outarray)
    inew.save(outputfilename)
main()


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

Quad Panels for Instagram Posting

I’ve set up another simple python program to produce an image made of four others. This is a step to fancy animations. Apparently panels of four things are more eye-catching than panels of one thing. It’s even more pronounced if only one panel is animated. That’ll take OpenCV to do automatically.

For what it’s worth, if you aren’t familiar with programming note how I’ve used a function to simplify the code. Once I read the documentation for OpenCV I’ll add a function to generate each frame. This function will look like main() but without any hard-wired constants.

#!/usr/bin/python3
#
# (c) 2023 Treadco
#

import numpy as np
import sys
from PIL import Image

# return an image scaled to fit into xw,yw with background color
# defaults are for a 2048,2048 instagram image of 4 quadrants
def fitted_image(  an_image,xw=1024,yw=1024, background=(255,255,255)):
    iarray = np.array( an_image.convert("RGB") )
    nx = iarray.shape[0]
    ny = iarray.shape[1]
    inew = Image.new( "RGB", [xw,yw], (255,255,255))
   
    if nx > ny :
        simage = an_image.resize([ int((xw*ny)/nx),yw ])
        inew.paste( simage, [int((xw-int((xw*ny)/nx))/2),0])
    else:
        simage = an_image.resize([xw, int((yw*nx)/ny) ])
        inew.paste( simage, [0,int((yw-int((yw*nx)/ny))/2)])
    return inew

def main():
    if len( sys.argv) < 5:
      print("Could not open the input \nUsage to_quad ul ur ll lr outputname")
      sys.exit()

    if len( sys.argv) < 6:
        outputfilename = "output.jpg"
    else: 
        outputfilename = sys.argv[5]

    inew = Image.new( "RGB", [2048,2048], (255,255,255))
    inew.paste( fitted_image(Image.open(sys.argv[1])), [0,0])
    inew.paste( fitted_image(Image.open(sys.argv[2])), [1024,0])
    inew.paste( fitted_image(Image.open(sys.argv[3])), [0,1024])
    inew.paste( fitted_image(Image.open(sys.argv[4])), [1024,1024])
       

    inew.save(outputfilename)
    

main()

Embedding an image for Instagram

This is a short python program which uses PIL (Python imaging library or Pillow) to perform its magic. Cut and paste or email me for the source (Or even better sign up for my page and I’ll use that email).

It takes an input image and places it in a 2048×2048 square with a white background.

#!/usr/bin/python3
#
#  (c) 2023 Treadco
#

import numpy as np
import sys
from PIL import Image

def main():
    try:
      image = Image.open(sys.argv[1])
    except IndexError:
      print("Could not open the input \nUsage to_instagram inputfile outputname")
      sys.exit()

    if len( sys.argv) < 3:
        outputfilename = "output.jpg"
    else: 
        outputfilename = sys.argv[2]

    iarray = np.array(image.convert("RGB"))
    nx = iarray.shape[0]
    ny = iarray.shape[1]
    inew = Image.new( "RGB", [2048,2048], (255,255,255))
   
    if nx > ny :
        simage = image.resize([ int((2048.*ny)/nx),2048 ])
        inew.paste( simage, [int((2048-int((2048.*ny)/nx))/2),0])
    else:
        simage = image.resize([2048, int((2048.*nx)/ny) ])
        inew.paste( simage, [0,int((2048-int((2048.*nx)/ny))/2)])

    inew.save(outputfilename)
    
 

main()

A Kickstarter – get in on it!

I’ve pre-released a kickstarter to fund a photobook.

https://www.kickstarter.com/projects/photorob/wildlife-glimpsed-an-artbook-of-wild-animals-and-plants

About 50 pages of photos and commentary – including where to see these animals and some of their natural history.

A Mascot’s Tale

I was found lying on a country road, in Cornwall, on my face and in the mud.

Not a very dignified pose, I’d say, certainly below the dignity of any proper animal.

So a kind stranger propped me on a wall in front of a house to see if anyone claimed me. They didn’t so he took me home, cleaned me up, and adopted me as his mascot. 

I must have been too fierce for anyone else.

Here I am resting after my journey.

My new people took me on adventures like the South Coast footpath:

and the sea:

This is by a town called “Mazel” and spelled “Mousehole.” Isn’t that a funny name? I didn’t see any mice.

I had a cold drink on one hot afternoon. That ginger-ale tasted so good. It’s no wonder that it’s by appointment to the Prince of Wales.

They also took me up into the hills – which I now know aren’t very high in Cornwall – but they seemed it at the time.

Finally they took me to the airport. Here I’m watching the activity on the runway.

and here I’m posing in front of the plane we flew on.

It was a good thing they didn’t stuff me in the luggage because that didn’t fly with us.

I’ll add more about my adventures in my new home later.

Looking forward to 2022

2021 has been a heck of a year. Lots of family changes, limited mobility due to Covid, and a difficult combination of on-line and (mandatory) in person teaching.

To the good:

  • I’m now Emeritus. I was more than a little worried about getting this status, despite a rather decent number of well-regarded publications (>200) and a respectable funding record (Something like $8Million total grant funding as PI, MPI, or Co-PI in the last 20 years).
  • We’re healthy and in a financially sound position.
  • We’re vaccinated against that blasted virus. Contrary to right-wing hype, there weren’t side effects and we’re not magnetic or carrying microchips or infecting people with mysterious shed particles.
  • I helped my ultimate (or possibly penultimate) PhD student to finish their dissertation and graduate.
  • Our oldest cat didn’t have to cross the rainbow bridge. We finally found what was making him sick, and … well … chemotherapy works wonders.

On the other hand, there are still things to do, ranging from long-term research projects to getting our documents in order.

Still, between using an RV (travel trailer – more in a later post) and vaccination we should be able to get around. I hope we can even visit our UK family and possibly get onto continental Europe.

A few photos.

Ruby Throated Hummingbird

On the wing

Ruby throated hummingbirds are one of the more common ones in the eastern united states. We decided to put up a feeder and after a week or so this pretty little female decided to visit. She moves quickly enough that the “on the wing” photographs are just slightly blurred. But when she stopped to feed, it was another story.

I took these images with my Sony A7III using the 600mm lens at f6.3, 1/800s, and 2000 asa (equivalent speed). The trick is to sit in a chair and wait. If they’re spooked move further away, and then after a while move closer. These birds, once they realize that you’re not a threat, become bold. They’ll buzz you to remind you to fill up the feeder, which is an interesting experience. The feed is one part sugar to four parts water by volume without added food color.

Anoles

Anoles are not Chameleons

Anoles are one of the more common lizards in the American south. We have them on our porch. This is the dominant male. He’s got one eye on me, but is really focused on another male who was interested in this high status site.

I couldn’t get a great picture of their fight so this will have to do.

Redbud Panorama

spring in the Alabama woods

Redbuds are one of the more common trees in southern wooded areas and scrub. Their one of the first trees to flower – a harbinger of spring. The heart-shaped leaves will soon follow. Later, near the end of summer, they’ll have seed pods.

Two Deer.

A buck (behind) and doe near Weiss lake

I’ve been practicing with the local wildlife. Finally figured out how to implement “back button” focusing on the Sony A7III, which helps enormously with a telephoto. No more shifting focus to the wrong piece of grass.

The other big trick it to be non-threatening. I take a small chair and sit. The deer watch for a while and then go back to deer stuff (eating mostly).

The sony 600mm lens is pretty good as this detail shows.