Skip to content
This repository was archived by the owner on Dec 23, 2018. It is now read-only.

Commit

Permalink
Implement normalization with exponents set to 1.
Browse files Browse the repository at this point in the history
Implement Band Pass filters.  Addresses #39.
Fix gaussians. Addresses  #40.
  • Loading branch information
inati committed Dec 17, 2018
1 parent 2b6a462 commit fd5775d
Show file tree
Hide file tree
Showing 6 changed files with 456 additions and 413 deletions.
132 changes: 62 additions & 70 deletions ia_mri_tools/features.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import numpy as np
from .filters import _pinv, radial, high_pass, gradient, hessian, gradient_rot, hessian_rot
from .filters import _pinv, high_pass, low_pass
from .filters import band_pass, gradient_band_pass, hessian_band_pass
from .filters import gradient_rot, hessian_rot


# Scales for first stage local gain control
# and output gain control
HIGH_PASS_SCALE = 4
NORMALIZATION_SCALE = 5

# Number of spatial scales for rotationally invariant features
NUM_SCALES = 3
# Number of spatial scales
NUM_SCALES = 4

# Scales for normalization / gain control
NORMALIZATION_SCALE = 4

def _get_dim(data):
"""
Expand All @@ -26,99 +25,92 @@ def _get_dim(data):
return ndim


def compute_local_normalization(data, scale=NORMALIZATION_SCALE):
def input_normalization(data, scale=NORMALIZATION_SCALE, high_pass_output=True):
"""
Compute inverse of the local norm of a feature
:param data: 2D or 3D amplitude image
:param scale: spatial scale averagin filter
:return: 2D or 3D normalization image
normalized_data = normalization * data
"""

# Compute the local amplitude
a = np.sqrt(radial(np.abs(data)**2, 'gaussian', scale=scale))

# Gain is (1/a)
n = _pinv(a)

return n


def input_normalization(data, high_pass_scale=HIGH_PASS_SCALE, normalization_scale=NORMALIZATION_SCALE):
"""
Compute local normalization for the input in a manner similar to the retina
Normalize by the power of the high pass filtered image
Normalize in a manner similar to the retina
"""
z = high_pass(data, scale)
a = np.abs(z)
sigma = np.mean(a)
f = sigma + low_pass(a, scale)
if high_pass_output:
y = z / f
else:
y = data/f

hp = high_pass(data, scale=high_pass_scale)
norm = compute_local_normalization(hp, scale=normalization_scale)
output = norm*data
return output
return y


def riff(data, nscales=NUM_SCALES, high_pass_scale=HIGH_PASS_SCALE, normalization_scale=NORMALIZATION_SCALE):
def riff(data, nscales=NUM_SCALES, normalization_scale=None):
"""
Compute multi-scale rotationally invariant image features
from the zeroth, first, and second order gaussian derivatives
with divisive normalization
:param data: 2D or 3D numpy array
:param nscales: int number of scales
:normalization_scale: int scale for stage 1 input normalization
:return: list of 3*nscales+1 2D or 3D numpy arrays and list of names
"""

if normalization_scale is None:
normalization_scale = NORMALIZATION_SCALE
if normalization_scale < nscales:
raise RuntimeError('Normalization scale cannot be less than the number of Scales.')

# Initialize the textures list and the names list
t = []
names = []

# Initialize the total power for final stage normalization
total_power = 0.0

# Stage 1, Local gain control
d = input_normalization(data,
high_pass_scale=high_pass_scale,
normalization_scale=normalization_scale,
)
# Stage 1: High pass filter and local normalization
d = input_normalization(data, scale=normalization_scale)

# Step 2, feature generation
# The first feature is the high pass filter
feat = high_pass(d, scale=0)
t.append(feat)
names.append(f'High Pass')
total_power += np.abs(feat)**2
# Stage 2: Feature generation
# The features are organized by level
# At each level:
# bandpass (2 rotational invariant: bp, |bp|)
# gradient (1 rotational invariant: |g|)
# hessian (4 rotational invariants: Tr(H), Det(H), Sec, |H|)
# Keep a running total of the total power from each feature
total_power = 0.0

for lev in range(nscales):
# The next set of features are the rotational invariants of the zeroth order gaussian derivatives
feat = radial(d, 'gaussian', scale=lev)
# Bandpass (difference of adjacent gaussians)
feat = band_pass(d, scale_one=lev, scale_two=lev+1)
t.append(feat)
names.append(f'Gaussian S{lev}')
names.append(f'Band Pass S{lev}')
t.append(np.abs(feat))
names.append(f'Band Pass Magnitude S{lev}')
total_power += np.abs(feat)**2

# The next set of features are the rotational invariants of the first order gaussian derivatives
g = gradient(d, scale=lev)
# Gradient
g = gradient_band_pass(d, scale=lev)
feat = gradient_rot(g)
t.append(feat)
names.append(f'Gradient S{lev}R1')
names.append(f'Gradient Magnitude S{lev}')
# The power is the square of the gradient amplitude
total_power += feat**2

# The next set of features are the rotational invariants of the second order gaussian derivatives
h = hessian(d, scale=lev)
h = hessian_band_pass(d, scale=lev)
feat = hessian_rot(h)
# The power is the the square of the Frobenius norm, the last rotationally invariant feature
total_power += feat[-1]**2
for n in range(len(feat)):
t.append(feat[n])
names.append(f'Hessian S{lev}R{n+1}')

# The final amplitude
total_amplitude = np.sqrt(total_power)
# Step 3, the final local normalization
norm = compute_local_normalization(total_amplitude, scale=normalization_scale)
# Loop over the features and scale them all
for n in range(len(t)):
t[n] = norm*t[n]
t.append(feat[0])
names.append(f'Laplacian S{lev}')
if data.ndim == 2:
t.append(feat[1])
names.append(f'Hessian Det S{lev}')
t.append(feat[2])
names.append(f'Hessian Magnitude S{lev}')
# The power is the the square of the Frobenius norm
total_power += feat[2]**2
elif data.ndim == 3:
t.append(feat[1])
names.append(f'Hessian R2 S{lev}')
t.append(feat[2])
names.append(f'Hessian Det S{lev}')
t.append(feat[3])
names.append(f'Hessian Magnitude S{lev}')
# The power is the the square of the Frobenius norm
total_power += feat[3]**2

return t, names
Loading

0 comments on commit fd5775d

Please sign in to comment.