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

Motion detection on the webcam

It is surprisingly easy to make a small Python script that takes a webcam or any other video and detects when something is moving there. It uses the OpenCV library.

1. Difference between frames

Compares two frames and displays only what are change. The rest is black.

import cv2

# Select camera. Usualy 0, or 1 and so on
cam = cv2.VideoCapture(0)

try:
	while cam.isOpened():
		ret, frame1 = cam.read()
		ret, frame2 = cam.read()
		diff = cv2.absdiff(frame1, frame2)
    
		# To exit press 'q'    
		if cv2.waitKey(10) == ord('q'):
			break
    	
		# Display
		cv2.imshow('Erinevus', diff)
except:
	print("Error.")

2. Binary image

Turn it into binary: only black and white. To make it easy to find contours.

import cv2

# Select camera. Usualy 0, or 1 and so on
cam = cv2.VideoCapture(0)

try:
	while cam.isOpened():
		ret, frame1 = cam.read()
		ret, frame2 = cam.read()
		# Compare frames
		diff = cv2.absdiff(frame1, frame2)
		# Convert diff to grayscale image
		gray = cv2.cvtColor(diff, cv2.COLOR_RGB2GRAY)
		# Blur gray image
		blur = cv2.GaussianBlur(gray, (5, 5), 0)
		# Converts to Binary images. Only black and white colour.
		_, thresh = cv2.threshold(blur, 20, 255, cv2.THRESH_BINARY)
		# Expand moving image part
		dilated = cv2.dilate(thresh, None, iterations=3)
    
		# To exit press 'q'    
		if cv2.waitKey(10) == ord('q'):
			break
    	
		# Display
		cv2.imshow('Erinevus', dilated)
except:
	print("Error.")

3. Contours

Now displays founded contours over the original image.

import cv2

# Select camera. Usualy 0, or 1 and so on
cam = cv2.VideoCapture(0)

try:
	while cam.isOpened():
		ret, frame1 = cam.read()
		ret, frame2 = cam.read()
		# Compare frames
		diff = cv2.absdiff(frame1, frame2)
		# Convert diff to grayscale image
		gray = cv2.cvtColor(diff, cv2.COLOR_RGB2GRAY)
		# Blur gray image
		blur = cv2.GaussianBlur(gray, (5, 5), 0)
		# Converts to Binary images. Only black and white colour.
		_, thresh = cv2.threshold(blur, 20, 255, cv2.THRESH_BINARY)
		# Expand moving image part
		dilated = cv2.dilate(thresh, None, iterations=3)
		# Find moving part contures
		contours, _ = cv2.findContours(dilated, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
		# Draw contours
		cv2.drawContours(frame1, contours, -1, (0, 255, 0), 2) #
    
		# To exit press 'q'    
		if cv2.waitKey(10) == ord('q'):
			break
    	
		# Display
		cv2.imshow('Erinevus', frame1)
except:
	print("Error.")

Rectangle

When we know where the contours are. Where are the coordinates of the beginning on the x and y axes. We can draw rectangles around these regions.

import cv2

# Select camera. Usualy 0, or 1 and so on
cam = cv2.VideoCapture(0)

try:
	while cam.isOpened():
		ret, frame1 = cam.read()
		ret, frame2 = cam.read()
		# Compare frames
		diff = cv2.absdiff(frame1, frame2)
		# Convert diff to grayscale image
		gray = cv2.cvtColor(diff, cv2.COLOR_RGB2GRAY)
		# Blur gray image
		blur = cv2.GaussianBlur(gray, (5, 5), 0)
		# Converts to Binary images. Only black and white colour.
		_, thresh = cv2.threshold(blur, 20, 255, cv2.THRESH_BINARY)
		# Expand moving image part
		dilated = cv2.dilate(thresh, None, iterations=3)
		# Find moving part contures
		contours, _ = cv2.findContours(dilated, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

		for c in contours:
			# Select movement size area.
			# If contour is smaller it will be ignored.
			if cv2.contourArea(c) < 2000:
				continue
			# Contour position and size
			x, y, w, h = cv2.boundingRect(c)
			# Draw rectangle
			cv2.rectangle(frame1, (x, y), (x+w, y+h), (0, 255, 0), 2)
			# To something
    
		# To exit press 'q'    
		if cv2.waitKey(10) == ord('q'):
			break
    	
		# Display
		cv2.imshow('Liikumine', frame1)
except:
	print("Error.")

Demo video