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:

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
SECONDS_TO_COUNTDOWN = 7


def file_exists(filepath):
    """
    Returns true if file exists, false otherwise.
    """
    try:
        f = open(filepath, 'r')
        exists = True
        f.close()
    except:
        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
                #break

        # 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
        cv2.putText(copy, 
                    str(countdown),
                    (round(resized.shape[1] / 2) - 15, round(resized.shape[0] / 2)+10),
                    cv2.FONT_HERSHEY_PLAIN,
                    4,
                    (255, 255, 255))

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

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

    # Clean up
    cam.release()
    cv2.destroyAllWindows()

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

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

Spurkfun Edge and Camera

My Arduino settings and code to use Sparkfun Edge with the camera module.

Hardware

  • Sparkfun Edge (Input only 3.3V.)
  • Himax CMOS Imaging Camera – HM01B0
  • Wemos CH340G USB to serial (Make sure it output is 3.3V!)
Spurkfun Edge

In Arduino IDE

  • Add Boards Manager URL: https://raw.githubusercontent.com/sparkfun/Arduino_Boards/master/IDE_Board_Manager/package_sparkfun_index.json
  • Install SparkFun Apollo3 Boards package
  • Install SparkFun Himax HM01B0 Cameras library

Settings

Arduino settings

Upload sketch

  • Press and hold the Reset button
  • Press and hold the 14 button
  • Release the Reset button
  • Hold the 14 button and hit Upload in the Arduino IDE

If it is successful you will see this message:

Read images

Board default Serial Baudrate is 460800.

Example Python code is utils/StopMotion.py.

Run this to save images on same directory:

./StopMotion.py -p /dev/ttyUSB0 -o .

First you see something this. When I first tried this it was all I get. Only these numbers forever. Then on the next day I came back, hooked it up and miraculous it started to work.

Positive output

Example image (bmp):

Links

Serial Plotter code

This is my standalone serial plotter. I like to use Arduino, Esp866 etc. and then i started to learn python and decided to make something useful. Something that i will use and what is missing now for me. Although Arduino Ide contains serial plotter. It is little bit different.

Written in python 3 and Qt5. It is basically my first GUI application ever.

Incoming data should be string. Ending with new line character. Number can be separated with almost any character.

Like:

label2la15be17el28/31/42/54 78

or

a2b1.5c1.7d2.8/3.1/4.2/5.4 7.8

But not with unless it is a negative number:

5-10-22-33-40-55-62-75

Script will extracts all numbers and generate graph.

Tested on Ubuntu 19.10 and Windows 10.

Sourcecode and instructions are on Github: https://github.com/taunoe/tauno-serial-plotter

C vs. C++: Bubble sort

I have identical bubble sort algorithm in C and C++. Same data and same computer. Experiment shows that C is twice as slow as C ++.
In C it takes 124 processor clicks or 0.000124 sec.
In C++ it takes 66 processor clicks or 0.000066 sec.

C Bubble sort code:

#include <stdio.h>
#include <time.h>

int arry_size = 0;
int numbers[] = { 8, 1, 5, 2, 4, 0, 3, 7, 9, 6, 99, 55, 44, 21, 12,
34, 5, 68, 19, 71, 70, 82, 65, 41, 72, 11, 36, 18, 27, 14, 15, 13 };

void print_array(int numbers[], int arry_size) {
  for (int i = 0; i < arry_size; i++) {
    printf("%d ", numbers[i]);
    if (i == arry_size - 1) {
      printf("\n");
    }
  }
}

void bubble_sort(int numbers[], int numbers_size) {
    int n, i;
    for (n = numbers_size - 1; n > 0; n--) {
        for (i = 1; i <= n; i++) {
            int left = numbers[i - 1];
            int right = numbers[i];
            if (left > right) {
                numbers[i - 1] = right;
                numbers[i] = left;
            }
        }
    }
}

int main ()
{
  clock_t execution_time;
  execution_time = clock();

  arry_size = sizeof(numbers)/sizeof(numbers[0]);

  printf("Before: ");
  print_array(numbers, arry_size);
  bubble_sort(numbers, arry_size);
  printf("After: ");
  print_array(numbers, arry_size);

  execution_time = clock() - execution_time;
  printf ("Processor time: %ld clicks (%f sec.)\n", 
          execution_time, ((float)execution_time) / CLOCKS_PER_SEC);

  return 0;
}
C Bubble sort time

C++ Bubble sort code:

#include <iostream>
#include <time.h>

int arry_size = 0;
int numbers[] = { 8, 1, 5, 2, 4, 0, 3, 7, 9, 6, 99, 55, 44, 21, 12,
34, 5, 68, 19, 71, 70, 82, 65, 41, 72, 11, 36, 18, 27, 14, 15, 13 };

void print_array(int numbers[], int arry_size) {
  for (int i = 0; i < arry_size; i++) {
    std::cout << numbers[i] << " ";
    if (i == arry_size - 1) {
      std::cout << '\n';
    }
  }
}

void bubble_sort(int numbers[], int numbers_size) {
    int n, i;
    for (n = numbers_size - 1; n > 0; n--) {
        for (i = 1; i <= n; i++) {
            int left = numbers[i - 1];
            int right = numbers[i];
            if (left > right) {
                numbers[i - 1] = right;
                numbers[i] = left;
            }
        }
    }
}

int main () {
clock_t execution_time;
  execution_time = clock();

  arry_size = sizeof(numbers)/sizeof(numbers[0]);

  std::cout << "Before: ";
  print_array(numbers, arry_size);
  bubble_sort(numbers, arry_size);
  std::cout << "After: ";
  print_array(numbers, arry_size);

  execution_time = clock() - execution_time;

  std::cout << "Processor time:" << execution_time 
            << "clicks (" << ((float)execution_time) / CLOCKS_PER_SEC
            << " sec.)" << '\n';
  return 0;
}
C++ Bubble sort time

https://github.com/taunoe/cpp-examples/tree/master/Sorting-algorithms/Bubble-sort