Code owners
Assign users and groups as approvers for specific file changes. Learn more.
st2d.py 4.21 KiB
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Aug 29 11:30:17 2019
@author: vand@dtu.dk
"""
import numpy as np
import scipy.ndimage
# STRUCTURE TENSOR 2D
def structure_tensor(image, sigma, rho):
""" Structure tensor for 2D image data
Arguments:
image: a 2D array of size N = rows*columns
sigma: a noise scale, structures smaller than sigma will be
removed by smoothing
rho: an integration scale giving the size over the neighborhood in
which the orientation is to be analysed
Returns:
an array with shape (3,N) containing elements of structure tensor
s_xx, s_yy, s_xy ordered acording to image.ravel()
Author: vand@dtu.dk, 2019
"""
# computing derivatives (scipy implementation truncates filter at 4 sigma)
image = image.astype(np.float);
Ix = scipy.ndimage.gaussian_filter(image, sigma, order=[1,0], mode='nearest')
Iy = scipy.ndimage.gaussian_filter(image, sigma, order=[0,1], mode='nearest')
# integrating elements of structure tensor (scipy uses sequence of 1D)
Jxx = scipy.ndimage.gaussian_filter(Ix**2, rho, mode='nearest')
Jyy = scipy.ndimage.gaussian_filter(Iy**2, rho, mode='nearest')
Jxy = scipy.ndimage.gaussian_filter(Ix*Iy, rho, mode='nearest')
S = np.vstack((Jxx.ravel(), Jyy.ravel(), Jxy.ravel()));
return S
def eig_special(S):
""" Eigensolution for symmetric real 2-by-2 matrices
Arguments:
S: an array with shape (3,N) containing structure tensor
Returns:
val: an array with shape (2,N) containing sorted eigenvalues
vec: an array with shape (2,N) containing eigenvector corresponding
to the smallest eigenvalue (the other is orthogonal to the first)
Author: vand@dtu.dk, 2019
"""
val = 0.5*(S[0]+S[1]+np.outer(np.array([-1,1]), np.sqrt((S[0]-S[1])**2+4*S[2]**2)))
vec = np.vstack((-S[2],S[0]-val[0])) # y will be positive
aligned = S[2]==0 # dealing with diagonal matrices
vec[:,aligned] = 1-np.argsort(S[0:2,aligned], axis=0)
vec = vec/np.sqrt(vec[0]**2+vec[1]**2) # normalizing
return val, vec
def solve_flow(S):
""" Solving 1D optic flow, returns LLS optimal x for flow along y axis
( x is a solution to S[0]*x=S[2] )
Arguments:
S: an array with shape (3,N) containing 2D structure tensor
Returns:
x: an array with shape (1,N) containing x components of the flow
Author: vand@dtu.dk, 2019
"""
aligned = S[0]==0 # 0 or inf solutions
x = np.zeros((1,S.shape[1]))
x[0,~aligned] = - S[2,~aligned]/S[0,~aligned]
return x # returning shape (1,N) array for consistancy with 3D case
#% HELPING FUNCTIONS FOR VISUALIZATION
def plot_orientations(ax, dim, vec, s = 5):
""" Helping function for adding orientation-quiver to the plot.
Arguments: plot axes, image shape, orientation, arrow spacing.
"""
vx = vec[0].reshape(dim)
vy = vec[1].reshape(dim)
xmesh, ymesh = np.meshgrid(np.arange(dim[0]), np.arange(dim[1]), indexing='ij')
ax.quiver(ymesh[s//2::s,s//2::s],xmesh[s//2::s,s//2::s],vy[s//2::s,s//2::s],vx[s//2::s,s//2::s],color='r',angles='xy')
ax.quiver(ymesh[s//2::s,s//2::s],xmesh[s//2::s,s//2::s],-vy[s//2::s,s//2::s],-vx[s//2::s,s//2::s],color='r',angles='xy')
def polar_histogram(ax, distribution, cmap = 'hsv'):
""" Helping function for producing polar histogram.
Arguments: plot axes, oriantation distribution, colormap.
"""
N = distribution.size
bin_centers_full = (np.arange(2*N)+0.5)*np.pi/N # full circle (360 deg)
distribution_full = np.r_[distribution,distribution]/max(distribution) # full normalized distribution
x = np.r_[distribution_full*np.cos(bin_centers_full),0]
y = np.r_[distribution_full*np.sin(bin_centers_full),0]
triangles = np.array([(i, np.mod(i-1,2*N), 2*N) for i in range(2*N)]) # triangles[0] is symmetric over 0 degree
triangle_centers_full = (np.arange(2*N))*np.pi/N # a triangle covers area BETWEEN two bin_centers
triangle_colors = np.mod(triangle_centers_full, np.pi)/np.pi # from 0 to 1-(1/2N)
ax.tripcolor(y, x, triangles, facecolors=triangle_colors, cmap=cmap, vmin = 0.0, vmax = 1.0)
ax.set_aspect('equal')
ax.set_xlim([-1,1])
ax.set_ylim([1,-1])