示例#1
0
def compute_error(H, x, x1):
    """
    Give this homography, compute the planar reprojection error
    between points a and b.  x and x1 can be n, 2 or n,3 homogeneous
    coordinates.  If only x, y coordinates, assume that z=1, e.g. x, y, 1 for
    all coordiantes.

    Parameters
    ----------

    x : ndarray
        n,2 array of x,y coordinates

    x1 : ndarray
        n,2 array of x,y coordinates

    Returns
    -------

    df : dataframe
         With columns for x_residual, y_residual, rmse, and
         error contribution.  The dataframe also has cumulative
         x, t, and total RMS statistics accessible via
         df.x_rms, df.y_rms, and df.total_rms, respectively.
    """
    if x.shape[1] == 2:
        x = make_homogeneous(x)
    if x1.shape[1] == 2:
        x1 = make_homogeneous(x1)

    z = np.empty(x.shape)
    # ToDo: Vectorize for performance
    for i, j in enumerate(x):
        z[i] = H.dot(j)
        z[i] /= z[i][-1]

    data = np.empty((x.shape[0], 4))

    data[:, 0] = x_res = x1[:, 0] - z[:, 0]
    data[:, 1] = y_res = x1[:, 1] - z[:, 1]

    if data[:,:1].all() == 0:
        data[:] = 0.0
        total_rms = x_rms = y_rms = 0
    else:
        data[:, 2] = rms = np.sqrt(x_res**2 + y_res**2)
        total_rms = np.sqrt(np.mean(x_res**2 + y_res**2))
        x_rms = np.sqrt(np.mean(x_res**2))
        y_rms = np.sqrt(np.mean(y_res**2))
        data[:, 3] = rms / total_rms

    df = pd.DataFrame(data,
                      columns=['x_residuals',
                               'y_residuals',
                               'rmse',
                               'error_contribution'])
    df.total_rms = total_rms
    df.x_rms = x_rms
    df.y_rms = y_rms
    return df
示例#2
0
def compute_error(H, x, x1):
    """
    Give this homography, compute the planar reprojection error
    between points a and b.  x and x1 can be n, 2 or n,3 homogeneous
    coordinates.  If only x, y coordinates, assume that z=1, e.g. x, y, 1 for
    all coordiantes.

    Parameters
    ----------

    x : ndarray
        n,2 array of x,y coordinates

    x1 : ndarray
        n,2 array of x,y coordinates

    Returns
    -------

    df : dataframe
         With columns for x_residual, y_residual, rmse, and
         error contribution.  The dataframe also has cumulative
         x, t, and total RMS statistics accessible via
         df.x_rms, df.y_rms, and df.total_rms, respectively.
    """
    if x.shape[1] == 2:
        x = make_homogeneous(x)
    if x1.shape[1] == 2:
        x1 = make_homogeneous(x1)

    z = np.empty(x.shape)
    # ToDo: Vectorize for performance
    for i, j in enumerate(x):
        z[i] = H.dot(j)
        z[i] /= z[i][-1]

    data = np.empty((x.shape[0], 4))

    data[:, 0] = x_res = x1[:, 0] - z[:, 0]
    data[:, 1] = y_res = x1[:, 1] - z[:, 1]

    if data[:, :1].all() == 0:
        data[:] = 0.0
        total_rms = x_rms = y_rms = 0
    else:
        data[:, 2] = rms = np.sqrt(x_res**2 + y_res**2)
        total_rms = np.sqrt(np.mean(x_res**2 + y_res**2))
        x_rms = np.sqrt(np.mean(x_res**2))
        y_rms = np.sqrt(np.mean(y_res**2))
        data[:, 3] = rms / total_rms

    df = pd.DataFrame(
        data,
        columns=['x_residuals', 'y_residuals', 'rmse', 'error_contribution'])
    df.total_rms = total_rms
    df.x_rms = x_rms
    df.y_rms = y_rms
    return df
示例#3
0
def main(msg):
    fp = shapely.wkt.loads(msg['poly'])
    
    files = msg['files']
    # Compute the fundamental matrices
    fundamentals = {}
    matches = []
    for k, v in msg['matches'].items():
        edge = eval(k)
        print(edge)
        match = pd.read_json(v)
        s = match.iloc[0].source
        d = match.iloc[0].destination
        source_path = files[str(edge[0])]
        destination_path = files[str(edge[1])]

        x1 = from_hdf(source_path, index=match.source_idx.values, descriptors=False)
        x2 = from_hdf(destination_path, index=match.destination_idx.values, descriptors=False)

        x1 = make_homogeneous(x1[['x', 'y']].values)
        x2 = make_homogeneous(x2[['x', 'y']].values)
        f, fmask = compute_fundamental_matrix(x1, x2, method='ransac', reproj_threshold=20)
        fundamentals[edge] = f
        match['strength'] = compute_reprojection_error(f, x1, x2)
        matches.append(match)
    
    matches = pd.concat(matches)

    # Of the concatenated matches only a subset intersect the geometry for this overlap, pull these
    
    def check_in(r, poly):
        p = shapely.geometry.Point(r.lon, r.lat)
        return p.within(poly)

    intersects = matches.apply(check_in, args=(fp,), axis=1)
    matches = matches[intersects]
    matches = matches.reset_index(drop=True)

    # Apply the spatial suppression
    bounds = fp.bounds
    k = fp.area / 0.005
    if k < 3:
        k = 3
    if k > 25:
        k = 25
    subset = spatial_suppression(matches, bounds, k=k)

    # Push the points through
    overlaps = msg['overlaps']
    oid = msg['oid']
    pts = deepen(subset, fundamentals, overlaps, oid)

    return pts
示例#4
0
def compute_fundamental_error(F, x, x1):
    """
    Compute the fundamental error using the idealized error metric.

    Ideal error is defined by $x^{\intercal}Fx = 0$,
    where $x$ are all matchpoints in a given image and
    $x^{\intercal}F$ defines the standard form of the
    epipolar line in the second image.

    This method assumes that x and x1 are ordered such that x[0]
    correspondes to x1[0].

    Parameters
    ----------
    F : ndarray
        (3,3) Fundamental matrix

    x : arraylike
        (n,2) or (n,3) array of homogeneous coordinates

    x1 : arraylike
        (n,2) or (n,3) array of homogeneous coordinates with the same
        length as argument x

    Returns
    -------
    F_error : ndarray
              n,1 vector of reprojection errors
    """

    # TODO: Can this be vectorized for performance?
    if x.shape[1] != 3:
        x = make_homogeneous(x)
    if x1.shape[1] != 3:
        x1 = make_homogeneous(x1)

    if isinstance(x, pd.DataFrame):
        x = x.values
    if isinstance(x1, pd.DataFrame):
        x1 = x1.values

    err = np.empty(len(x))
    for i in range(len(x)):
        err[i] = x1[i].T.dot(F).dot(x[i])
    return err
    def compute_error(self, a, b, mask=None):
        """
        Give this homography, compute the planar reprojection error
        between points a and b.

        Parameters
        ----------

        a : ndarray
            n,2 array of x,y coordinates

        b : ndarray
            n,2 array of x,y coordinates

        index : ndarray
                Index to be used in the returned dataframe

        Returns
        -------

        df : dataframe
             With columns for x_residual, y_residual, rmse, and
             error contribution.  The dataframe also has cumulative
             x, t, and total RMS statistics accessible via
             df.x_rms, df.y_rms, and df.total_rms, respectively.
        """

        if mask is not None:
            mask = mask
        else:
            mask = pd.Series(True, index=self.index)

        a = a[mask].values
        b = b[mask].values

        if a.shape[1] == 2:
            a = make_homogeneous(a)
        if b.shape[1] == 2:
            b = make_homogeneous(b)

        # ToDo: Vectorize for performance
        for i, j in enumerate(a):
            a[i] = self.dot(j)
            a[i] /= a[i][-1]

        data = np.empty((a.shape[0], 4))

        data[:, 0] = x_res = b[:, 0] - a[:, 0]
        data[:, 1] = y_res = b[:, 1] - a[:, 1]
        data[:, 2] = rms = np.sqrt(x_res**2 + y_res**2)
        total_rms = np.sqrt(np.mean(x_res**2 + y_res**2))
        x_rms = np.sqrt(np.mean(x_res**2))
        y_rms = np.sqrt(np.mean(y_res**2))

        data[:, 3] = rms / total_rms

        df = pd.DataFrame(data,
                          columns=['x_residuals',
                                   'y_residuals',
                                   'rmse',
                                   'error_contribution'],
                          index=self.index)

        df.total_rms = total_rms
        df.x_rms = x_rms
        df.y_rms = y_rms
        return df
示例#6
0
def compute_fundamental_matrix(kp1, kp2, method='mle', reproj_threshold=2.0,
                               confidence=0.99, mle_reproj_threshold=0.5):
    """
    Given two arrays of keypoints compute the fundamental matrix.  This function
    accepts two dataframe of keypoints that have

    Parameters
    ----------
    kp1 : arraylike
          (n, 2) of coordinates from the source image

    kp2 : ndarray
          (n, 2) of coordinates from the destination image


    method : {'ransac', 'lmeds', 'normal', '8point'}
              The openCV algorithm to use for outlier detection

    reproj_threshold : float
                       The maximum distances in pixels a reprojected points
                       can be from the epipolar line to be considered an inlier

    confidence : float
                 [0, 1] that the estimated matrix is correct

    Returns
    -------
    F : ndarray
        A 3x3 fundamental matrix

    mask : pd.Series
           A boolean mask identifying those points that are valid.

    Notes
    -----
    While the method is user definable, if the number of input points
    is < 7, normal outlier detection is automatically used, if 7 > n > 15,
    least medians is used, and if 7 > 15, ransac can be used.
    """
    if method == 'mle':
        # Grab an initial estimate using RANSAC, then apply MLE
        method_ = cv2.FM_RANSAC
    elif method == 'ransac':
        method_ = cv2.FM_RANSAC
    elif method == 'lmeds':
        method_ = cv2.FM_LMEDS
    elif method == 'normal':
        method_ = cv2.FM_7POINT
    elif method == '8point':
        method_ = cv2.FM_8POINT
    else:
        raise ValueError("Unknown estimation method. Choices are: 'lme', 'ransac', 'lmeds', '8point', or 'normal'.")


    # OpenCV wants arrays
    try: # OpenCV < 3.4.1
        F, mask = cv2.findFundamentalMat(np.asarray(kp1),
                                         np.asarray(kp2),
                                         method_,
                                         param1=reproj_threshold,
                                         param2=confidence)
    except: # OpenCV >= 3.4.1
        F, mask = cv2.findFundamentalMat(np.asarray(kp1),
                                         np.asarray(kp2),
                                         method_,
                                         ransacReprojThreshold=reproj_threshold,
                                         confidence=confidence)
    if F is None:
        warnings.warn("F Computation Failed.")
        return None, None
    if F.shape != (3,3):
        warnings.warn('F computation fell back to 7-point algorithm, not setting F.')
        return None, None
    # Ensure that the singularity constraint is met
    F = enforce_singularity_constraint(F)

    try:
        mask = mask.astype(bool).ravel()  # Enforce dimensionality
    except:
        return  # pragma: no cover

    if method == 'mle':
        # Now apply the gold standard algorithm to refine F

        if kp1.shape[1] != 3:
            kp1 = make_homogeneous(kp1)
        if kp2.shape[1] != 3:
            kp2 = make_homogeneous(kp2)

        # Generate an idealized and to be updated camera model
        p1 = camera.camera_from_f(F)
        p = camera.idealized_camera()
        if kp1[mask].shape[0] <=12 or kp2[mask].shape[0] <=12:
            warnings.warn("Unable to apply MLE.  Not enough correspondences.  Returning with a RANSAC computed F matrix.")
            return F, mask

        # Apply Levenber-Marquardt to perform a non-linear lst. squares fit
        #  to minimize triangulation error (this is a local bundle)
        result = optimize.least_squares(camera.projection_error, p1.ravel(),
                                        args=(p, kp1[mask].T, kp2[mask].T),
                                        method='lm')

        gold_standard_p = result.x.reshape(3, 4) # SciPy Lst. Sq. requires a vector, camera is 3x4
        optimality = result.optimality
        gold_standard_f = camera_utils.crossform(gold_standard_p[:,3]).dot(gold_standard_p[:,:3])

        F = gold_standard_f

        mask = update_fundamental_mask(F, kp1, kp2,
                                       threshold=mle_reproj_threshold).values

    return F, mask
示例#7
0
def deepen(matches, fundamentals, overlaps, oid):

    points = []
    for g, subm in matches.groupby(['source', 'destination']):
        w = int(g[0])
        v = int(g[1])
            
        push_into = [i for i in overlaps if i not in g]
        
        x1 = make_homogeneous(subm[['source_x', 'source_y']].values)
        x2 = make_homogeneous(subm[['destination_x', 'destination_y']].values)
        
        pid = 0
        for i in range(x1.shape[0]):
            row = subm.iloc[i]
            geom  = 'SRID=949900;POINTZ({} {} {})'.format(row.lon, row.lat, 0)
            a = x1[i]
            b = x2[i]

            p1 = {'image_id':w,
                  'keypoint_id':int(row.source_idx),
                  'x':float(a[0]), 'y':float(a[1]), 
                  'match_id':int(row.name),
                  'point_id':'{}_{}_{}'.format(oid, g, pid),
                  'geom':geom}
            p2 = {'image_id':v,
                  'keypoint_id':int(row.destination_idx),
                  'x':float(b[0]), 'y':float(b[1]), 
                  'match_id':int(row.name),
                  'point_id':'{}_{}_{}'.format(oid, g, pid),
                  'geom':geom}
            
            points.append(p1)
            points.append(p2)
                
            for e in push_into:
                try:
                    if w > e:
                        f31 = [e,w]
                        f31 = np.asarray(fundamentals[tuple(f31)]).T
                    else:
                        f31 = [w,e]
                        f31 = np.asarray(fundamentals[tuple(f31)])     

                    if v > e:
                        f32 = [e,v]
                        f32 = np.asarray(fundamentals[tuple(f32)]).T
                    else:
                        f32 = [v,e]
                        f32 = np.asarray(fundamentals[tuple(f32)])

                    x3 = np.cross(f31.dot(a), f32.dot(b))
                    x3[0] /= x3[2]
                    x3[1] /= x3[2]

                    # This needs to aggregate all of the 
                    n = {'image_id':e,
                        'keypoint_id':None, 
                        'x':float(x3[0]), 'y':float(x3[1]),
                        'point_id':'{}_{}_{}'.format(oid, g, pid),
                        'geom':geom, 'match_id':None}

                    points.append(n)
                except:
                    pass
            pid += 1
    return points
示例#8
0
    def compute_error(self, a, b, mask=None):
        """
        Give this homography, compute the planar reprojection error
        between points a and b.

        Parameters
        ----------

        a : ndarray
            n,2 array of x,y coordinates

        b : ndarray
            n,2 array of x,y coordinates

        index : ndarray
                Index to be used in the returned dataframe

        Returns
        -------

        df : dataframe
             With columns for x_residual, y_residual, rmse, and
             error contribution.  The dataframe also has cumulative
             x, t, and total RMS statistics accessible via
             df.x_rms, df.y_rms, and df.total_rms, respectively.
        """

        if mask is not None:
            mask = mask
        else:
            mask = pd.Series(True, index=self.index)

        a = a[mask].values
        b = b[mask].values

        if a.shape[1] == 2:
            a = make_homogeneous(a)
        if b.shape[1] == 2:
            b = make_homogeneous(b)

        # ToDo: Vectorize for performance
        for i, j in enumerate(a):
            a[i] = self.dot(j)
            a[i] /= a[i][-1]

        data = np.empty((a.shape[0], 4))

        data[:, 0] = x_res = b[:, 0] - a[:, 0]
        data[:, 1] = y_res = b[:, 1] - a[:, 1]
        data[:, 2] = rms = np.sqrt(x_res**2 + y_res**2)
        total_rms = np.sqrt(np.mean(x_res**2 + y_res**2))
        x_rms = np.sqrt(np.mean(x_res**2))
        y_rms = np.sqrt(np.mean(y_res**2))

        data[:, 3] = rms / total_rms

        df = pd.DataFrame(data,
                          columns=[
                              'x_residuals', 'y_residuals', 'rmse',
                              'error_contribution'
                          ],
                          index=self.index)

        df.total_rms = total_rms
        df.x_rms = x_rms
        df.y_rms = y_rms
        return df