Module code >> skvideo.motion.gme
Fork me on GitHub

Source code for skvideo.motion.gme

"""
Implementation of global motion estimators.

"""

import numpy as np
import os
import scipy.ndimage
import scipy.spatial

from ..utils import *


def _hausdorff_distance(E_1, E_2):
    # binary structure
    diamond = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]])

    # extract only 1-pixel border line of objects
    E_1_per = E_1^scipy.ndimage.binary_erosion(E_1, structure=diamond)
    E_2_per = E_2^scipy.ndimage.binary_erosion(E_2, structure=diamond)

    A = scipy.ndimage.distance_transform_edt(~E_2_per)[E_1_per].max()
    B = scipy.ndimage.distance_transform_edt(~E_1_per)[E_2_per].max()
    return np.max((A, B))



[docs] def globalEdgeMotion(frame1, frame2, r=6, method='hamming'): """Global motion estimation using edge features Given two frames, find a robust global translation vector found using edge information. Frames must be luminance, RGB, or edge masks. Parameters ---------- frame1 : ndarray first input frame. Edge mask if dtype=bool, else luminance or rgb image. shape (1, M, N, C), (1, M, N), (M, N, C) or (M, N) frame2 : ndarray second input frame. Edge mask if dtype=bool, else luminance or rgb image. shape (1, M, N, C), (1, M, N), (M, N, C) or (M, N) r : int Search radius for measuring correspondences. method : string "hamming" --> use Hamming distance when measuring edge correspondence distances. The distance used in the census transform. [#f1]_ "hausdorff" --> use Hausdorff distance when measuring edge correspondence distances. [#f2]_ Returns ---------- globalMotionVector : list of int, length 2 The motion to minimize edge distances by moving frame2 with respect to frame1. Returns ``[0, 0]`` when either frame contains no edges (no edge correspondence to minimize), so a blank/edgeless frame yields "no detected motion" rather than a spurious or crashing result. References ---------- .. [#f1] Ramin Zabih and John Woodfill. Non-parametric local transforms for computing visual correspondence. Computer Vision-ECCV, 151-158, 1994. .. [#f2] Kevin Mai, Ramin Zabih, and Justin Miller. Feature-based algorithms for detecting and classifying scene breaks. Cornell University, 1995. """ frame1 = vshape(frame1) frame2 = vshape(frame2) if frame1.shape != frame2.shape: raise ValueError("frame1 and frame2 must have the same shape; got %s vs %s" % (frame1.shape, frame2.shape)) T, M, N, C = frame1.shape if C == 3: frame1 = rgb2gray(frame1) frame2 = rgb2gray(frame2) elif C != 1: raise ValueError("called with frames having %d channels. Please supply only the luminance channel or RGB images." % (C,)) # if type bool, then these are edge maps. No need to convert them, but # still squeeze to 2D so the roll/morphology ops below see (M, N) like # the canny() path produces (vshape made them 4D). if frame1.dtype != bool: E_1 = canny(frame1.squeeze()) else: E_1 = frame1.squeeze() if frame2.dtype != bool: E_2 = canny(frame2.squeeze()) else: E_2 = frame2.squeeze() # If either frame has no edges, there is no edge correspondence to # minimize: every displacement ties (hamming would argmin to the first, # i.e. (-r, -r)) and the hausdorff distance does an empty-array reduction # and crashes. Report no detectable motion. if not E_1.any() or not E_2.any(): return [0, 0] distances = [] displacements = [] for dx in range(-r, r+1, 1): for dy in range(-r, r+1, 1): cimage = np.roll(E_2, dx, axis=0) cimage = np.roll(cimage, dy, axis=1) # smallest distance between a point of points found in cimage if method == 'hamming': distance = scipy.spatial.distance.hamming(np.ravel(cimage), np.ravel(E_1)) elif method == 'hausdorff': # cimage is the shifted frame2 edge map; measure its distance # to frame1's edges (E_1), matching the hamming branch. distance = _hausdorff_distance(cimage, E_1) else: raise NotImplementedError( "Unknown method %r; expected 'hamming' or 'hausdorff'." % (method,) ) # compute # of bit flip distance distances.append(distance) displacements.append([dx, dy]) idx = np.argmin(distances) return displacements[idx]