This whole album is such a ride but this song especially has given me some cool visions/visuals that I wanted to recreate. Knowing in my head what I wanted, I had a feeling I was going to end up using After Effects and honestly dreaded the thought of masking and moving every pixel column so I set about trying to figure out a way if I could use my favorite tool (you guessed it: Python!) to do the job.
Turns out there's a great open source module for python that deals with Image handling called Pillow. I'll link it in the references below. What was particularly handy was being able to pull out pixel colors by x,y coordinates and construct images in a similar fashion.
So quick summary of the code. You input how long you want your clip to be at whatever fps you give it and returns that many frames. It'll pull an image you give it and drop columns in an almost even distribution (I let the viewer soak in the full image at the end for 5 seconds). Most of the constants are calculated using your image size. But the important ones are the column tracker and the active dropping. The column tracker is initialized as a list of every column. Every time the script realizes its time to drop a column, it'll move it from that list to the active dropping list where it'll stay until the column has reached the bottom of the image. Dropping the column is a matter of copying pixels from the original picture to the new location on the new frame in the right order. The frame object is continuously built upon rather than creating a new picture for each frame.
Anyways, this is my first time doing something like this with Python. Truthfully, didn't even know it was possible, just goes to show how versatile this coding language is and reinforces my belief that everyone should have this tool handy.
Code
importosfromPILimport Image
importrandomfrommathimport floor
#################################################################################################### User Defined Variables###################################################################################################
image ='in_rainbows_wallpaper.png'#image = 'In_Rainbows_Official_Cover.jpg'
track_length =3*60+58#4 min 8 sec
fps =24
crawl_length =10*fps
#################################################################################################### Directory Construction###################################################################################################
new_dir = image.split('.',1)[0]+'/'if os.path.isdir(new_dir):
for filename in os.listdir(new_dir):
file_path = os.path.join(new_dir, filename)
try:
if os.path.isfile(file_path) or os.path.islink(file_path):
os.unlink(file_path)
elif os.path.isdir(file_path):
shutil.rmtree(file_path)
exceptExceptionas e:
print('Failed to delete %s. Reason: %s'% (file_path, e))
else:
os.mkdir(new_dir)
#################################################################################################### Initializing Constants###################################################################################################
cover = Image.open(image)
W,H = cover.size
total_frames = track_length*fps
column_drop_rate = (total_frames-crawl_length-5*fps)/W
print(column_drop_rate)
pixel_rate =round(H/crawl_length)
row_tracker = [0]*W
column_tracker =list(range(W))
active_dropping = []
drop_tracker =0
frame = Image.new('RGB', (W,H))
#################################################################################################### Image Construction###################################################################################################for i inrange(total_frames):
if (i/column_drop_rate)>drop_tracker and column_tracker:
summoned_column = random.choice(column_tracker) #off to the guillotine!
column_tracker.remove(summoned_column)
active_dropping.append(summoned_column)
drop_tracker +=1for column in active_dropping:
row_tracker[column] += pixel_rate
if row_tracker[column] >= H-1:
row_tracker[column] = H-1
active_dropping.remove(column)
for pixel inrange(row_tracker[column]+1):
pixel_color = cover.getpixel((column,H-pixel-1))
frame.putpixel((column,row_tracker[column]-pixel),pixel_color)
frame.save(new_dir +'Frame'+str(i).zfill(5)+'.jpg') #jpg is fastest