Long-exposure photography in another way

The traditional way to make long-exposure photography is to take photos with a long-duration shutter speed to sharply capture the stationary elements of images while blurring, smearing, or obscuring the moving elements. Long-exposure photography captures one element that conventional photography does not: an extended period of time. (1)

One day I watched Scott Manley video where he showed how he used video to produce long-exposure rocket launch photos. So I experimented with the code and random videos that I already had on my phone. Video is a sequence of photos and when we take the lightest part of every frame and merge it together into one image we get a result that is very similar to the traditional long-exposure technique.

One of my favourite photographers who use long-exposure photography and produces photos that look like paintings is Chris Friel. His works inspired me to make my MOVE photo series. Although I could not discover his technique secrets.

But I think using videos and OpenCV is a new way for me to experiment with ideas. For example, videos let you combine not only the lightest areas but also the darkest areas from frames. What is impossible with the traditional way.

One thing that I quickly learned is that the video should be short or your final image is completely white or black.

It’s definitely fun to make them and the result is often unpredictable.

Some quick experiments:

Gode on GitHub.

  1. Long-exposure photography
  2. https://taunoerik.art/project-type/move/
  3. https://www.cfriel.com/home
  4. https://opencv.org/
  5. https://github.com/taunoe/Valgusmaal

Continuous image capture from webcam

As part of an online course, I currently take “Computer Vision with Embedded Machine Learning” where is need to collect images to make the dataset. I modified the original code so I can use my regular Linux box and webcam, not Raspberry Pi. The programme counts down and saves images every 7 seconds. That is enough time to change the object position. It also crops and resizes images to 96×96 px size as needed.

This is my setup:

My setup

Example images from created dataset:


And my code:

#!/usr/bin/env python3
Image Capture

Displays image preview on screen.
Counts down and saves image 96*96px.
Restarts count down.
To exit press "q".

06.09.2021 Tauno Erik

import cv2
import numpy as np

# Settings
file_num = 0
save_path = "./"      # Save images to current directory
file_suffix = ".png"  # Extension for image file

def file_exists(filepath):
    Returns true if file exists, false otherwise.
        f = open(filepath, 'r')
        exists = True
        exists = False
    return exists

def get_filepath():
    Returns the next available full path to image file
    global file_num
    # Loop through possible file numbers to see if that file already exists
    filepath = save_path + str(file_num) + file_suffix
    while file_exists(filepath):
        file_num += 1
        filepath = save_path + str(file_num) + file_suffix

    return filepath

def main():
    countdown = SECONDS_TO_COUNTDOWN

    # Figure out the name of the output image filename
    filepath = get_filepath()

    cam = cv2.VideoCapture(0)

    # Set smaller resolution
    #cam.set(cv2.CAP_PROP_FRAME_WIDTH, 160) # 640
    #cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 120) # 480

    # Initial countdown timestamp
    countdown_timestamp = cv2.getTickCount()

    while cam.isOpened():
        # Read camera
        ret, frame = cam.read()

        # Get timestamp for calculating actual framerate
        timestamp = cv2.getTickCount()

        # Each second, decrement countdown
        if (timestamp - countdown_timestamp) / cv2.getTickFrequency() > 1.0:
            countdown_timestamp = cv2.getTickCount()
            countdown -= 1
            # When countdown reaches 0, break out of loop to save image
            if countdown <= 0:
                # Get new image file name
                filepath = get_filepath()
                # Save image
                cv2.imwrite(filepath, resized)
                # Start new count down
                countdown = SECONDS_TO_COUNTDOWN

        # Frame resolution
        frame_height = frame.shape[0]
        frame_width = frame.shape[1]

        # Crop center of image
        new_size = 98
        start_y = int(frame_height/2 - new_size/2)
        end_y = int(frame_height/2 + new_size/2)
        start_x = int(frame_width/2 - new_size/2)
        end_x = int(frame_width/2 + new_size/2)
        # Crop
        cropped = frame[start_y:end_y, start_x:end_x]

        # Rezise to 96*96
        resized = cv2.resize(cropped, (96,96), interpolation=cv2.INTER_CUBIC)

        # Put text only on copied image
        copy = resized.copy()
        # Draw countdown on image
                    (round(resized.shape[1] / 2) - 15, round(resized.shape[0] / 2)+10),
                    (255, 255, 255))

        # Display raw camera image
        cv2.imshow('Kaamera', copy)

        # Press 'q' to exit
        if cv2.waitKey(10) == ord('q'):

    # Clean up

if __name__ == "__main__":
    print('To exit press "q"')

Original code: https://github.com/ShawnHymel/computer-vision-with-embedded-machine-learning/blob/master/1.1.3%20-%20Data%20Collection/Raspberry%20Pi/pi-cam-capture.py

The artistic shape detection algorithm

Today I learned one simple shape detection algorithm. In contrast image, it tries to find how many corners on some shapes are. When there is three, then it is a triangle and so on. And draws coloured contour around it. In a controlled environment, it works mostly as supposed. When you released it to the real wild world it gives quite artistic results.

#!/usr/bin/env python3

Shape detection from images.
Tauno Erik

import cv2 as cv
import os

# Colors (BGR)
RED = (0,0,255)
GREEN = (0,255,0)
BLUE = (255,0,0)
YELLOW = (0,255,255)
CYAN = (255,255,0)
MAGENTA = (255,0,255)
ORANGE = (0,140,255)
PINK = (147,20,255)
PURPLE = (128,0,128)
GOLDEN = (32,165,218)
BROWN = (42,42,165)

def full_path(filename):
  ''' Returns full path to file. '''
  folder = os.path.dirname(__file__) # File location
  full_path = os.path.join(folder, filename)
  return full_path

def shape_detection(file):
  img = cv.imread(file)
  gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
  ret, thresh = cv.threshold(gray, 50, 255, 1)
  contours, h = cv.findContours(thresh, 1, 2)

  img_hall = cv.cvtColor(gray, cv.COLOR_GRAY2BGR)

  for cnt in contours:
    approx = cv.approxPolyDP(cnt, 0.01*cv.arcLength(cnt, True), True)  # Returns array
    print("Shape with {} pints".format(len(approx)))
    n = len(approx)

    if n is 15 or n is 16:
      cv.drawContours(img_hall, [cnt], 0, YELLOW, 5)
    elif n is 12:
      cv.drawContours(img_hall, [cnt], 0, BROWN, 5)
    elif n is 11:
      cv.drawContours(img_hall, [cnt], 0, GOLDEN, 5)
    elif n is 10:
      cv.drawContours(img_hall, [cnt], 0, PURPLE, 5)
    elif n is 9:
      cv.drawContours(img_hall, [cnt], 0, PINK, 5)
    elif n is 8:
      cv.drawContours(img_hall, [cnt], 0, CYAN, 5)
    elif n is 7:
      cv.drawContours(img_hall, [cnt], 0, ORANGE, 5)
    elif n is 6:
      cv.drawContours(img_hall, [cnt], 0, CYAN, 5)
    elif n is 5:
      cv.drawContours(img_hall, [cnt], 0, RED, 5)
    elif n is 4:
      cv.drawContours(img_hall, [cnt], 0, GREEN, 5)
    elif n is 3:
      cv.drawContours(img_hall, [cnt], 0, BLUE, 5)

  cv.imshow('Shaps', img_hall)

if __name__ == "__main__":
  print('Shape detection!')
  print('To close window press: q')

  file = full_path('images/kujundid.jpg')