How to show time in an image ?

We could liquify clocks or just use timelapse-video. But we can do this better in one frame.

We can just take many pictures of the same subject and capture them at different times.

This is the image we want:

All we need is a small python-script.

What it should do:

  • process JPG-files
  • clculate column-widths
    • Alert, if there are more images than pixel
    • even distribution of leftover pixels
    • Tell us how big a single slice will be.
  • (optional) process subdirectories recursively
  • input ans output-images are the same size
    • Alert if the image size varies.

timeslice-helper.py

Parameter:

  • import-folder path
  • booloean, if subdirectories should be processed (default: false)
  • (optional) output-filename

We start by checking if the given directory exists and process the other arguments. If anything is unexpected, we stop and alert the user.

# Main
if __name__ == "__main__":

    path = sys.argv[1]
    path = os.path.abspath(path)
    if not os.path.isdir(path):
        sys.exit("Path is not a directory or does not exist.")
    rec = sys.argv[2]
    name = ""
    #
    if len(sys.argv) > 3:
        name = sys.argv[3]
    if rec == "True":
        get_dir(path)
    else:
        process_dir(path, name)

Single folders will always only be processed by process_dir()

Lets collect the relevant directories:

def get_dir(path):
    dir = []
    for currentpath, folders, files in os.walk(path):
        for folder in folders:
            print(os.path.join(currentpath, folder))
            dir.append(os.path.join(currentpath, folder))

    for d in dir:
        print("Processing " + str(d))
        process_dir(d, "")

First we set up an array, which will be our ToDo-list.

We add every subfolder to this list and print it out, so we could cancel before we start processing.

After this, a single directory ist given to process_dir() and we pint out what we are currently processing.

def process_dir(d, name):
    files = []
    images = []

    # Load Image (JPEG/JPG needs libjpeg to load)
    for file_name in sorted(glob.iglob(d + '/*.jpg', recursive=True)):
        files.append(file_name)
    # check if folder contains no images
    if not files:
        print("This folder contains no images.")
        return

    if check_files(files):
        print("All images have the same size.")
    else:
        print("Not all images have the same size.")
        return

    print("Processing " + str(len(files)) + " images.")

    # Slice and save
    new = slice(files)
    print("Saving image...")
    filename = ""
    if name == "":
        filename = str(d).replace("/", '_')
    else:
        filename = name.replace("/", '_')
    p = "output/" + filename.replace("\\", '_')
    save_image(new, p + '.png')
    print(p + ".png is ready.")

First we collect all image files. If a directory doesn’t contain any images, we just carry on with the ToDo-list.

def check_files(files):
    width, height = open_image(files[0]).size
    if len(files) > width:
        print("To many images. Each slice would be smaller than 1px.")
        return
    print("The output image will be " + str(width) + "x" + str(height))
    for x in files:
        w, h = open_image(x).size
        if w != width or h != height:
            return False
    return True

We make sure, that all pictures in a series have the same aspect ratio. If there are more pictures than available pixel (width of a picture) we can’t proceed and will inform te user about the problem. (Otherwise the slices would be smaller than 1px).

After checking all files, we can begin processing. All process_dir does afer processing ist taking care of the filename and saving of the final image.

If no output-filename is given, we will use the name of the directory.

slice() – Where the magic happens

def slice(files):
    # Get size
    width, height = open_image(files[0]).size

    # calculate slices
    s = int((width / len(files)))
    print("Each slice will be " + str(s) + "pixel wide.")
    slices = []
    for x in files:
        slices.append(s)
    if (s * len(files)) < width:
        print("Distributing lextover columns.")
        leftover = width - (s * len(files))
        print(str(leftover))
        for i in range((len(files) - 1), 0, -1):
            if leftover > 0:
                slices[i] = slices[i] + 1
                leftover = leftover - 1
                print("Image " + str(i) + " will be 1px bigger.")
        # slices[len(files) - 1] = slices[len(files) - 1] + (width - (s * len(files)))
        print("Leftover pixel-columns were distributed evenly among the leftmost slices.")

    # Create new Image and a Pixel Map
    new = create_image(width, height)
    pixels = new.load()

    current = 0
    # column
    c = 0
    for row in slices:
        pic = open_image(files
) print("Slicing image " + str(c + 1) + " - " + str(slices
) + " Pixels wide.") for i in range(current, current + row): for j in range(height): # print(str(c+1) + " - " + str(i) + " - " + str(j)) # Set Pixel in new image pixels[i, j] = get_pixel(pic, i, j) c = c + 1 current = current + row # Return new image return

We need to calculate how big our slices will be. Leftover pixels will be evenly distributedby adding single pixels to each slice starting left. After creating a new image, we walk through all pixel-columns und check where these pixels will come from. These pixels will be copied from the origin-image.

This picture is generated from 2000 single pictures, which were shot with a RaspberryPi over the course of 13 hours.

You can find the whole script on github to try it whith your own images.