Webcam stream

Just run the webcam and show the output. Click on the blue power button (top right) to stop it. If you just want to pause the video, click the pause button.


In [ ]:
from cvloop import cvloop

cvloop()

Videos can be loaded as simple as well, just provide a path (video taken from OpenCV):


In [ ]:
from cvloop import cvloop

cvloop('768x576.avi')

In [ ]:
from cvloop import cvloop

# Prints info about the image and skips thus the first frame. 
# Otherwise it behaves like the default.
cvloop(print_info=True)

A word of caution

It is important to keep a reference to the cvloop return value. If it is the last statement in a notebook cell, this is done automatically (as you can see in almost all examples), since notebooks store the last return value in Out[]. However, if you want to do something after starting the loop, you will have to keep a reference yourself.


In [ ]:
# This will stop automatically before even reading the first frame.
from cvloop import cvloop
cvloop()
print('Oh no!')

In [ ]:
# While this will work.
from cvloop import cvloop
loop = cvloop()
print('Oh yeah!')

Premade functions

The cvloop.functions module provides premade functions ready to use. Most of them are just simple wrappers around OpenCV functions or filters. Below are some examples, for more take a look at the cvloop_functions notebook.


In [ ]:
from cvloop import cvloop, Inverter

# Inverts the image.
cvloop(function=Inverter())

In [ ]:
from cvloop import cvloop, BackgroundSubtractorMOG2

# Performs a background subtraction.
cvloop(function=BackgroundSubtractorMOG2())

Custom functions

It is possible to pass custom functions to the loop. The functions take an image as input and return an image as output:

def custom_function(image):
    modified_image = ... # do something cool
    return modified_image

The example performs background subtraction on the webcam stream (see OpenCV Documentation for details).


In [ ]:
import cv2
from cvloop import cvloop

# This is the same as cvloop.functions.cv_background_subtractor_mog2.
def mog2(frame):
    return mog2.fgbg.apply(frame)
mog2.fgbg = cv2.createBackgroundSubtractorMOG2()

cvloop(function=mog2)

Side by side

To compare the input image with the output, you can pass the side_by_side option.


In [ ]:
from cvloop import cvloop, Inverter

cvloop(function=Inverter(), side_by_side=True)

Color conversion

If the image is a color image and convert_color is not -1, the image is converted accordingly. The default is the conversion to RGB from BGR, i.e. cv2.COLOR_BGR2RGB. The conversion happens before the original image is processed by the passed function.


In [ ]:
import cv2
from cvloop import cvloop

cvloop(convert_color=cv2.COLOR_BGR2GRAY)

In [ ]:
from cvloop import cvloop

cvloop(convert_color=-1)

In [ ]:
import cv2
from cvloop import cvloop

def conv(frame):
    return cv2.cvtColor(frame, cv2.COLOR_BGR2XYZ)

cvloop(function=conv, convert_color=-1, side_by_side=True)

Custom color map

By default the plot function makes some guesses about how to show an image. If the image data consists of only two dimensions, gray scale is assumed, resulting in the usage of the grayscale color map. If the image data is three dimensional, it is assumed to be in RGB colors. (Note that unless overwritten, cvloop performs the conversion from OpenCVs standard BGR automatically!)

However, it is possible to provide custom colormaps, as will be demonstrated below. In general the colormaps should be designed such that matplotlib.pyplot.imshow can handle them – the colormaps reference is a good starting point. To apply a color map properly, the images are converted to grayscale first, using $\text{Gray} = .299 R + .587 G + .114 B$. If the image was already in grayscale, it is preserved.


In [ ]:
from cvloop import cvloop

cvloop(cmaps='Paired')

If a simple color map is provided, it is applied to all images.


In [ ]:
from cvloop import cvloop

cvloop(cmaps='Paired', side_by_side=True)

Multiple custom color maps

It is also possible to provide a color map for the input and the output image individually. None entries will be ignored.


In [ ]:
from cvloop import cvloop

cvloop(cmaps=('terrain', 'Paired'), side_by_side=True)

In [ ]:
from cvloop import cvloop

cvloop(cmaps=(None, 'Paired'), side_by_side=True)

In [ ]:
from cvloop import cvloop

cvloop(cmaps=(None, None), side_by_side=True)

Complex VideoCapture source

Sometimes it is important to change properties of the cv2 VideoCapture source. cvloop allows passing a prepared VideoCapture. cvloop will try to release the source, but if it does not have the release() method, you will have to do it yourself.


In [ ]:
import cv2
from cvloop import cvloop

capture = cv2.VideoCapture(0)
capture.set(cv2.CAP_PROP_FRAME_WIDTH, 640.)
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480.)
cvloop(source=capture, function=lambda frame: 255 - frame)

In [ ]:
# releasing the resource (only rarely needed)
capture.release()

Alternative video source

It is also possible to provide your own "video source". The only thing you have to do is implement a read method which can be invoked without arguments and returns two values, ret and frame, where ret is False if no frame is given (frame == None). Note however that ret == False interrupts the video process.

Also it is important to notice that if you don't have a get method which allows to get the properties cv2.CAP_PROP_FRAME_WIDTH and cv2.CAP_PROP_FRAME_HEIGHT, the first frame will be skipped to determine those dimensions. This might change in future version to avoid losing frames.


In [ ]:
import time
import numpy as np
from cvloop import cvloop

def map_to_image(vals, minval, maxval):
    return ((vals - np.min(vals)) * (maxval - 1) / (np.max(vals) - np.min(vals)) + minval).astype(np.int)

class MySource:
    def __init__(self, dim=(50, 25)):
        self.shift = 0
        self.W, self.H = dim
        self.image = np.ones((self.W, self.H))
        self.stop = 100

    def read(self):
        time.sleep(1/30)
        self.stop -= 1

        self.image = np.ones((self.W, self.H))
        self.shift = (self.shift + .1) % (np.pi * 2)
        x = np.arange(0, 2*np.pi, 0.001) + self.shift
        y = np.sin(x)
        self.image[map_to_image(x, 0, self.W), map_to_image(y, 0, self.H)] = 0
        
        return self.stop >= 0, self.image.T

custom_source = MySource()
cvloop(source=custom_source, cmaps='terrain')

Video annotations

This feature is still experimental!

It is possible to add annotations to the processed image. Annotations can either be rectangles (RECT) or circles (CIRC).

To use annotations, provide a list of annotations following this format:

[x, y, frame, options]

Where x and y are the center coordinates of the annotation and frame is the frame number for the annotation to be shown. options is an optional parameter. It is a dictionary with any of the following properties:

  • shape: Either CIRC or RECT. Determines the shape of the annotation. Defaults to RECT.
  • color: Either a scalar value (gray scale) or an RGB tuple (r, g, b). All values must be between 0 and 1. Determines the color of the annotation. Defaults to (1, 0, 0) (bright red).
  • line: The line width of the annotation. Defaults to 2.
  • size: The size of the annotation. For circles (CIRC) this should be a scalar value for the radius (default: 30). For rectangles (RECT) this should be a tuple (width, height) (default: (40, 30)).

In [ ]:
from cvloop import cvloop

annotations = []

# Show all annotations from frames 10 to 79
for i in range(10, 80):
    # Red rectangle moving from left to right
    annotations.append([i * 5 + 40, 50, i])
    # Gray circle
    annotations.append([250, 140, i, {'shape': 'CIRC', 'color': .8}])

    # Violett rectangle with all rectangle properties set:
    annotations.append([300, 250, i, {
        'shape': 'RECT',
        'color': (0.3, 0.2, 0.8),
        'line': 4,
        'size': (30, 80)
    }])

    # Orange circle with all circle properties set:
    annotations.append([450, 300, i, {
        'shape': 'CIRC',
        'color': (1, 0.5, 0.1),
        'line': 1,
        'size': 40
    }])

cvloop('768x576.avi', annotations=annotations)