Coupled Decomposition - Part II

[2]:
import math
import os
import sys

sys.path.insert(0, os.path.abspath('/data/autocnet'))

import autocnet
from autocnet import CandidateGraph

# The GPU based extraction library that contains SIFT extraction and matching
import cudasift as cs

# A method to resize the images on the fly.
from scipy.misc import imresize

# Fundamental matrix computation
from autocnet.transformation import fundamental_matrix as fm

from autocnet.transformation.decompose import coupled_decomposition
from scipy.spatial.distance import cdist

%pylab inline
figsize(16,4)
Populating the interactive namespace from numpy and matplotlib

CandidateGraph -> Custom Extraction Func -> Matching -> Coupled Decomposition

[3]:
a = 'AS15-P-0111_CENTER_LRG_CROPPED.png'
b = 'AS15-P-0112_CENTER_LRG_CROPPED.png'

adj = {a:[b],
       b:[a]}

cg = CandidateGraph.from_adjacency(adj)

# Enable the GPU
autocnet.cuda(enable=True, gpu=0)
[4]:
# Write a custom keypoint extraction function - this could get monkey patched onto the graph object...
def extract(arr, downsample_amount=None, **kwargs):
    total_size = arr.shape[0] * arr.shape[1]
    if not downsample_amount:
        downsample_amount = math.ceil(total_size / 12500**2)
    shape = (int(arr.shape[0] / downsample_amount), int(arr.shape[1] / downsample_amount))
    # Downsample
    arr = imresize(arr, shape, interp='lanczos')

    npts = max(arr0.shape) / 3.5
    sd = cs.PySiftData(npts)
    cs.ExtractKeypoints(arr, sd, **kwargs)
    kp, des = sd.to_data_frame()
    kp = kp[['x', 'y', 'scale', 'sharpness', 'edgeness', 'orientation', 'score', 'ambiguity']]
    kp['score'] = 0.0
    kp['ambiguity'] = 0.0

    return kp, des, sd, downsample_amount, arr
[5]:
# Write a generic decomposer
def custom_decompose(arr0, arr1):
    kp0, des0, sd0, downsample_amount0, arr0 = extract(arr0, thresh=1)
    kp1, des1, sd1, downsample_amount1, arr1 = extract(arr1, thresh=1)

    # Now apply matching, outlier detection, and compute a fundamental matrix
    sd0 = cs.PySiftData.from_data_frame(kp0, des0)
    sd1 = cs.PySiftData.from_data_frame(kp1, des1)

    # Apply the matcher
    cs.PyMatchSiftData(sd0, sd1)
    matches, _ = sd0.to_data_frame()
    # Generic decision about ambiguity and score based on quantiles

    ambiguity_threshold = matches.ambiguity.quantile(0.01)  # Grabbing the 1%s in this data set
    score = matches.score.quantile(0.85)

    print(ambiguity_threshold, score)

    submatches = matches.query('ambiguity <= {} and score >= {}'.format(ambiguity_threshold, score))

    kpa = submatches[['x','y']]
    kpb = submatches[['match_xpos', 'match_ypos']]

    F, mask = fm.compute_fundamental_matrix(kpa, kpb, method='ransac', reproj_threshold=2.0)
    F = fm.enforce_singularity_constraint(F)

    inliers = submatches[mask]


    return inliers, arr0, arr1

arr0 = cg.node[0].geodata.read_array()
arr1 = cg.node[1].geodata.read_array()

inliers, arr0, arr1 = custom_decompose(arr0, arr1)
0.9287434983253479 0.9619517207145691

Checking the Results

Now check the quality of our automated correspondence outlier thresholds - is the automated approach working?

[6]:
imshow(arr0)
plot(inliers.x, inliers.y, 'ro', markersize=3)
show()
imshow(arr1)
plot(inliers.match_xpos, inliers.match_ypos, 'bo', markersize=3)
../../../_images/users_tutorials_apollopan_5b._Coupled_Decomposition_-_Part_II_7_0.png
[6]:
[<matplotlib.lines.Line2D at 0x7fcdb8691080>]
../../../_images/users_tutorials_apollopan_5b._Coupled_Decomposition_-_Part_II_7_2.png

Extension

Now extend the custom decomposition func above to apply the decomposition method to the data.

[7]:
# Write a generic decomposer
def custom_decompose(arr0, arr1):
    kp0, des0, sd0, downsample_amount0, arr0 = extract(arr0, thresh=1)
    kp1, des1, sd1, downsample_amount1, arr1 = extract(arr1, thresh=1)

    # Now apply matching, outlier detection, and compute a fundamental matrix
    sd0 = cs.PySiftData.from_data_frame(kp0, des0)
    sd1 = cs.PySiftData.from_data_frame(kp1, des1)

    # Apply the matcher
    cs.PyMatchSiftData(sd0, sd1)
    matches, _ = sd0.to_data_frame()
    # Generic decision about ambiguity and score based on quantiles

    # Apply outlier detection methods for the matches
    ambiguity_threshold = matches.ambiguity.quantile(0.01)  # Grabbing the 1%s in this data set
    score = matches.score.quantile(0.85)
    submatches = matches.query('ambiguity <= {} and score >= {}'.format(ambiguity_threshold, score))

    # Compute a fundamental matrix
    kpa = submatches[['x','y']]
    kpb = submatches[['match_xpos', 'match_ypos']]
    F, mask = fm.compute_fundamental_matrix(kpa, kpb, method='ransac', reproj_threshold=2.0)
    F = fm.enforce_singularity_constraint(F)

    # Grab the inliers
    inliers = submatches[mask]

    # Prepare for coupled decomposition
    midx = arr0.shape[1] / 2
    midy = arr0.shape[0] / 2

    mid = np.array([[midx, midy]])
    dists = cdist(mid, inliers[['x', 'y']])
    mid_correspondence = inliers.iloc[np.argmin(dists)]
    mid_correspondence

    # Decompose the images into quadrants
    smembership, dmembership, = coupled_decomposition(arr0, arr1,
                                                 sorigin=mid_correspondence[['x', 'y']],
                                                 dorigin=mid_correspondence[['match_xpos', 'match_ypos']],
                                                 theta=0)

    # Return the membership decisions
    return smembership, dmembership, arr0, arr1

arr0 = cg.node[0].geodata.read_array()
arr1 = cg.node[1].geodata.read_array()

# Keep returning arr0 and arr1 because MatPlotLib will crash the kernel if we try to visualize a non-downsampled version of the image.
smem, dmem, arr0, arr1 = custom_decompose(arr0, arr1)

Checking Again

That the decomposition looks reasonably close. The next step is to recursively apply the decomposition.

[8]:
imshow(arr0, cmap='gray')
imshow(smem, alpha=0.25)
show()
imshow(arr1, cmap='gray')
imshow(dmem, alpha=0.25)

../../../_images/users_tutorials_apollopan_5b._Coupled_Decomposition_-_Part_II_11_0.png
[8]:
<matplotlib.image.AxesImage at 0x7fcdba73ea90>
../../../_images/users_tutorials_apollopan_5b._Coupled_Decomposition_-_Part_II_11_2.png