示例#1
0
def assign_spatially_constrained_matches(chip2_dlen_sqrd, kpts1, kpts2, H,
                                         fx2_to_fx1, fx2_to_dist, match_xy_thresh,
                                         norm_xy_bounds=(0.0, 1.0)):
    """
    Args:
        chip2_dlen_sqrd (dict):
        kpts1 (ndarray[float32_t, ndim=2]):  keypoints
        kpts2 (ndarray[float32_t, ndim=2]):  keypoints
        H (ndarray[float64_t, ndim=2]):  homography/perspective matrix that maps image1 space into image2 space
        fx2_to_fx1 (ndarray): image2s nearest feature indices in image1
        fx2_to_dist (ndarray):
        match_xy_thresh (float):
        norm_xy_bounds (tuple):

    Returns:
        tuple: assigntup(
            fx2_match, - matching feature indices in image 2
            fx1_match, - matching feature indices in image 1
            fx1_norm,  - normmalizing indices in image 1
            match_dist, - descriptor distances between fx2_match and fx1_match
            norm_dist, - descriptor distances between fx2_match and fx1_norm
            )

    CommandLine:
        python -m vtool.matching --test-assign_spatially_constrained_matches

    Example:
        >>> # ENABLE_DOCTEST
        >>> from vtool.matching import *  # NOQA
        >>> kpts1 = np.array([[  6.,   4.,   15.84,    4.66,    7.24,    0.  ],
        ...                   [  9.,   3.,   20.09,    5.76,    6.2 ,    0.  ],
        ...                   [  1.,   1.,   12.96,    1.73,    8.77,    0.  ],])
        >>> kpts2 = np.array([[  2.,   1.,   12.11,    0.38,    8.04,    0.  ],
        ...                   [  5.,   1.,   22.4 ,    1.31,    5.04,    0.  ],
        ...                   [  6.,   1.,   19.25,    1.74,    4.72,    0.  ],])
        >>> match_xy_thresh = .37
        >>> chip2_dlen_sqrd = 1400
        >>> norm_xy_bounds = (0.0, 1.0)
        >>> H = np.array([[ 2,  0, 0],
        >>>               [ 0,  1, 0],
        >>>               [ 0,  0, 1]])
        >>> fx2_to_fx1 = np.array([[2, 1, 0],
        >>>                        [0, 1, 2],
        >>>                        [2, 0, 1]], dtype=np.int32)
        >>> fx2_to_dist = np.array([[.40, .80, .85],
        >>>                         [.30, .50, .60],
        >>>                         [.80, .90, .91]], dtype=np.float32)
        >>> # verify results
        >>> assigntup = assign_spatially_constrained_matches(chip2_dlen_sqrd, kpts1, kpts2, H, fx2_to_fx1, fx2_to_dist, match_xy_thresh, norm_xy_bounds)
        >>> fx2_match, fx1_match, fx1_norm, match_dist, norm_dist = assigntup
        >>> result = ut.list_str(assigntup, precision=3)
        >>> print(result)
        (
            np.array([0, 1, 2], dtype=np.int32),
            np.array([2, 0, 2], dtype=np.int32),
            np.array([1, 1, 0], dtype=np.int32),
            np.array([ 0.4,  0.3,  0.8], dtype=np.float32),
            np.array([ 0.8,  0.5,  0.9], dtype=np.float32),
        )

    Example:

    assigns spatially constrained vsone match using results of nearest
    neighbors.
    """
    import vtool as vt
    index_dtype = fx2_to_fx1.dtype
    # Find spatial errors of keypoints under current homography (kpts1 mapped into image2 space)
    fx2_to_xyerr_sqrd = ktool.get_match_spatial_squared_error(kpts1, kpts2, H, fx2_to_fx1)
    fx2_to_xyerr = np.sqrt(fx2_to_xyerr_sqrd)
    fx2_to_xyerr_norm = np.divide(fx2_to_xyerr, np.sqrt(chip2_dlen_sqrd))

    # Find matches and normalizers that satisfy spatial constraints
    fx2_to_valid_match      = ut.inbounds(fx2_to_xyerr_norm, 0.0, match_xy_thresh, eq=True)
    fx2_to_valid_normalizer = ut.inbounds(fx2_to_xyerr_norm, *norm_xy_bounds, eq=True)
    fx2_to_fx1_match_col = vt.find_first_true_indices(fx2_to_valid_match)
    fx2_to_fx1_norm_col  = vt.find_next_true_indices(fx2_to_valid_normalizer, fx2_to_fx1_match_col)

    assert fx2_to_fx1_match_col != fx2_to_fx1_norm_col, 'normlizers are matches!'

    fx2_to_hasmatch = [pos is not None for pos in fx2_to_fx1_norm_col]
    # IMAGE 2 Matching Features
    fx2_match = np.where(fx2_to_hasmatch)[0].astype(index_dtype)
    match_col_list = np.array(ut.take(fx2_to_fx1_match_col, fx2_match), dtype=fx2_match.dtype)
    norm_col_list = np.array(ut.take(fx2_to_fx1_norm_col, fx2_match), dtype=fx2_match.dtype)

    # We now have 2d coordinates into fx2_to_fx1
    # Covnert into 1d coordinates for flat indexing into fx2_to_fx1
    _match_index_2d = np.vstack((fx2_match, match_col_list))
    _norm_index_2d  = np.vstack((fx2_match, norm_col_list))
    _shape2d        = fx2_to_fx1.shape
    match_index_1d  = np.ravel_multi_index(_match_index_2d, _shape2d)
    norm_index_1d   = np.ravel_multi_index(_norm_index_2d, _shape2d)

    # Find initial matches
    # IMAGE 1 Matching Features
    fx1_match = fx2_to_fx1.take(match_index_1d)
    fx1_norm  = fx2_to_fx1.take(norm_index_1d)
    # compute constrained ratio score
    match_dist = fx2_to_dist.take(match_index_1d)
    norm_dist  = fx2_to_dist.take(norm_index_1d)

    # package and return
    assigntup = fx2_match, fx1_match, fx1_norm, match_dist, norm_dist
    return assigntup
示例#2
0
def plot_scores(hs, qcx_list, fnum=1):
    print('[dev] plot_scores(fnum=%r)' % fnum)
    topN_gt    = 1
    topN_ranks = 3
    qcx2_res = get_qcx2_res(hs, qcx_list)
    data_scores = []  # matching scores
    data_qpairs = []  # query info (qcx, cx)
    data_gtranks = []  # query info (gtrank)
    # Append all scores to a giant list
    for res in qcx2_res.itervalues():
        # Get gt scores first
        qcx = res.qcx
        top_cxs = np.array(res.topN_cxs(hs, N=topN_ranks, only_gt=False))
        gt_cxs  = np.array(res.topN_cxs(hs, N=topN_gt, only_gt=True))
        top_scores = res.cx2_score[top_cxs]
        np.intersect1d(top_cxs, gt_cxs)
        # list of ranks if score is ground truth otherwise -1
        isgt_ranks = [tx if cx in set(gt_cxs) else -1 for (tx, cx) in enumerate(top_cxs)]
        qcx_pairs  = [(hs.cx2_cid(qcx), hs.cx2_cid(cx)) for cx in top_cxs]
        # Append all scores
        data_scores.extend(top_scores.tolist())
        data_qpairs.extend(qcx_pairs)
        data_gtranks.extend(isgt_ranks)
    data_scores = np.array(data_scores)
    data_qpairs = np.array(data_qpairs)
    data_gtranks = np.array(data_gtranks)

    data_sortx = data_scores.argsort()
    sorted_scores = data_scores[data_sortx]

    # Draw and info
    rank_colorbounds = [
        ((-1, 0), df2.GRAY),
        ((0, 1), df2.TRUE_GREEN),
        ((1, 5), df2.UNKNOWN_PURP),
        ((5, None), df2.FALSE_RED),
    ]
    print('[dev] matching chipscore stats: ' + utool.stats_str(data_scores))
    df2.figure(fnum=fnum, doclf=True, docla=True)
    # Finds the knee
    df2.plot(sorted_scores, color=df2.ORANGE, label='all scores')

    # Plot results with ranks within (low, high) bounds
    colorbounds_iter = reversed(list(enumerate(rank_colorbounds)))
    for count, ((low, high), rankX_color) in colorbounds_iter:
        datarank_flag = utool.inbounds(data_gtranks, low, high)
        rankX_xs = np.where(datarank_flag[data_sortx])[0]
        rankX_ys = sorted_scores[rankX_xs]
        if high is None:
            rankX_label = '%d <= gt rank' % low
        else:
            rankX_label = '%d <= gt rank < %d' % (low, high)
        if len(rankX_ys) > 0:
            df2.plot(rankX_xs, rankX_ys, 'o', color=rankX_color, label=rankX_label, alpha=.5)

    rowid = qcx2_res.itervalues().next().rowid

    df2.set_logyscale_from_data(data_scores)

    df2.set_xlabel('chipmatch index')
    df2.dark_background()
    df2.set_figtitle('matching scores\n' + rowid)
    df2.legend(loc='upper left')
    df2.iup()
    fnum += 1

    score_table = np.vstack((data_scores, data_gtranks, data_qpairs.T)).T
    score_table = score_table[data_sortx[::-1]]

    column_labels = ['score', 'gtrank', 'qcid', 'cid']
    header = 'score_table\nuid=%r' % rowid
    column_type = [float, int, int, int]
    csv_txt = utool.util_csv.numpy_to_csv(score_table,  column_labels, header, column_type)
    print(csv_txt)
    #utool.embed()
    return fnum
示例#3
0
def assign_spatially_constrained_matches(chip2_dlen_sqrd,
                                         kpts1,
                                         kpts2,
                                         H,
                                         fx2_to_fx1,
                                         fx2_to_dist,
                                         match_xy_thresh,
                                         norm_xy_bounds=(0.0, 1.0)):
    """
    Args:
        chip2_dlen_sqrd (dict):
        kpts1 (ndarray[float32_t, ndim=2]):  keypoints
        kpts2 (ndarray[float32_t, ndim=2]):  keypoints
        H (ndarray[float64_t, ndim=2]):  homography/perspective matrix that maps image1 space into image2 space
        fx2_to_fx1 (ndarray): image2s nearest feature indices in image1
        fx2_to_dist (ndarray):
        match_xy_thresh (float):
        norm_xy_bounds (tuple):

    Returns:
        tuple: assigntup(
            fx2_match, - matching feature indices in image 2
            fx1_match, - matching feature indices in image 1
            fx1_norm,  - normmalizing indices in image 1
            match_dist, - descriptor distances between fx2_match and fx1_match
            norm_dist, - descriptor distances between fx2_match and fx1_norm
            )

    CommandLine:
        python -m vtool.matching --test-assign_spatially_constrained_matches

    Example:
        >>> # ENABLE_DOCTEST
        >>> from vtool.matching import *  # NOQA
        >>> kpts1 = np.array([[  6.,   4.,   15.84,    4.66,    7.24,    0.  ],
        ...                   [  9.,   3.,   20.09,    5.76,    6.2 ,    0.  ],
        ...                   [  1.,   1.,   12.96,    1.73,    8.77,    0.  ],])
        >>> kpts2 = np.array([[  2.,   1.,   12.11,    0.38,    8.04,    0.  ],
        ...                   [  5.,   1.,   22.4 ,    1.31,    5.04,    0.  ],
        ...                   [  6.,   1.,   19.25,    1.74,    4.72,    0.  ],])
        >>> match_xy_thresh = .37
        >>> chip2_dlen_sqrd = 1400
        >>> norm_xy_bounds = (0.0, 1.0)
        >>> H = np.array([[ 2,  0, 0],
        >>>               [ 0,  1, 0],
        >>>               [ 0,  0, 1]])
        >>> fx2_to_fx1 = np.array([[2, 1, 0],
        >>>                        [0, 1, 2],
        >>>                        [2, 0, 1]], dtype=np.int32)
        >>> fx2_to_dist = np.array([[.40, .80, .85],
        >>>                         [.30, .50, .60],
        >>>                         [.80, .90, .91]], dtype=np.float32)
        >>> # verify results
        >>> assigntup = assign_spatially_constrained_matches(chip2_dlen_sqrd, kpts1, kpts2, H, fx2_to_fx1, fx2_to_dist, match_xy_thresh, norm_xy_bounds)
        >>> fx2_match, fx1_match, fx1_norm, match_dist, norm_dist = assigntup
        >>> result = ut.list_str(assigntup, precision=3)
        >>> print(result)
        (
            np.array([0, 1, 2], dtype=np.int32),
            np.array([2, 0, 2], dtype=np.int32),
            np.array([1, 1, 0], dtype=np.int32),
            np.array([ 0.4,  0.3,  0.8], dtype=np.float32),
            np.array([ 0.8,  0.5,  0.9], dtype=np.float32),
        )

    Example:

    assigns spatially constrained vsone match using results of nearest
    neighbors.
    """
    import vtool as vt
    index_dtype = fx2_to_fx1.dtype
    # Find spatial errors of keypoints under current homography (kpts1 mapped into image2 space)
    fx2_to_xyerr_sqrd = ktool.get_match_spatial_squared_error(
        kpts1, kpts2, H, fx2_to_fx1)
    fx2_to_xyerr = np.sqrt(fx2_to_xyerr_sqrd)
    fx2_to_xyerr_norm = np.divide(fx2_to_xyerr, np.sqrt(chip2_dlen_sqrd))

    # Find matches and normalizers that satisfy spatial constraints
    fx2_to_valid_match = ut.inbounds(fx2_to_xyerr_norm,
                                     0.0,
                                     match_xy_thresh,
                                     eq=True)
    fx2_to_valid_normalizer = ut.inbounds(fx2_to_xyerr_norm,
                                          *norm_xy_bounds,
                                          eq=True)
    fx2_to_fx1_match_col = vt.find_first_true_indices(fx2_to_valid_match)
    fx2_to_fx1_norm_col = vt.find_next_true_indices(fx2_to_valid_normalizer,
                                                    fx2_to_fx1_match_col)

    assert fx2_to_fx1_match_col != fx2_to_fx1_norm_col, 'normlizers are matches!'

    fx2_to_hasmatch = [pos is not None for pos in fx2_to_fx1_norm_col]
    # IMAGE 2 Matching Features
    fx2_match = np.where(fx2_to_hasmatch)[0].astype(index_dtype)
    match_col_list = np.array(ut.take(fx2_to_fx1_match_col, fx2_match),
                              dtype=fx2_match.dtype)
    norm_col_list = np.array(ut.take(fx2_to_fx1_norm_col, fx2_match),
                             dtype=fx2_match.dtype)

    # We now have 2d coordinates into fx2_to_fx1
    # Covnert into 1d coordinates for flat indexing into fx2_to_fx1
    _match_index_2d = np.vstack((fx2_match, match_col_list))
    _norm_index_2d = np.vstack((fx2_match, norm_col_list))
    _shape2d = fx2_to_fx1.shape
    match_index_1d = np.ravel_multi_index(_match_index_2d, _shape2d)
    norm_index_1d = np.ravel_multi_index(_norm_index_2d, _shape2d)

    # Find initial matches
    # IMAGE 1 Matching Features
    fx1_match = fx2_to_fx1.take(match_index_1d)
    fx1_norm = fx2_to_fx1.take(norm_index_1d)
    # compute constrained ratio score
    match_dist = fx2_to_dist.take(match_index_1d)
    norm_dist = fx2_to_dist.take(norm_index_1d)

    # package and return
    assigntup = fx2_match, fx1_match, fx1_norm, match_dist, norm_dist
    return assigntup