Commit 2a595e9a authored by jay's avatar jay
Browse files

updates to the matchers to reflect the attribute changes on the edges

parent 16e86caf
Loading
Loading
Loading
Loading
+45 −84
Original line number Diff line number Diff line
@@ -6,7 +6,8 @@ from autocnet.matcher.feature_matcher import match
from autocnet.transformation.decompose import coupled_decomposition


def decompose_and_match(self, k=2, maxiteration=3, size=18, buf_dist=3,**kwargs):
def decompose(self, subset=False, k=2, maxiteration=2, size=18,
              buf_dist=3, ndv=None, **kwargs):
    """
    Similar to match, this method first decomposed the image into
    $4^{maxiteration}$ subimages and applys matching between each sub-image.
@@ -21,10 +22,6 @@ def decompose_and_match(self, k=2, maxiteration=3, size=18, buf_dist=3,**kwargs)
    k : int
        The number of neighbors to find

    method : {'coupled', 'whole'}
             whether to utilize coupled decomposition
             or match the whole image

    maxiteration : int
                   When using coupled decomposition, the number of recursive
                   divisions to apply.  The total number of resultant
@@ -49,26 +46,28 @@ def decompose_and_match(self, k=2, maxiteration=3, size=18, buf_dist=3,**kwargs)
               the (sub)image a point must be in order to be used as a
               partioning point.  The smaller the distance, the more likely
               percision errors can results in erroneous partitions.

    ndv : float
          The no data value that will be masked when computing the radial
          correlation.
    """
    def func(group):
        ratio = 0.8
        res = [False] * len(group)
        if len(res) == 1:
            return [single]
        if group.iloc[0] < group.iloc[1] * ratio:
            res[0] = True
        return res

    # Grab the original image arrays

    sdata = self.source.get_array()
    ddata = self.destination.get_array()

    ssize = sdata.shape
    dsize = ddata.shape

    sdata[sdata == ndv] = np.nan
    ddata[ddata == ndv] = np.nan

    matches, _ = self.clean(['ratio'])
    sidx = matches['source_idx']
    didx = matches['destination_idx']

    # Grab all the available candidate keypoints
    skp = self.source.get_keypoints()
    dkp = self.destination.get_keypoints()
    skp = self.source.get_keypoints().loc[sidx]
    dkp = self.destination.get_keypoints().loc[didx]

    # Set up the membership arrays
    self.smembership = np.zeros(sdata.shape, dtype=np.int16)
@@ -76,12 +75,9 @@ def decompose_and_match(self, k=2, maxiteration=3, size=18, buf_dist=3,**kwargs)
    self.smembership[:] = -1
    self.dmembership[:] = -1
    pcounter = 0

    # FLANN Matcher
    fl= FlannMatcher()

    for k in range(maxiteration):
        partitions = np.unique(self.smembership)
        npartitions = len(partitions)
        for p in partitions:
            sy_part, sx_part = np.where(self.smembership == p)
            dy_part, dx_part = np.where(self.dmembership == p)
@@ -98,90 +94,55 @@ def decompose_and_match(self, k=2, maxiteration=3, size=18, buf_dist=3,**kwargs)
            mindx = np.min(dx_part)
            maxdx = np.max(dx_part) + 1

            # Clip the sub image from the full images
            # Clip the sub image from the full images (this is a MBR)
            asub = sdata[minsy:maxsy, minsx:maxsx]
            bsub = ddata[mindy:maxdy, mindx:maxdx]

            # Utilize the FLANN matcher to find a match to approximate a center
            fl.add(self.destination.descriptors, self.destination['node_id'])
            fl.train()

            scounter = 0
            decompose = False
            while True:
                sub_skp = skp.query('x >= {} and x <= {} and y >= {} and y <= {}'.format(minsx, maxsx, minsy, maxsy))
                # Check the size to ensure a valid return
                if len(sub_skp) == 0:
                    break # No valid keypoints in this (sub)image
                if size > len(sub_skp):
                    size = len(sub_skp)
                candidate_idx = np.random.choice(sub_skp.index, size=size, replace=False)
                candidates = self.source.descriptors[candidate_idx]
                matches = fl.query(candidates, self.source['node_id'], k=3, index=candidate_idx)

                # Apply Lowe's ratio test to try to find a 'good' starting point
                mask = matches.groupby('source_idx')['distance'].transform(func).astype('bool')
                candidate_matches = matches[mask]
                match_idx = candidate_matches['source_idx'].astype(np.int)

                # Extract those matches that pass the ratio check
                sub_skp = skp.iloc[match_idx]

                # Check that valid points remain
                if len(sub_skp) == 0:
                    break

                # Locate the candidate closest to the middle of all of the matches
            # Approximate the mid point of the partition as the mean of the matched keypoints
            sub_skp = skp.query('x >= {} and x <= {} and y >= {} and y <= {}'.format(minsx, maxsx,
                                                                                 minsy, maxsy))
            smx, smy = sub_skp[['x', 'y']].mean()
            mid = np.array([[smx, smy]])
            dists = cdist(mid, sub_skp[['x', 'y']])

            closest = sub_skp.iloc[np.argmin(dists)]
            closest_idx = closest.name
            soriginx, soriginy = closest[['x', 'y']]

            # Grab the corresponding point in the destination
                q = candidate_matches.query('source_idx == {}'.format(closest.name))
                dest_idx = int(q['destination_idx'].iat[0])
                doriginx = dkp.at[dest_idx, 'x']
                doriginy = dkp.at[dest_idx, 'y']

                if mindy + buf_dist <= doriginy <= maxdy - buf_dist\
                 and mindx + 3 <= doriginx <= maxdx - 3:
                    # Point is good to split on
                    decompose = True
                    break
                else:
                    scounter += 1
                    if scounter >= maxiteration:
                        break

            # Clear the Flann matcher for reuse
            fl.clear()

            # Check that the identified match falls within the (sub)image
            # This catches most bad matches that have passed the ratio check
            if not (buf_dist <= doriginx - mindx <= bsub.shape[1] - buf_dist) or not\
                   (buf_dist <= doriginy - mindy <= bsub.shape[0] - buf_dist):
                   decompose = False

            if decompose:
                # Apply coupled decomposition, shifting the origin to the sub-image
            dest_idx = matches[matches['source_idx'] == closest_idx]['destination_idx']
            dest_pt = dkp.loc[dest_idx]
            doriginx, doriginy = dest_pt[['x', 'y']].values[0]

            # Sub image origin is assumed to be 0,0 (local sub-image space), while match point origins are
            # in the full image space.  Shift the match point orign to be in the sub-image space if needed
            soriginx -= minsx
            soriginy -= minsy
            doriginx -= mindx
            doriginy -= mindy

            # Apply coupled decomposition
            s_submembership, d_submembership = coupled_decomposition(asub, bsub,
                                                                     sorigin=(soriginx - minsx, soriginy - minsy),
                                                                     dorigin=(doriginx - mindx, doriginy - mindy),
                                                                     **kwargs)
                                                                     sorigin=(soriginx, soriginy),
                                                                     dorigin=(doriginx, doriginy))

            # Shift the returned membership counters to a set of unique numbers
            s_submembership += pcounter
            d_submembership += pcounter

                # And assign membership
            self.smembership[minsy:maxsy,
                        minsx:maxsx] = s_submembership
                self.dmembership[mindy:maxdy,
                            mindx:maxdx] = d_submembership

            sdy = dy_part - min(dy_part)
            sdx = dx_part - min(dx_part)
            self.dmembership[dy_part, dx_part] = d_submembership[sdy, sdx]
            pcounter += 4


def decompose_and_match(self, **kwargs):

    decompose(self, **kwargs)

    # Now match the decomposed segments to one another
    for p in np.unique(self.smembership):
        sy_part, sx_part = np.where(self.smembership == p)
+2 −3
Original line number Diff line number Diff line
import warnings

import cudasift as cs

def extract_features(array, nfeatures=None):
def extract_features(array, nfeatures=None, **kwargs):
    if not nfeatures:
        nfeatures = int(max(array.shape) / 1.75)
    else:
        warnings.warn('NFeatures specified with the CudaSift implementation.  Please ensure the distribution of keypoints is what you expect.')

    siftdata = cs.PySiftData(nfeatures)
    cs.ExtractKeypoints(array, siftdata)
    cs.ExtractKeypoints(array, siftdata, **kwargs)
    keypoints, descriptors = siftdata.to_data_frame()
    keypoints = keypoints[['x', 'y', 'scale', 'sharpness', 'edgeness', 'orientation', 'score', 'ambiguity']]
    # Set the columns that have unfilled values to zero to avoid confusion
+1 −1
Original line number Diff line number Diff line
@@ -33,4 +33,4 @@ def match(self, ratio=0.8, **kwargs):

    # Set the matches and set the 'ratio' (ambiguity) mask
    self.matches = df
    self.masks = ('ratio', df['ambiguity'] <= ratio)
    self.masks['ratio'] = df['ambiguity'] <= ratio
+12 −0
Original line number Diff line number Diff line
import pandas as pd
from autocnet.matcher.feature import FlannMatcher

def match(self, k=2, **kwargs):
@@ -68,6 +69,17 @@ def match(self, k=2, **kwargs):
        _add_matches(matches)
        fl.clear()

    # TODO: This should be converted to a decorator on the class
    # TODO: This entire method should never have access to the class
    self.masks = pd.DataFrame()

    fl = FlannMatcher()
    mono_matches(self.source, self.destination, **kwargs)

    # Since this matches bidirectionally
    if 'aidx' in kwargs.keys():
        if not 'bidx' in kwargs.keys():
            kwargs['bidx'] = None
        kwargs['aidx'], kwargs['bidx'] = kwargs['bidx'], kwargs['aidx']

    mono_matches(self.destination, self.source, **kwargs)