Scrolling a message on a building in a time lapse video
This post is about vision processing and time-lapse video modification. As you probably know I own few stationary long term time-lapse cameras, and sometimes do some tricks with this huge data collection. This time I will walk you through improvised dot matrix display made from building windows.
Source video
First you need time-lapse movie. I selected pictures captured each minute for period of about 12 hours. Scene illumination should be even (current python script limitation). Prepare dark background by cloning windows with no light from other photos. Also remaining picture should be left transparent. This way video will not be static. Name this layer “dark_background” – it will be used by python script.
Then select all windows with light. Clone if any is missing. Then create separate layer for each window. Layers should be named “x,y” starting from left bottom corner (for example “1,1” – see notes on the picture). This is time consuming process, but helps avoiding any image processing mistakes. It took me about 30 minutes to complete.
Save this as PSD file. It will be consumed by python script.
Overlay
To create overlay layer I used tool called jinx. It allows real-time control of LED’s, but I used just static output functionality.
Setup matrix size (Setup / Matrix Options) Width=12, Height=16
Setup Output (Setup / Output Devices / Add / Bitmap Export / Redirect Output to File…)
Now create scrolling message. In order to start recording press (Setup / Start Output). To stop – navigate to the same menu item.
Easy part goes here
If everything went good, quick and dirty python script will do the magic. Feel free to modify to suit your needs. Things to do and improve: color RGB windows instead of natural ones, automatic window detection, enumeration and mapping and many other things.
# -*- coding: utf-8 -*- from psd_tools import PSDImage from PIL import Image import fnmatch, os psd_file = 'data.psd' image_path = 'images1' overlay_path = 'overlay1' start_from = 620 pixel_grid_width = 12 pixel_grid_height = 16 psd_layer_dark_background = 'dark_background' def find_files(directory, pattern): file_list = [] for root, dirs, files in os.walk(directory): for basename in files: if fnmatch.fnmatch(basename, pattern): filename = os.path.join(root, basename) file_list.append(filename) return file_list def get_active_pixels(img): (overlay_w, overlay_h) = img.size active_overlay_pixels = [] for x in xrange(overlay_w): for y in xrange(overlay_h): (r,g,b) = img.getpixel((x, y)) if r+g+b > 200: pixel = str(x+1)+','+str(overlay_h-y) active_overlay_pixels.append(pixel) return active_overlay_pixels print 'Loading PSD file...' psd = PSDImage.load(psd_file) windows = {} print 'Loading layers...' for layer in psd.layers: try: window = {} window['name'] = layer.name window['x1'] = layer.bbox.x1 window['y1'] = layer.bbox.y1 window['img'] = layer.as_PIL() windows[layer.name] = window except: pass # Load source image list print 'Searching for source images...', source_image_list = find_files(image_path, '*.jpg') print 'found:', len(source_image_list) # Load overlay images print 'Searching for overlay images...', overlay_image_list = find_files(overlay_path, '*.bmp') print 'found:', len(overlay_image_list) source_index = start_from for overlay_index in overlay_image_list: img_overlay = Image.open(overlay_index) active_overlay_pixels = get_active_pixels(img_overlay) img_main = Image.open(source_image_list[source_index]) img_main.paste(windows[psd_layer_dark_background]['img'], (windows[psd_layer_dark_background]['x1'], windows[psd_layer_dark_background]['y1']), windows[psd_layer_dark_background]['img']) for pixel in active_overlay_pixels: try: img_main.paste(windows[pixel]['img'], (windows[pixel]['x1'], windows[pixel]['y1']), windows[pixel]['img']) except: pass os.remove(source_image_list[source_index]) img_main.save(source_image_list[source_index]) source_index += 1 print source_index