Module dvt.batch

Annotators for extracting high-level metadata about the images in the input.

Expand source code
# -*- coding: utf-8 -*-
"""Annotators for extracting high-level metadata about the images in the input.
"""

from numpy import abs as npabs, mean, percentile, prod, zeros
from cv2 import cvtColor, calcHist, resize, COLOR_BGR2HSV

from .abstract import BatchAnnotator


class DiffAnnotator(BatchAnnotator):
    """Annotator for detecting differences between frames."""

    def __init__(self, **kwargs):

        self.quantiles = kwargs.get("quantiles", [])
        self.size = kwargs.get("size", 32)
        self.bins = kwargs.get("bins", 16)

    def annotate_batch(self, batch):
        """Annotate the batch of frames with the difference annotator.

        Args:
            batch (FrameBatch): A batch of images to annotate.

        Returns:
            A list of dictionaries containing the video name, frame, average
            value, and any requested quantile and histogram differences.
        """

        output = {"frame": batch.get_frame_names()}
        output["avg_value"] = _average_value_batch(batch)

        for quant in self.quantiles:
            key = "q{0:d}".format(quant)
            output[key] = _l1_quantile(batch, quantile=quant, size=self.size)

            key = "h{0:d}".format(quant)
            output[key] = _hist_diffs(batch, quantile=quant, bins=self.bins)

        return {"diff": output}


def _l1_quantile(batch, quantile=50, size=32):
    """Compute differences between subsequent frames in a batch."""
    bsize = batch.bsize
    msize = bsize + 1
    assert msize <= batch.get_frames().shape[0]

    simg = zeros((msize, size, size, 3))
    for iran in range(msize):
        fsmall = resize(batch.get_frames()[iran, :, :, :], (size, size))
        fsmall_hsv = cvtColor(fsmall, COLOR_BGR2HSV)
        simg[iran, :, :, :] = fsmall_hsv

    norm = simg[slice(0, bsize), :, :, :] - simg[slice(1, bsize + 1), :, :, :]

    return percentile(npabs(norm), q=quantile, axis=(1, 2, 3))


def _hist_diffs(batch, quantile=50, bins=16):
    """Compute differences between HSV histograms across a batch."""
    bsize = batch.bsize
    msize = bsize + 1
    assert msize <= batch.get_frames().shape[0]

    hist_vals = _hsv_hist(batch, msize, bins=bins)
    norm = hist_vals[slice(0, bsize), :] - hist_vals[slice(1, bsize + 1), :]
    norm = norm / prod(batch.get_frames().shape[1:4]) * 100

    return percentile(npabs(norm), q=quantile, axis=(1))


def _hsv_hist(batch, msize, bins=16):
    """Compute histogram counts from a batch of images."""
    hist_vals = zeros((msize, bins * 3))
    for iran in range(msize):
        hsv = cvtColor(batch.get_frames()[iran, :, :, :], COLOR_BGR2HSV)
        for i in range(3):
            hist = calcHist([hsv], [i], None, [bins], [0, 256]).flatten()
            hist_vals[iran, slice(i * bins, (i + 1) * bins)] = hist

    return hist_vals


def _average_value_batch(batch):
    """Compute the average value across a batch of images."""
    img = batch.get_batch()
    return mean(img, axis=(1, 2, 3))

Functions

def calcHist(images, channels, mask, histSize, ranges, hist, accumulate)

calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]]) -> hist . @overload

def cvtColor(src, code, dst, dstCn)

cvtColor(src, code[, dst[, dstCn]]) -> dst . @brief Converts an image from one color space to another. .
. The function converts an input image from one color space to another. In case of a transformation . to-from RGB color space, the order of the channels should be specified explicitly (RGB or BGR). Note . that the default color format in OpenCV is often referred to as RGB but it is actually BGR (the . bytes are reversed). So the first byte in a standard (24-bit) color image will be an 8-bit Blue . component, the second byte will be Green, and the third byte will be Red. The fourth, fifth, and . sixth bytes would then be the second pixel (Blue, then Green, then Red), and so on. .
. The conventional ranges for R, G, and B channel values are: . - 0 to 255 for CV_8U images . - 0 to 65535 for CV_16U images . - 0 to 1 for CV_32F images .
. In case of linear transformations, the range does not matter. But in case of a non-linear . transformation, an input RGB image should be normalized to the proper value range to get the correct . results, for example, for RGB \f$\rightarrow\f$ L*u*v* transformation. For example, if you have a . 32-bit floating-point image directly converted from an 8-bit image without any scaling, then it will . have the 0..255 value range instead of 0..1 assumed by the function. So, before calling #cvtColor , . you need first to scale the image down: . @code . img *= 1./255; . cvtColor(img, img, COLOR_BGR2Luv); . @endcode . If you use #cvtColor with 8-bit images, the conversion will have some information lost. For many . applications, this will not be noticeable but it is recommended to use 32-bit images in applications . that need the full range of colors or that convert an image before an operation and then convert . back. .
. If conversion adds the alpha channel, its value will set to the maximum of corresponding channel . range: 255 for CV_8U, 65535 for CV_16U, 1 for CV_32F. .
. @param src input image: 8-bit unsigned, 16-bit unsigned ( CV_16UC… ), or single-precision . floating-point. . @param dst output image of the same size and depth as src. . @param code color space conversion code (see #ColorConversionCodes). . @param dstCn number of channels in the destination image; if the parameter is 0, the number of the . channels is derived automatically from src and code. .
. @see @ref imgproc_color_conversions

def resize(src, dsize, dst, fx, fy, interpolation)

resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) -> dst . @brief Resizes an image. .
. The function resize resizes the image src down to or up to the specified size. Note that the . initial dst type or size are not taken into account. Instead, the size and type are derived from . the src,dsize,fx, and fy. If you want to resize src so that it fits the pre-created dst, . you may call the function as follows: . @code . // explicitly specify dsize=dst.size(); fx and fy will be computed from that. . resize(src, dst, dst.size(), 0, 0, interpolation); . @endcode . If you want to decimate the image by factor of 2 in each direction, you can call the function this . way: . @code . // specify fx and fy and let the function compute the destination image size. . resize(src, dst, Size(), 0.5, 0.5, interpolation); . @endcode . To shrink an image, it will generally look best with #INTER_AREA interpolation, whereas to . enlarge an image, it will generally look best with c#INTER_CUBIC (slow) or #INTER_LINEAR . (faster but still looks OK). .
. @param src input image. . @param dst output image; it has the size dsize (when it is non-zero) or the size computed from . src.size(), fx, and fy; the type of dst is the same as of src. . @param dsize output image size; if it equals zero, it is computed as: . \f[\texttt{dsize = Size(round(fxsrc.cols), round(fysrc.rows))}\f] . Either dsize or both fx and fy must be non-zero. . @param fx scale factor along the horizontal axis; when it equals 0, it is computed as . \f[\texttt{(double)dsize.width/src.cols}\f] . @param fy scale factor along the vertical axis; when it equals 0, it is computed as . \f[\texttt{(double)dsize.height/src.rows}\f] . @param interpolation interpolation method, see #InterpolationFlags .
. @sa warpAffine, warpPerspective, remap

Classes

class DiffAnnotator (**kwargs)

Annotator for detecting differences between frames.

Expand source code
class DiffAnnotator(BatchAnnotator):
    """Annotator for detecting differences between frames."""

    def __init__(self, **kwargs):

        self.quantiles = kwargs.get("quantiles", [])
        self.size = kwargs.get("size", 32)
        self.bins = kwargs.get("bins", 16)

    def annotate_batch(self, batch):
        """Annotate the batch of frames with the difference annotator.

        Args:
            batch (FrameBatch): A batch of images to annotate.

        Returns:
            A list of dictionaries containing the video name, frame, average
            value, and any requested quantile and histogram differences.
        """

        output = {"frame": batch.get_frame_names()}
        output["avg_value"] = _average_value_batch(batch)

        for quant in self.quantiles:
            key = "q{0:d}".format(quant)
            output[key] = _l1_quantile(batch, quantile=quant, size=self.size)

            key = "h{0:d}".format(quant)
            output[key] = _hist_diffs(batch, quantile=quant, bins=self.bins)

        return {"diff": output}

Ancestors

Methods

def annotate_batch(self, batch)

Annotate the batch of frames with the difference annotator.

Args

batch : FrameBatch
A batch of images to annotate.

Returns

A list of dictionaries containing the video name, frame, average value, and any requested quantile and histogram differences.

Expand source code
def annotate_batch(self, batch):
    """Annotate the batch of frames with the difference annotator.

    Args:
        batch (FrameBatch): A batch of images to annotate.

    Returns:
        A list of dictionaries containing the video name, frame, average
        value, and any requested quantile and histogram differences.
    """

    output = {"frame": batch.get_frame_names()}
    output["avg_value"] = _average_value_batch(batch)

    for quant in self.quantiles:
        key = "q{0:d}".format(quant)
        output[key] = _l1_quantile(batch, quantile=quant, size=self.size)

        key = "h{0:d}".format(quant)
        output[key] = _hist_diffs(batch, quantile=quant, bins=self.bins)

    return {"diff": output}

Inherited members