Low-level operations

In addition to contour filters, vuba also provides a number of wrappers and convenience functions around some of the lower level image operations provided with OpenCV. These can be grouped into the following categories:

  • Format conversion: wrappers that permit conversion between colour spaces.

  • Drawing: functions related to drawing different shapes on images that have an equivalent API regardless of whether single or multiple shapes are requested to be drawn.

  • Mask contructors: convenience functions for both creating masks, and performing segmentation based on masks created.

Here, we will demonstrate their usage in several examples. There are links at the end of this guide to more in-depth example scripts that are hosted on github.

For these examples, the following imports are required:

In [1]: import numpy as np

In [2]: import matplotlib.pyplot as plt

In [3]: import cv2

In [4]: import vuba

Format conversion

There are several format conversion wrappers provided by vuba:

These all have the exact same behaviour as the corresponding OpenCV functions:

In [5]: video = vuba.Video('../examples/example_data/raw_video/test.avi')

In [6]: frame = video.read(0)

# Grayscale a BGR frame
In [7]: gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

In [8]: gray_frame = vuba.gray(frame)

# Convert grayscale frame to BGR
In [9]: bgr_frame = cv2.cvtColor(gray_frame, cv2.COLOR_GRAY2BGR)

In [10]: bgr_frame = vuba.bgr(gray_frame)

# Convert BGR frame to HSV
In [11]: hsv_frame = cv2.cvtColor(bgr_frame, cv2.COLOR_BGR2HSV)

In [12]: hsv_frame = vuba.hsv(bgr_frame)

Note that these functions also contain additional exceptions that provide more explicit error messages:

In [13]: vuba.bgr(bgr_frame)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-13-467794c4d864> in <module>
----> 1 vuba.bgr(bgr_frame)

~/checkouts/readthedocs.org/user_builds/vuba/checkouts/latest/vuba/ops.py in bgr(frame)
    312 
    313     """
--> 314     _channel_check(frame, 2)
    315     return cv2.cvtColor(frame, cv2.COLOR_GRAY2BGR)
    316 

~/checkouts/readthedocs.org/user_builds/vuba/checkouts/latest/vuba/ops.py in _channel_check(img, type_)
     16     if channels != type_:
     17         raise ValueError(
---> 18             f"Input image needs to be {exc_info[type_]} or have {type_} channels. Instead an image with {channels} channels was provided."
     19         )
     20 

ValueError: Input image needs to be grayscale or have 2 channels. Instead an image with 3 channels was provided.

In [14]: cv2.cvtColor(bgr_frame, cv2.COLOR_GRAY2BGR)
---------------------------------------------------------------------------
error                                     Traceback (most recent call last)
<ipython-input-14-5aa365a27c09> in <module>
----> 1 cv2.cvtColor(bgr_frame, cv2.COLOR_GRAY2BGR)

error: OpenCV(4.5.5) /io/opencv/modules/imgproc/src/color.simd_helpers.hpp:92: error: (-2:Unspecified error) in function 'cv::impl::{anonymous}::CvtHelper<VScn, VDcn, VDepth, sizePolicy>::CvtHelper(cv::InputArray, cv::OutputArray, int) [with VScn = cv::impl::{anonymous}::Set<1>; VDcn = cv::impl::{anonymous}::Set<3, 4>; VDepth = cv::impl::{anonymous}::Set<0, 2, 5>; cv::impl::{anonymous}::SizePolicy sizePolicy = cv::impl::<unnamed>::NONE; cv::InputArray = const cv::_InputArray&; cv::OutputArray = const cv::_OutputArray&]'
> Invalid number of channels in input image:
>     'VScn::contains(scn)'
> where
>     'scn' is 3

For more information on these wrappers, please see the following API documentation: gray(), bgr(), hsv().

Drawing

Currently, vuba provides four drawing wrappers:

These wrappers support usage with both single and multiple shapes. This can remove the sometimes cumbersome series of for loops one has to write when drawing multiple shapes. Below, we will demonstrate their usage using a simple binary threshold applied to our example video:

# Read in the first frame and grayscale it
In [15]: first = video.read(index=0, grayscale=True)

# Threshold the grayscaled frame to a binary threshold (n=50, to=255)
In [16]: _, thresh = cv2.threshold(first, 50, 255, cv2.THRESH_BINARY)

# Find all the contours in the thresholded image
In [17]: contours, hierarchy = vuba.find_contours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

Next, let’s draw the resultant polygons on the frame we grabbed:

# Convert to bgr for drawing below
In [18]: frame = vuba.bgr(first)

# Draw all contours
In [19]: vuba.draw_contours(frame, contours, -1, (0,255,0), 1)

# Draw the largest contour
In [20]: vuba.draw_contours(frame, vuba.largest(contours), -1, (0,0,255), 2)

In [21]: plt.imshow(frame)
Out[21]: <matplotlib.image.AxesImage at 0x7fd4dd65fc10>
_images/simple_drawing.png

Note that because this is a wrapper, the arguments for colour, line thickness etc. are equivalent to those used in the corresponding OpenCV function. For a more in-depth example, see the following script.

Creating masks

Vuba provides a number of convenience functions for creating masks for bitwise-and operations:

Each of these performs much as you would expect: you supply coordinates and parameters that describe the corresponding shape(s), and a mask is created that enables one to segment to those shapes in images of the same size. Because each of these contructors uses the above drawing functions under the hood, you can supply multiple shapes and construct a mask that corresponds to them:

# Using the above contours to find multiple bounding boxes
In [22]: bboxs = vuba.fit_rectangles(contours)

In [23]: mask = vuba.rect_mask(first, bboxs)

In [24]: out = vuba.bgr(mask)

In [25]: plt.imshow(out)
Out[25]: <matplotlib.image.AxesImage at 0x7fd4dd6674d0>
_images/multi_rect_mask.png

Using this mask, we can use Mask() to perform segmentation on an image of the same size:

In [26]: segm = vuba.Mask(mask)

In [27]: ret = segm(first)

In [28]: ret = vuba.bgr(ret)

# Visualise our segmentation
In [29]: vuba.draw_rectangles(ret, bboxs, (0, 255, 0), 2)

In [30]: plt.imshow(ret)
Out[30]: <matplotlib.image.AxesImage at 0x7fd4dea08410>
_images/simple_segmentation.png

See also

For additional example scripts that cover these functions in more depth, see the following: