The article "Final cut: films condensed into a single frame – in pictures" in The Guardian tells us about the photographer Jason Shulman who captured the entire duration of a movie in a single image.
Inspired by the article, I decided to reproduce results, but computationally. I didn't expose the movies in front of a camera. Instead, I wrote a small program in python which stacks all frames from the given video file into a single picture. The process is essentially an equivalent to a single shot with an ultra-long exposure of the film.
The resulted picture often looks like a gray rectangle and requires color correction (playing a bit with levels in Gimp or Photoshop).
The source code of my utility. The OpenCV library automated all the hard work. The program is just a sum of matrixes (frames / images).
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import click
from clickclick import AliasedGroup
import cv2
import numpy as np
from tqdm import tqdm
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
@click.group(cls=AliasedGroup, context_settings=CONTEXT_SETTINGS)
def cli():
"""
Stacks all video frames into one picture.
Idea: https://www.theguardian.com/artanddesign/gallery/2017/may/16/jason-shulman-films-condensed-into-a-single-photo-frame-in-pictures
"""
@cli.command(help="stacks all video frames into one picture, technically converts video into one picture")
@click.argument("film")
@click.argument("output-image")
@click.argument("scale", type=float)
def convert(film, output_image, scale):
# Opens the Video file
capture = cv2.VideoCapture(film)
stacked_image = None
frames = 0
total_frames = capture.get(cv2.CAP_PROP_FRAME_COUNT)
frames_limit = int(total_frames * 0.9)
with tqdm(total=frames_limit) as progress_bar:
while capture.isOpened() and frames < frames_limit: # frames limit - we don't want to have titles in the final image
ret, image = capture.read()
if not ret:
break
image = cv2.resize(image, None, fx=scale, fy=scale)
image = image.astype(np.float32) / 255
if stacked_image is None:
stacked_image = image
else:
stacked_image += image
frames += 1
progress_bar.update(1)
stacked_image /= frames
stacked_image = (stacked_image*255).astype(np.uint8)
cv2.imwrite(output_image, stacked_image)
capture.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
cli()
Jul 7, 2019. Berlin