def testdata_matching_affine_inliers_normalized():
    tup = testdata_matching_affine_inliers()
    kpts1, kpts2, fm, aff_inliers, rchip1, rchip2, xy_thresh_sqrd = tup
    kpts1_ma = kpts1.take(fm.T[0].take(aff_inliers), axis=0)
    kpts2_ma = kpts2.take(fm.T[1].take(aff_inliers), axis=0)
    #kpts1_ma, kpts2_ma, rchip1, rchip2, xy_thresh_sqrd = testdata_matching_affine_inliers()
    # Matching affine inliers
    xy1_ma = ktool.get_xys(kpts1_ma)
    xy2_ma = ktool.get_xys(kpts2_ma)
    # Matching affine inliers normalized
    xy1_man, T1 = ltool.whiten_xy_points(xy1_ma)
    xy2_man, T2 = ltool.whiten_xy_points(xy2_ma)
    return xy1_man, xy2_man, rchip1, rchip2, T1, T2
def testdata_matching_affine_inliers_normalized():
    tup = testdata_matching_affine_inliers()
    kpts1, kpts2, fm, aff_inliers, rchip1, rchip2, xy_thresh_sqrd = tup
    kpts1_ma = kpts1.take(fm.T[0].take(aff_inliers), axis=0)
    kpts2_ma = kpts2.take(fm.T[1].take(aff_inliers), axis=0)
    #kpts1_ma, kpts2_ma, rchip1, rchip2, xy_thresh_sqrd = testdata_matching_affine_inliers()
    # Matching affine inliers
    xy1_ma = ktool.get_xys(kpts1_ma)
    xy2_ma = ktool.get_xys(kpts2_ma)
    # Matching affine inliers normalized
    xy1_man, T1 = ltool.whiten_xy_points(xy1_ma)
    xy2_man, T2 = ltool.whiten_xy_points(xy2_ma)
    return xy1_man, xy2_man, rchip1, rchip2, T1, T2
Exemple #3
0
 def kpts_dlen_sqrd(tx):
     kpts2 = topx2_kpts[tx]
     aid = topx2_aid[tx]
     fm    = aid2_fm[aid]
     x_m, y_m = ktool.get_xys(kpts2[fm[:, 1]])
     dlensqrd = (x_m.max() - x_m.min()) ** 2 + (y_m.max() - y_m.min()) ** 2
     return dlensqrd
Exemple #4
0
 def kpts_dlen_sqrd(tx):
     kpts2 = topx2_kpts[tx]
     aid = topx2_aid[tx]
     fm = aid2_fm[aid]
     x_m, y_m = ktool.get_xys(kpts2[fm[:, 1]])
     dlensqrd = (x_m.max() - x_m.min())**2 + (y_m.max() - y_m.min())**2
     return dlensqrd
Exemple #5
0
def perterb_kpts(kpts, xy_std=None, invV_std=None, ori_std=None, damping=None,
                 seed=None, **kwargs):
    """ Adds normally distributed pertibations to keypoints """
    # TODO: Move to ktool
    # Get standard deviations of pertibations
    if xy_std is None:
        xy_std   = ktool.get_xys(kpts).std(1) + mtool.eps
    if invV_std is None:
        invV_std = ktool.get_invVs(kpts).std(1) + mtool.eps
    if ori_std is None:
        ori_std  = ktool.get_oris(kpts).std() + mtool.eps
    xy_std = np.array(xy_std, dtype=ktool.KPTS_DTYPE)
    invV_std = np.array(invV_std, dtype=ktool.KPTS_DTYPE)
    if damping is not None:
        xy_std /= damping
        invV_std /= damping
        ori_std /= damping
    if seed is not None:
        np.random.seed(seed)
    # Create normally distributed pertibations
    xy_aug   = np.random.normal(0, scale=xy_std, size=(len(kpts), 2)).astype(ktool.KPTS_DTYPE)
    try:
        invV_aug = np.random.normal(0, scale=invV_std, size=(len(kpts), 3)).astype(ktool.KPTS_DTYPE)
    except ValueError as ex:
        ut.printex(ex, key_list=[(type, 'invV_std')])
        raise
    ori_aug = np.random.normal(0, scale=ori_std, size=(len(kpts), 1)).astype(ktool.KPTS_DTYPE)
    # Augment keypoints
    aug = np.hstack((xy_aug, invV_aug, ori_aug))
    kpts_ = kpts + aug
    # Ensure keypoint feasibility
    kpts_ = force_kpts_feasibility(kpts_)
    #print(ut.dict_str({key: type(val) if not isinstance(val, np.ndarray) else val.dtype for key, val in locals().items()}))
    #assert kpts_.dtype == ktool.KPTS_DTYPE, 'bad cast somewhere kpts_.dtype=%r' % (kpts_.dtype)
    return kpts_
def get_normalized_affine_inliers(kpts1, kpts2, fm, aff_inliers):
    """
    returns xy-inliers that are normalized to have a mean of 0 and std of 1 as
    well as the transformations so the inverse can be taken
    """
    fm_affine = fm.take(aff_inliers, axis=0)
    # Get corresponding points and shapes
    kpts1_ma = kpts1.take(fm_affine.T[0], axis=0)
    kpts2_ma = kpts2.take(fm_affine.T[1], axis=0)
    #kpts1_ma = kpts1.take(fm_affine.T[0], axis=0)
    #kpts2_ma = kpts2.take(fm_affine.T[1], axis=0)
    # Normalize affine inliers xy locations
    xy1_ma = ktool.get_xys(kpts1_ma)
    xy2_ma = ktool.get_xys(kpts2_ma)
    xy1_man, T1 = ltool.whiten_xy_points(xy1_ma)
    xy2_man, T2 = ltool.whiten_xy_points(xy2_ma)
    return xy1_man, xy2_man, T1, T2
def get_normalized_affine_inliers(kpts1, kpts2, fm, aff_inliers):
    """
    returns xy-inliers that are normalized to have a mean of 0 and std of 1 as
    well as the transformations so the inverse can be taken
    """
    fm_affine = fm.take(aff_inliers, axis=0)
    # Get corresponding points and shapes
    kpts1_ma = kpts1.take(fm_affine.T[0], axis=0)
    kpts2_ma = kpts2.take(fm_affine.T[1], axis=0)
    #kpts1_ma = kpts1.take(fm_affine.T[0], axis=0)
    #kpts2_ma = kpts2.take(fm_affine.T[1], axis=0)
    # Normalize affine inliers xy locations
    xy1_ma = ktool.get_xys(kpts1_ma)
    xy2_ma = ktool.get_xys(kpts2_ma)
    xy1_man, T1 = ltool.whiten_xy_points(xy1_ma)
    xy2_man, T2 = ltool.whiten_xy_points(xy2_ma)
    return xy1_man, xy2_man, T1, T2
Exemple #8
0
def orientation_actors(kpts, H=None):
    """ creates orientation actors w.r.t. the gravity vector """
    import vtool.keypoint as ktool

    try:
        # Get xy diretion of the keypoint orientations
        _xs, _ys = ktool.get_xys(kpts)
        _iv11s, _iv21s, _iv22s = ktool.get_invVs(kpts)
        _oris = ktool.get_oris(kpts)
        # mpl's 0 ori == (-tau / 4) w.r.t GRAVITY_THETA
        abs_oris = _oris + ktool.GRAVITY_THETA
        _sins = np.sin(abs_oris)
        _coss = np.cos(abs_oris)
        # The following is essentially
        # invV.dot(R)
        _dxs = _coss * _iv11s
        _dys = _coss * _iv21s + _sins * _iv22s
        # ut.embed()

        # if H is not None:
        #    # adjust for homogrpahy
        #    import vtool as vt
        #    _xs, _ys = vt.transform_points_with_homography(H, np.vstack((_xs, _ys)))
        #    _dxs, _dys = vt.transform_points_with_homography(H, np.vstack((_dxs, _dys)))

        # head_width_list = np.log(_iv11s * _iv22s) / 5
        head_width_list = np.ones(len(_iv11s)) / 10
        kwargs = {
            'length_includes_head': True,
            'shape': 'full',
            'overhang': 0,
            'head_starts_at_zero': False,
        }
        if H is not None:
            kwargs['transform'] = HomographyTransform(H)

        ori_actors = [
            mpl.patches.FancyArrow(x, y, dx, dy, head_width=hw, **kwargs)
            for (x, y, dx, dy,
                 hw) in zip(_xs, _ys, _dxs, _dys, head_width_list)
        ]
    except ValueError as ex:
        print('\n[mplkp.2] !!! ERROR %s: ' % str(ex))
        print('_oris.shape = %r' % (_oris.shape, ))
        # print('x, y, dx, dy = %r' % ((x, y, dx, dy),))
        print('_dxs = %r' % (_dxs, ))
        print('_dys = %r' % (_dys, ))
        print('_xs = %r' % (_xs, ))
        print('_ys = %r' % (_ys, ))
        raise

    return ori_actors
Exemple #9
0
def orientation_actors(kpts, H=None):
    """ creates orientation actors w.r.t. the gravity vector """
    try:
        # Get xy diretion of the keypoint orientations
        _xs, _ys = ktool.get_xys(kpts)
        _iv11s, _iv21s, _iv22s = ktool.get_invVs(kpts)
        _oris = ktool.get_oris(kpts)
        # mpl's 0 ori == (-tau / 4) w.r.t GRAVITY_THETA
        abs_oris = _oris + ktool.GRAVITY_THETA
        _sins = np.sin(abs_oris)
        _coss = np.cos(abs_oris)
        # The following is essentially
        # invV.dot(R)
        _dxs = _coss * _iv11s
        _dys = _coss * _iv21s +  _sins * _iv22s
        #ut.embed()

        #if H is not None:
        #    # adjust for homogrpahy
        #    import vtool as vt
        #    _xs, _ys = vt.transform_points_with_homography(H, np.vstack((_xs, _ys)))
        #    _dxs, _dys = vt.transform_points_with_homography(H, np.vstack((_dxs, _dys)))

        #head_width_list = np.log(_iv11s * _iv22s) / 5
        head_width_list = np.ones(len(_iv11s)) / 10
        kwargs = {
            'length_includes_head': True,
            'shape': 'full',
            'overhang': 0,
            'head_starts_at_zero': False,
        }
        if H is not None:
            kwargs['transform'] = HomographyTransform(H)

        ori_actors = [mpl.patches.FancyArrow(x, y, dx, dy, head_width=hw, **kwargs)
                      for (x, y, dx, dy, hw) in
                      zip(_xs, _ys, _dxs, _dys, head_width_list)]
    except ValueError as ex:
        print('\n[mplkp.2] !!! ERROR %s: ' % str(ex))
        print('_oris.shape = %r' % (_oris.shape,))
        print('x, y, dx, dy = %r' % ((x, y, dx, dy),))
        print('_dxs = %r' % (_dxs,))
        print('_dys = %r' % (_dys,))
        print('_xs = %r' % (_xs,))
        print('_ys = %r' % (_ys,))
        raise

    return ori_actors
def test_affine_errors(H, kpts1, kpts2, fm, xy_thresh_sqrd, scale_thresh_sqrd,
                       ori_thresh):
    """
    used for refinement as opposed to initial estimation
    """
    kpts1_m = kpts1.take(fm.T[0], axis=0)
    kpts2_m = kpts2.take(fm.T[1], axis=0)
    invVR1s_m = ktool.get_invVR_mats3x3(kpts1_m)
    xy2_m = ktool.get_xys(kpts2_m)
    det2_m = ktool.get_sqrd_scales(kpts2_m)
    ori2_m = ktool.get_oris(kpts2_m)
    refined_inliers, refined_errors = _test_hypothesis_inliers(
        H, invVR1s_m, xy2_m, det2_m, ori2_m, xy_thresh_sqrd, scale_thresh_sqrd,
        ori_thresh)
    refined_tup1 = (refined_inliers, refined_errors, H)
    return refined_tup1
def test_affine_errors(H, kpts1, kpts2, fm, xy_thresh_sqrd, scale_thresh_sqrd,
                       ori_thresh):
    """
    used for refinement as opposed to initial estimation
    """
    kpts1_m = kpts1.take(fm.T[0], axis=0)
    kpts2_m = kpts2.take(fm.T[1], axis=0)
    invVR1s_m = ktool.get_invVR_mats3x3(kpts1_m)
    xy2_m  = ktool.get_xys(kpts2_m)
    det2_m = ktool.get_sqrd_scales(kpts2_m)
    ori2_m = ktool.get_oris(kpts2_m)
    refined_inliers, refined_errors = _test_hypothesis_inliers(
        H, invVR1s_m, xy2_m, det2_m, ori2_m, xy_thresh_sqrd, scale_thresh_sqrd,
        ori_thresh)
    refined_tup1 = (refined_inliers, refined_errors, H)
    return refined_tup1
Exemple #12
0
def perterb_kpts(kpts,
                 xy_std=None,
                 invV_std=None,
                 ori_std=None,
                 damping=None,
                 seed=None,
                 **kwargs):
    """ Adds normally distributed pertibations to keypoints """
    # TODO: Move to ktool
    # Get standard deviations of pertibations
    if xy_std is None:
        xy_std = ktool.get_xys(kpts).std(1) + mtool.eps
    if invV_std is None:
        invV_std = ktool.get_invVs(kpts).std(1) + mtool.eps
    if ori_std is None:
        ori_std = ktool.get_oris(kpts).std() + mtool.eps
    xy_std = np.array(xy_std, dtype=ktool.KPTS_DTYPE)
    invV_std = np.array(invV_std, dtype=ktool.KPTS_DTYPE)
    if damping is not None:
        xy_std /= damping
        invV_std /= damping
        ori_std /= damping
    if seed is not None:
        np.random.seed(seed)
    # Create normally distributed pertibations
    xy_aug = np.random.normal(0, scale=xy_std,
                              size=(len(kpts), 2)).astype(ktool.KPTS_DTYPE)
    try:
        invV_aug = np.random.normal(0, scale=invV_std,
                                    size=(len(kpts),
                                          3)).astype(ktool.KPTS_DTYPE)
    except ValueError as ex:
        ut.printex(ex, key_list=[(type, 'invV_std')])
        raise
    ori_aug = np.random.normal(0, scale=ori_std,
                               size=(len(kpts), 1)).astype(ktool.KPTS_DTYPE)
    # Augment keypoints
    aug = np.hstack((xy_aug, invV_aug, ori_aug))
    kpts_ = kpts + aug
    # Ensure keypoint feasibility
    kpts_ = force_kpts_feasibility(kpts_)
    #print(ut.dict_str({key: type(val) if not isinstance(val, np.ndarray) else val.dtype for key, val in locals().items()}))
    #assert kpts_.dtype == ktool.KPTS_DTYPE, 'bad cast somewhere kpts_.dtype=%r' % (kpts_.dtype)
    return kpts_
Exemple #13
0
def draw_keypoints(ax,
                   kpts_,
                   scale_factor=1.0,
                   offset=(0.0, 0.0),
                   rotation=0.0,
                   ell=True,
                   pts=False,
                   rect=False,
                   eig=False,
                   ori=False,
                   sifts=None,
                   siftkw={},
                   H=None,
                   **kwargs):
    """
    draws keypoints extracted by pyhesaff onto a matplotlib axis

    FIXME: There is probably a matplotlib bug here. If you specify two different
    alphas in a collection, whatever the last alpha was gets applied to
    everything

    Args:
        ax (mpl.Axes):
        kpts (ndarray): keypoints [[x, y, a, c, d, theta], ...]
        scale_factor (float):
        offset (tuple):
        rotation (float):
        ell (bool):
        pts (bool):
        rect (bool):
        eig (bool):
        ori (bool):
        sifts (None):

    References:
        http://stackoverflow.com/questions/28401788/transforms-non-affine-patch

    CommandLine:
        python -m wbia.plottool.mpl_keypoint draw_keypoints --show

    Example:
        >>> # ENABLE_DOCTEST
        >>> from wbia.plottool.mpl_keypoint import *  # NOQA
        >>> from wbia.plottool.mpl_keypoint import _draw_patches, _draw_pts  # NOQA
        >>> import wbia.plottool as pt
        >>> import vtool as vt
        >>> imgBGR = vt.get_star_patch(jitter=True)
        >>> H = np.array([[1, 0, 0], [.5, 2, 0], [0, 0, 1]])
        >>> H = np.array([[.8, 0, 0], [0, .8, 0], [0, 0, 1]])
        >>> H = None
        >>> TAU = 2 * np.pi
        >>> kpts_ = vt.make_test_image_keypoints(imgBGR, scale=.5, skew=2, theta=TAU / 8.0)
        >>> scale_factor=1.0
        >>> #offset=(0.0, -4.0)
        >>> offset=(0.0, 0.0)
        >>> rotation=0.0
        >>> ell=True
        >>> pts=True
        >>> rect=True
        >>> eig=True
        >>> ori=True
        >>> # make random sifts
        >>> sifts = mpl_sift.testdata_sifts()
        >>> siftkw = {}
        >>> kwargs = dict(ori_color=[0, 1, 0], rect_color=[0, 0, 1],
        >>>               eig_color=[1, 1, 0], pts_size=.1)
        >>> w, h = imgBGR.shape[0:2][::-1]
        >>> imgBGR_ = imgBGR if H is None else vt.warpAffine(
        >>>     imgBGR, H, (int(w * .8), int(h * .8)))
        >>> fig, ax = pt.imshow(imgBGR_ * 255)
        >>> draw_keypoints(ax, kpts_, scale_factor, offset, rotation, ell, pts,
        ...                rect, eig, ori, sifts, siftkw, H=H, **kwargs)
        >>> pt.iup()
        >>> pt.show_if_requested()
    """
    import vtool.keypoint as ktool

    if kpts_.shape[1] == 2:
        # pad out structure if only xy given
        kpts = np.zeros((len(kpts_), 6))
        kpts[:, 0:2] = kpts_
        kpts[:, 2] = 1
        kpts[:, 4] = 1
        kpts_ = kpts

    if scale_factor is None:
        scale_factor = 1.0
    # print('[mpl_keypoint.draw_keypoints] kwargs = ' + ut.repr2(kwargs))
    # ellipse and point properties
    pts_size = kwargs.get('pts_size', 2)
    pts_alpha = kwargs.get('pts_alpha', 1.0)
    ell_alpha = kwargs.get('ell_alpha', 1.0)
    ell_linewidth = kwargs.get('ell_linewidth', 2)
    ell_color = kwargs.get('ell_color', None)
    if ell_color is None:
        ell_color = [1, 0, 0]
    # colors
    pts_color = kwargs.get('pts_color', ell_color)
    rect_color = kwargs.get('rect_color', ell_color)
    eig_color = kwargs.get('eig_color', ell_color)
    ori_color = kwargs.get('ori_color', ell_color)
    # linewidths
    eig_linewidth = kwargs.get('eig_linewidth', ell_linewidth)
    rect_linewidth = kwargs.get('rect_linewidth', ell_linewidth)
    ori_linewidth = kwargs.get('ori_linewidth', ell_linewidth)
    # Offset keypoints
    assert len(kpts_) > 0, 'cannot draw no keypoints1'
    kpts = ktool.offset_kpts(kpts_, offset, scale_factor)
    assert len(kpts) > 0, 'cannot draw no keypoints2'
    # Build list of keypoint shape transforms from unit circles to ellipes
    invVR_aff2Ds = get_invVR_aff2Ds(kpts, H=H)
    try:
        if sifts is not None:
            # SIFT descriptors
            pass_props(
                kwargs,
                siftkw,
                'bin_color',
                'arm1_color',
                'arm2_color',
                'arm1_lw',
                'arm2_lw',
                'stroke',
                'arm_alpha',
                'arm_alpha',
                'multicolored_arms',
            )
            mpl_sift.draw_sifts(ax, sifts, invVR_aff2Ds, **siftkw)
        if rect:
            # Bounding Rectangles
            rect_patches = rectangle_actors(invVR_aff2Ds)
            _draw_patches(ax, rect_patches, rect_color, ell_alpha,
                          rect_linewidth)
        if ell:
            # Keypoint shape
            ell_patches = ellipse_actors(invVR_aff2Ds)
            _draw_patches(ax, ell_patches, ell_color, ell_alpha, ell_linewidth)
        if eig:
            # Shape eigenvectors
            eig_patches = eigenvector_actors(invVR_aff2Ds)
            _draw_patches(ax, eig_patches, eig_color, ell_alpha, eig_linewidth)
        if ori:
            # Keypoint orientation
            ori_patches = orientation_actors(kpts, H=H)
            _draw_patches(ax, ori_patches, ori_color, ell_alpha, ori_linewidth,
                          ori_color)
        if pts:
            # Keypoint locations
            _xs, _ys = ktool.get_xys(kpts)
            if H is not None:
                # adjust for homogrpahy
                import vtool as vt

                _xs, _ys = vt.transform_points_with_homography(
                    H, np.vstack((_xs, _ys)))

            pts_patches = _draw_pts(ax, _xs, _ys, pts_size, pts_color,
                                    pts_alpha)
            if pts_patches is not None:
                _draw_patches(ax, pts_patches, 'none', pts_alpha, pts_size,
                              pts_color)
    except ValueError as ex:
        ut.printex(ex, '\n[mplkp] !!! ERROR')
        # print('_oris.shape = %r' % (_oris.shape,))
        # print('_xs.shape = %r' % (_xs.shape,))
        # print('_iv11s.shape = %r' % (_iv11s.shape,))
        raise
def test_homog_errors(H,
                      kpts1,
                      kpts2,
                      fm,
                      xy_thresh_sqrd,
                      scale_thresh,
                      ori_thresh,
                      full_homog_checks=True):
    r"""
    Test to see which keypoints the homography correctly maps

    Args:
        H (ndarray[float64_t, ndim=2]):  homography/perspective matrix
        kpts1 (ndarray[float32_t, ndim=2]):  keypoints
        kpts2 (ndarray[float32_t, ndim=2]):  keypoints
        fm (list):  list of feature matches as tuples (qfx, dfx)
        xy_thresh_sqrd (float):
        scale_thresh (float):
        ori_thresh (float):  angle in radians
        full_homog_checks (bool):

    Returns:
        tuple: homog_tup1

    CommandLine:
        python -m vtool.spatial_verification --test-test_homog_errors:0 --show
        python -m vtool.spatial_verification --test-test_homog_errors:0 --show --rotation_invariance
        python -m vtool.spatial_verification --test-test_homog_errors:0 --show --rotation_invariance --no-affine-invariance --xy-thresh=.001
        python -m vtool.spatial_verification --test-test_homog_errors:0 --show --rotation_invariance --no-affine-invariance --xy-thresh=.001 --no-full-homog-checks
        python -m vtool.spatial_verification --test-test_homog_errors:0 --show --no-full-homog-checks
        # --------------
        # Shows (sorta) how inliers are computed
        python -m vtool.spatial_verification --test-test_homog_errors:1 --show
        python -m vtool.spatial_verification --test-test_homog_errors:1 --show --rotation_invariance
        python -m vtool.spatial_verification --test-test_homog_errors:1 --show --rotation_invariance --no-affine-invariance --xy-thresh=.001
        python -m vtool.spatial_verification --test-test_homog_errors:1 --show --rotation_invariance --xy-thresh=.001
        python -m vtool.spatial_verification --test-test_homog_errors:0 --show --rotation_invariance --xy-thresh=.001

    Example0:
        >>> # DISABLE_DOCTEST
        >>> from vtool.spatial_verification import *  # NOQA
        >>> import plottool as pt
        >>> kpts1, kpts2, fm, aff_inliers, rchip1, rchip2, xy_thresh_sqrd = testdata_matching_affine_inliers()
        >>> H = estimate_refined_transform(kpts1, kpts2, fm, aff_inliers)
        >>> scale_thresh, ori_thresh = 2.0, 1.57
        >>> full_homog_checks = not ut.get_argflag('--no-full-homog-checks')
        >>> homog_tup1 = test_homog_errors(H, kpts1, kpts2, fm, xy_thresh_sqrd, scale_thresh, ori_thresh, full_homog_checks)
        >>> homog_tup = (homog_tup1[0], homog_tup1[2])
        >>> ut.quit_if_noshow()
        >>> pt.draw_sv.show_sv(rchip1, rchip2, kpts1, kpts2, fm, homog_tup=homog_tup)
        >>> ut.show_if_requested()

    Example1:
        >>> # DISABLE_DOCTEST
        >>> from vtool.spatial_verification import *  # NOQA
        >>> import plottool as pt
        >>> kpts1, kpts2, fm_, aff_inliers, rchip1, rchip2, xy_thresh_sqrd = testdata_matching_affine_inliers()
        >>> H = estimate_refined_transform(kpts1, kpts2, fm_, aff_inliers)
        >>> scale_thresh, ori_thresh = 2.0, 1.57
        >>> full_homog_checks = not ut.get_argflag('--no-full-homog-checks')
        >>> # ----------------
        >>> # Take subset of feature matches
        >>> fm = fm_
        >>> scale_err, xy_err, ori_err = \
        ...     ut.exec_func_src(test_homog_errors, globals(), locals(),
        ...     'scale_err, xy_err, ori_err'.split(', '))
        >>> # we only care about checking out scale and orientation here. ignore bad xy points
        >>> xy_inliers_flag = np.less(xy_err, xy_thresh_sqrd)
        >>> scale_err[~xy_inliers_flag] = 0
        >>> # filter
        >>> fm = fm_[np.array(scale_err).argsort()[::-1][:10]]
        >>> fm = fm_[np.array(scale_err).argsort()[::-1][:10]]
        >>> # Exec sourcecode
        >>> kpts1_m, kpts2_m, off_xy1_m, off_xy1_mt, dxy1_m, dxy1_mt, xy2_m, xy1_m, xy1_mt, scale_err, xy_err, ori_err = \
        ...     ut.exec_func_src(test_homog_errors, globals(), locals(),
        ...     'kpts1_m, kpts2_m, off_xy1_m, off_xy1_mt, dxy1_m, dxy1_mt, xy2_m, xy1_m, xy1_mt, scale_err, xy_err, ori_err'.split(', '))
        >>> #---------------
        >>> ut.quit_if_noshow()
        >>> pt.figure(fnum=1, pnum=(1, 2, 1), title='orig points and offset point')
        >>> segments_list1 = np.array(list(zip(xy1_m.T.tolist(), off_xy1_m.T.tolist())))
        >>> pt.draw_line_segments(segments_list1, color=pt.LIGHT_BLUE)
        >>> pt.dark_background()
        >>> #---------------
        >>> pt.figure(fnum=1, pnum=(1, 2, 2), title='transformed points and matching points')
        >>> #---------------
        >>> # first have to make corresponding offset points
        >>> # Use reference point for scale and orientation tests
        >>> oris2_m   = ktool.get_oris(kpts2_m)
        >>> scales2_m = ktool.get_scales(kpts2_m)
        >>> dxy2_m    = np.vstack((np.sin(oris2_m), -np.cos(oris2_m)))
        >>> scaled_dxy2_m = dxy2_m * scales2_m[None, :]
        >>> off_xy2_m = xy2_m + scaled_dxy2_m
        >>> # Draw transformed semgents
        >>> segments_list2 = np.array(list(zip(xy2_m.T.tolist(), off_xy2_m.T.tolist())))
        >>> pt.draw_line_segments(segments_list2, color=pt.GREEN)
        >>> # Draw corresponding matches semgents
        >>> segments_list3 = np.array(list(zip(xy1_mt.T.tolist(), off_xy1_mt.T.tolist())))
        >>> pt.draw_line_segments(segments_list3, color=pt.RED)
        >>> # Draw matches between correspondences
        >>> segments_list4 = np.array(list(zip(xy1_mt.T.tolist(), xy2_m.T.tolist())))
        >>> pt.draw_line_segments(segments_list4, color=pt.ORANGE)
        >>> pt.dark_background()
        >>> #---------------
        >>> #vt.get _xy_axis_extents(kpts1_m)
        >>> #pt.draw_sv.show_sv(rchip1, rchip2, kpts1, kpts2, fm, homog_tup=homog_tup)
        >>> ut.show_if_requested()
    """
    kpts1_m = kpts1.take(fm.T[0], axis=0)
    kpts2_m = kpts2.take(fm.T[1], axis=0)
    # Transform all xy1 matches to xy2 space
    xy1_m = ktool.get_xys(kpts1_m)
    #with ut.embed_on_exception_context:
    xy1_mt = ltool.transform_points_with_homography(H, xy1_m)
    #xy1_mt  = ktool.transform_kpts_xys(H, kpts1_m)
    xy2_m = ktool.get_xys(kpts2_m)
    # --- Find (Squared) Homography Distance Error ---
    # You cannot test for scale or orientation easily here because
    # you no longer have an ellipse? (maybe, probably have a conic) when using a
    # projective transformation
    xy_err = dtool.L2_sqrd(xy1_mt.T, xy2_m.T)
    # Estimate final inliers
    #ut.embed()
    if full_homog_checks:
        # TODO: may need to use more than one reference point
        # Use reference point for scale and orientation tests
        oris1_m = ktool.get_oris(kpts1_m)
        scales1_m = ktool.get_scales(kpts1_m)
        # Get point offsets with unit length
        dxy1_m = np.vstack((np.sin(oris1_m), -np.cos(oris1_m)))
        scaled_dxy1_m = dxy1_m * scales1_m[None, :]
        off_xy1_m = xy1_m + scaled_dxy1_m
        # transform reference point
        off_xy1_mt = ltool.transform_points_with_homography(H, off_xy1_m)
        scaled_dxy1_mt = xy1_mt - off_xy1_mt
        scales1_mt = npl.norm(scaled_dxy1_mt, axis=0)
        #with warnings.catch_warnings():
        #    warnings.simplefilter("ignore")
        dxy1_mt = scaled_dxy1_mt / scales1_mt
        # adjust for gravity vector being 0
        oris1_mt = np.arctan2(dxy1_mt[1], dxy1_mt[0]) - ktool.GRAVITY_THETA
        _det1_mt = scales1_mt**2
        det2_m = ktool.get_sqrd_scales(kpts2_m)
        ori2_m = ktool.get_oris(kpts2_m)
        #xy_err    = dtool.L2_sqrd(xy2_m.T, _xy1_mt.T)
        scale_err = dtool.det_distance(_det1_mt, det2_m)
        ori_err = dtool.ori_distance(oris1_mt, ori2_m)
        ###
        xy_inliers_flag = np.less(xy_err, xy_thresh_sqrd)
        scale_inliers_flag = np.less(scale_err, scale_thresh)
        ori_inliers_flag = np.less(ori_err, ori_thresh)
        hypo_inliers_flag = xy_inliers_flag  # Try to re-use memory
        np.logical_and(hypo_inliers_flag,
                       ori_inliers_flag,
                       out=hypo_inliers_flag)
        np.logical_and(hypo_inliers_flag,
                       scale_inliers_flag,
                       out=hypo_inliers_flag)
        # Seems slower due to memory
        #hypo_inliers_flag = np.logical_and.reduce(
        #    [xy_inliers_flag, ori_inliers_flag, scale_inliers_flag])
        # this is also slower
        #hypo_inliers_flag = np.logical_and.reduce((xy_inliers_flag,
        #ori_inliers_flag, scale_inliers_flag), out=xy_inliers_flag)
        refined_inliers = np.where(hypo_inliers_flag)[0].astype(INDEX_DTYPE)
        refined_errors = (xy_err, ori_err, scale_err)
    else:
        refined_inliers = np.where(
            xy_err < xy_thresh_sqrd)[0].astype(INDEX_DTYPE)
        refined_errors = (xy_err, None, None)
    homog_tup1 = (refined_inliers, refined_errors, H)
    return homog_tup1
def get_affine_inliers(kpts1, kpts2, fm, fs, xy_thresh_sqrd, scale_thresh_sqrd,
                       ori_thresh):
    """
    Estimates inliers deterministically using elliptical shapes

    Compute all transforms from kpts1 to kpts2 (enumerate all hypothesis)
    We transform from chip1 -> chip2
    The determinants are squared keypoint scales

    FROM PERDOCH 2009::
        H = inv(Aj).dot(Rj.T).dot(Ri).dot(Ai)
        H = inv(Aj).dot(Ai)
        The input invVs = perdoch.invA's

    CommandLine:
        python -m vtool.spatial_verification --test-get_affine_inliers

    Example:
        >>> # ENABLE_DOCTEST
        >>> from vtool.spatial_verification import *  # NOQA
        >>> import vtool.tests.dummy as dummy
        >>> import vtool.keypoint as ktool
        >>> kpts1, kpts2 = dummy.get_dummy_kpts_pair((100, 100))
        >>> fm = dummy.make_dummy_fm(len(kpts1)).astype(np.int32)
        >>> fs = np.ones(len(fm), dtype=np.float64)
        >>> xy_thresh_sqrd = ktool.KPTS_DTYPE(.009) ** 2
        >>> scale_thresh_sqrd = ktool.KPTS_DTYPE(2)
        >>> ori_thresh = ktool.KPTS_DTYPE(TAU / 4)
        >>> output = get_affine_inliers(kpts1, kpts2, fm, fs, xy_thresh_sqrd,
        >>>                             scale_thresh_sqrd, ori_thresh)
        >>> result = ut.hashstr(output)
        >>> print(result)
        89kz8nh6p+66t!+u

    Ignore::
        from vtool.spatial_verification import *  # NOQA
        import vtool.tests.dummy as dummy
        import vtool.keypoint as ktool
        kpts1, kpts2 = dummy.get_dummy_kpts_pair((100, 100))
        a = kpts1[fm.T[0]]
        b = kpts1.take(fm.T[0])

        align = fm.dtype.itemsize * fm.shape[1]
        align2 = [fm.dtype.itemsize, fm.dtype.itemsize]
        viewtype1 = np.dtype(np.void, align)
        viewtype2 = np.dtype(np.int32, align2)
        c = np.ascontiguousarray(fm).view(viewtype1)
        fm_view = np.ascontiguousarray(fm).view(viewtype1)
        qfx = fm.view(np.dtype(np.int32 np.int32.itemsize))
        dfx = fm.view(np.dtype(np.int32, np.int32.itemsize))
        d = np.ascontiguousarray(c).view(viewtype2)

        fm.view(np.dtype(np.void, align))
        np.ascontiguousarray(fm).view(np.dtype((np.void, Z.dtype.itemsize * Z.shape[1])))
    """
    #http://ipython-books.github.io/featured-01/
    kpts1_m = kpts1.take(fm.T[0], axis=0)
    kpts2_m = kpts2.take(fm.T[1], axis=0)

    # Get keypoints to project in matrix form
    #invVR2s_m = ktool.get_invV_mats(kpts2_m, with_trans=True, with_ori=True)
    #invVR1s_m = ktool.get_invV_mats(kpts1_m, with_trans=True, with_ori=True)
    invVR2s_m = ktool.get_invVR_mats3x3(kpts2_m)
    invVR1s_m = ktool.get_invVR_mats3x3(kpts1_m)
    RV1s_m = ktool.invert_invV_mats(invVR1s_m)  # 539 us
    # BUILD ALL HYPOTHESIS TRANSFORMS: The transform from kp1 to kp2 is:
    Aff_mats = matrix_multiply(invVR2s_m, RV1s_m)
    # Get components to test projects against
    xy2_m = ktool.get_xys(kpts2_m)
    det2_m = ktool.get_sqrd_scales(kpts2_m)
    ori2_m = ktool.get_oris(kpts2_m)
    # SLOWER EQUIVALENT
    # RV1s_m    = ktool.get_V_mats(kpts1_m, with_trans=True, with_ori=True)  # 5.2 ms
    # xy2_m  = ktool.get_invVR_mats_xys(invVR2s_m)
    # ori2_m = ktool.get_invVR_mats_oris(invVR2s_m)
    # assert np.all(ktool.get_oris(kpts2_m) == ktool.get_invVR_mats_oris(invVR2s_m))
    # assert np.all(ktool.get_xys(kpts2_m) == ktool.get_invVR_mats_xys(invVR2s_m))

    # The previous versions of this function were all roughly comparable.
    # The for loop one was the slowest. I'm deciding to go with the one
    # where there is no internal function definition. It was moderately faster,
    # and it gives us access to profile that function
    inliers_and_errors_list = [
        _test_hypothesis_inliers(Aff, invVR1s_m, xy2_m, det2_m, ori2_m,
                                 xy_thresh_sqrd, scale_thresh_sqrd, ori_thresh)
        for Aff in Aff_mats
    ]
    aff_inliers_list = [tup[0] for tup in inliers_and_errors_list]
    aff_errors_list = [tup[1] for tup in inliers_and_errors_list]
    return aff_inliers_list, aff_errors_list, Aff_mats
Exemple #16
0
def draw_keypoints(ax, kpts_, scale_factor=1.0, offset=(0.0, 0.0), rotation=0.0,
                   ell=True, pts=False, rect=False, eig=False, ori=False,
                   sifts=None, siftkw={}, H=None, **kwargs):
    """
    draws keypoints extracted by pyhesaff onto a matplotlib axis

    FIXME: There is probably a matplotlib bug here. If you specify two different
    alphas in a collection, whatever the last alpha was gets applied to
    everything

    Args:
        ax (mpl.Axes):
        kpts (ndarray): keypoints [[x, y, a, c, d, theta], ...]
        scale_factor (float):
        offset (tuple):
        rotation (float):
        ell (bool):
        pts (bool):
        rect (bool):
        eig (bool):
        ori (bool):
        sifts (None):

    References:
        http://stackoverflow.com/questions/28401788/using-homogeneous-transforms-non-affine-with-matplotlib-patches

    CommandLine:
        python -m plottool.mpl_keypoint --test-draw_keypoints --show

    Example:
        >>> # ENABLE_DOCTEST
        >>> from plottool.mpl_keypoint import *  # NOQA
        >>> from plottool.mpl_keypoint import _draw_patches, _draw_pts  # NOQA
        >>> import plottool as pt
        >>> import vtool as vt
        >>> imgBGR = vt.get_star_patch(jitter=True)
        >>> H = np.array([[1, 0, 0], [.5, 2, 0], [0, 0, 1]])
        >>> H = None
        >>> TAU = 2 * np.pi
        >>> kpts_ = vt.make_test_image_keypoints(imgBGR, scale=.5, skew=2, theta=TAU / 8.0)
        >>> scale_factor=1.0
        >>> offset=(0.0, 0.0)
        >>> rotation=0.0
        >>> ell=True
        >>> pts=True
        >>> rect=True
        >>> eig=True
        >>> ori=True
        >>> # make random sifts
        >>> sifts = mpl_sift.testdata_sifts()
        >>> siftkw = {}
        >>> kwargs = dict(ori_color=[0, 1, 0], rect_color=[0, 0, 1], eig_color=[1, 1, 0], pts_size=.1)
        >>> w, h = imgBGR.shape[0:2][::-1]
        >>> imgBGR_ = imgBGR if H is None else vt.warpAffine(imgBGR, H, (w * 2, h * 2))
        >>> fig, ax = pt.imshow(imgBGR_ * 255)
        >>> draw_keypoints(ax, kpts_, scale_factor, offset, rotation, ell, pts,
        ...                rect, eig, ori, sifts, siftkw, H=H, **kwargs)
        >>> pt.iup()
        >>> pt.show_if_requested()
    """
    if kpts_.shape[1] == 2:
        # pad out structure if only xy given
        kpts = np.zeros((len(kpts_), 6))
        kpts[:, 0:2] = kpts_
        kpts[:, 2] = 1
        kpts[:, 4] = 1
        kpts_ = kpts

    if scale_factor is None:
        scale_factor = 1.0
    #print('[mpl_keypoint.draw_keypoints] kwargs = ' + ut.dict_str(kwargs))
    # ellipse and point properties
    pts_size       = kwargs.get('pts_size', 2)
    pts_alpha      = kwargs.get('pts_alpha', 1.0)
    ell_alpha      = kwargs.get('ell_alpha', 1.0)
    ell_linewidth  = kwargs.get('ell_linewidth', 2)
    ell_color      = kwargs.get('ell_color', None)
    if ell_color is None:
        ell_color = [1, 0, 0]
    # colors
    pts_color      = kwargs.get('pts_color',  ell_color)
    rect_color     = kwargs.get('rect_color', ell_color)
    eig_color      = kwargs.get('eig_color',  ell_color)
    ori_color      = kwargs.get('ori_color',  ell_color)
    # linewidths
    eig_linewidth  = kwargs.get('eig_linewidth',  ell_linewidth)
    rect_linewidth = kwargs.get('rect_linewidth', ell_linewidth)
    ori_linewidth  = kwargs.get('ori_linewidth',  ell_linewidth)
    # Offset keypoints
    assert len(kpts_) > 0, 'cannot draw no keypoints1'
    kpts = ktool.offset_kpts(kpts_, offset, scale_factor)
    assert len(kpts) > 0, 'cannot draw no keypoints2'
    # Build list of keypoint shape transforms from unit circles to ellipes
    invVR_aff2Ds = get_invVR_aff2Ds(kpts, H=H)
    try:
        if sifts is not None:
            # SIFT descriptors
            pass_props(kwargs, siftkw, 'bin_color', 'arm1_color', 'arm2_color',
                       'arm1_lw', 'arm2_lw', 'arm_alpha', 'arm_alpha', 'multicolored_arms')
            mpl_sift.draw_sifts(ax, sifts, invVR_aff2Ds, **siftkw)
        if rect:
            # Bounding Rectangles
            rect_patches = rectangle_actors(invVR_aff2Ds)
            _draw_patches(ax, rect_patches, rect_color, ell_alpha, rect_linewidth)
        if ell:
            # Keypoint shape
            ell_patches = ellipse_actors(invVR_aff2Ds)
            _draw_patches(ax, ell_patches, ell_color, ell_alpha, ell_linewidth)
        if eig:
            # Shape eigenvectors
            eig_patches = eigenvector_actors(invVR_aff2Ds)
            _draw_patches(ax, eig_patches, eig_color, ell_alpha, eig_linewidth)
        if ori:
            # Keypoint orientation
            ori_patches = orientation_actors(kpts, H=H)
            _draw_patches(ax, ori_patches, ori_color, ell_alpha, ori_linewidth, ori_color)
        if pts:
            # Keypoint locations
            _xs, _ys = ktool.get_xys(kpts)
            if H is not None:
                # adjust for homogrpahy
                import vtool as vt
                _xs, _ys = vt.transform_points_with_homography(H, np.vstack((_xs, _ys)))

            pts_patches = _draw_pts(ax, _xs, _ys, pts_size, pts_color, pts_alpha)
            if pts_patches is not None:
                _draw_patches(ax, pts_patches, 'none', pts_alpha, pts_size, pts_color)
    except ValueError as ex:
        ut.printex(ex, '\n[mplkp] !!! ERROR')
        #print('_oris.shape = %r' % (_oris.shape,))
        #print('_xs.shape = %r' % (_xs.shape,))
        #print('_iv11s.shape = %r' % (_iv11s.shape,))
        raise
def test_homog_errors(H, kpts1, kpts2, fm, xy_thresh_sqrd, scale_thresh,
                      ori_thresh, full_homog_checks=True):
    r"""
    Test to see which keypoints the homography correctly maps

    Args:
        H (ndarray[float64_t, ndim=2]):  homography/perspective matrix
        kpts1 (ndarray[float32_t, ndim=2]):  keypoints
        kpts2 (ndarray[float32_t, ndim=2]):  keypoints
        fm (list):  list of feature matches as tuples (qfx, dfx)
        xy_thresh_sqrd (float):
        scale_thresh (float):
        ori_thresh (float):  angle in radians
        full_homog_checks (bool):

    Returns:
        tuple: homog_tup1

    CommandLine:
        python -m vtool.spatial_verification --test-test_homog_errors:0 --show
        python -m vtool.spatial_verification --test-test_homog_errors:0 --show --rotation_invariance
        python -m vtool.spatial_verification --test-test_homog_errors:0 --show --rotation_invariance --no-affine-invariance --xy-thresh=.001
        python -m vtool.spatial_verification --test-test_homog_errors:0 --show --rotation_invariance --no-affine-invariance --xy-thresh=.001 --no-full-homog-checks
        python -m vtool.spatial_verification --test-test_homog_errors:0 --show --no-full-homog-checks
        # --------------
        # Shows (sorta) how inliers are computed
        python -m vtool.spatial_verification --test-test_homog_errors:1 --show
        python -m vtool.spatial_verification --test-test_homog_errors:1 --show --rotation_invariance
        python -m vtool.spatial_verification --test-test_homog_errors:1 --show --rotation_invariance --no-affine-invariance --xy-thresh=.001
        python -m vtool.spatial_verification --test-test_homog_errors:1 --show --rotation_invariance --xy-thresh=.001
        python -m vtool.spatial_verification --test-test_homog_errors:0 --show --rotation_invariance --xy-thresh=.001

    Example0:
        >>> # DISABLE_DOCTEST
        >>> from vtool.spatial_verification import *  # NOQA
        >>> import plottool as pt
        >>> kpts1, kpts2, fm, aff_inliers, rchip1, rchip2, xy_thresh_sqrd = testdata_matching_affine_inliers()
        >>> H = estimate_refined_transform(kpts1, kpts2, fm, aff_inliers)
        >>> scale_thresh, ori_thresh = 2.0, 1.57
        >>> full_homog_checks = not ut.get_argflag('--no-full-homog-checks')
        >>> homog_tup1 = test_homog_errors(H, kpts1, kpts2, fm, xy_thresh_sqrd, scale_thresh, ori_thresh, full_homog_checks)
        >>> homog_tup = (homog_tup1[0], homog_tup1[2])
        >>> ut.quit_if_noshow()
        >>> pt.draw_sv.show_sv(rchip1, rchip2, kpts1, kpts2, fm, homog_tup=homog_tup)
        >>> ut.show_if_requested()

    Example1:
        >>> # DISABLE_DOCTEST
        >>> from vtool.spatial_verification import *  # NOQA
        >>> import plottool as pt
        >>> kpts1, kpts2, fm_, aff_inliers, rchip1, rchip2, xy_thresh_sqrd = testdata_matching_affine_inliers()
        >>> H = estimate_refined_transform(kpts1, kpts2, fm_, aff_inliers)
        >>> scale_thresh, ori_thresh = 2.0, 1.57
        >>> full_homog_checks = not ut.get_argflag('--no-full-homog-checks')
        >>> # ----------------
        >>> # Take subset of feature matches
        >>> fm = fm_
        >>> scale_err, xy_err, ori_err = \
        ...     ut.exec_func_src(test_homog_errors, globals(), locals(),
        ...     'scale_err, xy_err, ori_err'.split(', '))
        >>> # we only care about checking out scale and orientation here. ignore bad xy points
        >>> xy_inliers_flag = np.less(xy_err, xy_thresh_sqrd)
        >>> scale_err[~xy_inliers_flag] = 0
        >>> # filter
        >>> fm = fm_[np.array(scale_err).argsort()[::-1][:10]]
        >>> fm = fm_[np.array(scale_err).argsort()[::-1][:10]]
        >>> # Exec sourcecode
        >>> kpts1_m, kpts2_m, off_xy1_m, off_xy1_mt, dxy1_m, dxy1_mt, xy2_m, xy1_m, xy1_mt, scale_err, xy_err, ori_err = \
        ...     ut.exec_func_src(test_homog_errors, globals(), locals(),
        ...     'kpts1_m, kpts2_m, off_xy1_m, off_xy1_mt, dxy1_m, dxy1_mt, xy2_m, xy1_m, xy1_mt, scale_err, xy_err, ori_err'.split(', '))
        >>> #---------------
        >>> ut.quit_if_noshow()
        >>> pt.figure(fnum=1, pnum=(1, 2, 1), title='orig points and offset point')
        >>> segments_list1 = np.array(list(zip(xy1_m.T.tolist(), off_xy1_m.T.tolist())))
        >>> pt.draw_line_segments(segments_list1, color=pt.LIGHT_BLUE)
        >>> pt.dark_background()
        >>> #---------------
        >>> pt.figure(fnum=1, pnum=(1, 2, 2), title='transformed points and matching points')
        >>> #---------------
        >>> # first have to make corresponding offset points
        >>> # Use reference point for scale and orientation tests
        >>> oris2_m   = ktool.get_oris(kpts2_m)
        >>> scales2_m = ktool.get_scales(kpts2_m)
        >>> dxy2_m    = np.vstack((np.sin(oris2_m), -np.cos(oris2_m)))
        >>> scaled_dxy2_m = dxy2_m * scales2_m[None, :]
        >>> off_xy2_m = xy2_m + scaled_dxy2_m
        >>> # Draw transformed semgents
        >>> segments_list2 = np.array(list(zip(xy2_m.T.tolist(), off_xy2_m.T.tolist())))
        >>> pt.draw_line_segments(segments_list2, color=pt.GREEN)
        >>> # Draw corresponding matches semgents
        >>> segments_list3 = np.array(list(zip(xy1_mt.T.tolist(), off_xy1_mt.T.tolist())))
        >>> pt.draw_line_segments(segments_list3, color=pt.RED)
        >>> # Draw matches between correspondences
        >>> segments_list4 = np.array(list(zip(xy1_mt.T.tolist(), xy2_m.T.tolist())))
        >>> pt.draw_line_segments(segments_list4, color=pt.ORANGE)
        >>> pt.dark_background()
        >>> #---------------
        >>> #vt.get _xy_axis_extents(kpts1_m)
        >>> #pt.draw_sv.show_sv(rchip1, rchip2, kpts1, kpts2, fm, homog_tup=homog_tup)
        >>> ut.show_if_requested()
    """
    kpts1_m = kpts1.take(fm.T[0], axis=0)
    kpts2_m = kpts2.take(fm.T[1], axis=0)
    # Transform all xy1 matches to xy2 space
    xy1_m   = ktool.get_xys(kpts1_m)
    #with ut.embed_on_exception_context:
    xy1_mt  = ltool.transform_points_with_homography(H, xy1_m)
    #xy1_mt  = ktool.transform_kpts_xys(H, kpts1_m)
    xy2_m   = ktool.get_xys(kpts2_m)
    # --- Find (Squared) Homography Distance Error ---
    # You cannot test for scale or orientation easily here because
    # you no longer have an ellipse? (maybe, probably have a conic) when using a
    # projective transformation
    xy_err = dtool.L2_sqrd(xy1_mt.T, xy2_m.T)
    # Estimate final inliers
    #ut.embed()
    if full_homog_checks:
        # TODO: may need to use more than one reference point
        # Use reference point for scale and orientation tests
        oris1_m   = ktool.get_oris(kpts1_m)
        scales1_m = ktool.get_scales(kpts1_m)
        # Get point offsets with unit length
        dxy1_m    = np.vstack((np.sin(oris1_m), -np.cos(oris1_m)))
        scaled_dxy1_m = dxy1_m * scales1_m[None, :]
        off_xy1_m = xy1_m + scaled_dxy1_m
        # transform reference point
        off_xy1_mt = ltool.transform_points_with_homography(H, off_xy1_m)
        scaled_dxy1_mt = xy1_mt - off_xy1_mt
        scales1_mt = npl.norm(scaled_dxy1_mt, axis=0)
        #with warnings.catch_warnings():
        #    warnings.simplefilter("ignore")
        dxy1_mt = scaled_dxy1_mt / scales1_mt
        # adjust for gravity vector being 0
        oris1_mt = np.arctan2(dxy1_mt[1], dxy1_mt[0]) - ktool.GRAVITY_THETA
        _det1_mt = scales1_mt ** 2
        det2_m = ktool.get_sqrd_scales(kpts2_m)
        ori2_m = ktool.get_oris(kpts2_m)
        #xy_err    = dtool.L2_sqrd(xy2_m.T, _xy1_mt.T)
        scale_err = dtool.det_distance(_det1_mt, det2_m)
        ori_err   = dtool.ori_distance(oris1_mt, ori2_m)
        ###
        xy_inliers_flag = np.less(xy_err, xy_thresh_sqrd)
        scale_inliers_flag = np.less(scale_err, scale_thresh)
        ori_inliers_flag   = np.less(ori_err, ori_thresh)
        hypo_inliers_flag = xy_inliers_flag  # Try to re-use memory
        np.logical_and(hypo_inliers_flag, ori_inliers_flag, out=hypo_inliers_flag)
        np.logical_and(hypo_inliers_flag, scale_inliers_flag, out=hypo_inliers_flag)
        # Seems slower due to memory
        #hypo_inliers_flag = np.logical_and.reduce(
        #    [xy_inliers_flag, ori_inliers_flag, scale_inliers_flag])
        # this is also slower
        #hypo_inliers_flag = np.logical_and.reduce((xy_inliers_flag,
        #ori_inliers_flag, scale_inliers_flag), out=xy_inliers_flag)
        refined_inliers = np.where(hypo_inliers_flag)[0].astype(INDEX_DTYPE)
        refined_errors = (xy_err, ori_err, scale_err)
    else:
        refined_inliers = np.where(xy_err < xy_thresh_sqrd)[0].astype(INDEX_DTYPE)
        refined_errors = (xy_err, None, None)
    homog_tup1 = (refined_inliers, refined_errors, H)
    return homog_tup1
def get_affine_inliers(kpts1, kpts2, fm, fs,
                        xy_thresh_sqrd,
                        scale_thresh_sqrd,
                        ori_thresh):
    """
    Estimates inliers deterministically using elliptical shapes

    Compute all transforms from kpts1 to kpts2 (enumerate all hypothesis)
    We transform from chip1 -> chip2
    The determinants are squared keypoint scales

    FROM PERDOCH 2009::
        H = inv(Aj).dot(Rj.T).dot(Ri).dot(Ai)
        H = inv(Aj).dot(Ai)
        The input invVs = perdoch.invA's

    CommandLine:
        python -m vtool.spatial_verification --test-get_affine_inliers

    Example:
        >>> # ENABLE_DOCTEST
        >>> from vtool.spatial_verification import *  # NOQA
        >>> import vtool.tests.dummy as dummy
        >>> import vtool.keypoint as ktool
        >>> kpts1, kpts2 = dummy.get_dummy_kpts_pair((100, 100))
        >>> fm = dummy.make_dummy_fm(len(kpts1)).astype(np.int32)
        >>> fs = np.ones(len(fm), dtype=np.float64)
        >>> xy_thresh_sqrd = ktool.KPTS_DTYPE(.009) ** 2
        >>> scale_thresh_sqrd = ktool.KPTS_DTYPE(2)
        >>> ori_thresh = ktool.KPTS_DTYPE(TAU / 4)
        >>> output = get_affine_inliers(kpts1, kpts2, fm, fs, xy_thresh_sqrd,
        >>>                             scale_thresh_sqrd, ori_thresh)
        >>> result = ut.hashstr(output)
        >>> print(result)
        89kz8nh6p+66t!+u

    Ignore::
        from vtool.spatial_verification import *  # NOQA
        import vtool.tests.dummy as dummy
        import vtool.keypoint as ktool
        kpts1, kpts2 = dummy.get_dummy_kpts_pair((100, 100))
        a = kpts1[fm.T[0]]
        b = kpts1.take(fm.T[0])

        align = fm.dtype.itemsize * fm.shape[1]
        align2 = [fm.dtype.itemsize, fm.dtype.itemsize]
        viewtype1 = np.dtype(np.void, align)
        viewtype2 = np.dtype(np.int32, align2)
        c = np.ascontiguousarray(fm).view(viewtype1)
        fm_view = np.ascontiguousarray(fm).view(viewtype1)
        qfx = fm.view(np.dtype(np.int32 np.int32.itemsize))
        dfx = fm.view(np.dtype(np.int32, np.int32.itemsize))
        d = np.ascontiguousarray(c).view(viewtype2)

        fm.view(np.dtype(np.void, align))
        np.ascontiguousarray(fm).view(np.dtype((np.void, Z.dtype.itemsize * Z.shape[1])))
    """
    #http://ipython-books.github.io/featured-01/
    kpts1_m = kpts1.take(fm.T[0], axis=0)
    kpts2_m = kpts2.take(fm.T[1], axis=0)

    # Get keypoints to project in matrix form
    #invVR2s_m = ktool.get_invV_mats(kpts2_m, with_trans=True, with_ori=True)
    #invVR1s_m = ktool.get_invV_mats(kpts1_m, with_trans=True, with_ori=True)
    invVR2s_m = ktool.get_invVR_mats3x3(kpts2_m)
    invVR1s_m = ktool.get_invVR_mats3x3(kpts1_m)
    RV1s_m    = ktool.invert_invV_mats(invVR1s_m)  # 539 us
    # BUILD ALL HYPOTHESIS TRANSFORMS: The transform from kp1 to kp2 is:
    Aff_mats = matrix_multiply(invVR2s_m, RV1s_m)
    # Get components to test projects against
    xy2_m  = ktool.get_xys(kpts2_m)
    det2_m = ktool.get_sqrd_scales(kpts2_m)
    ori2_m = ktool.get_oris(kpts2_m)
    # SLOWER EQUIVALENT
    # RV1s_m    = ktool.get_V_mats(kpts1_m, with_trans=True, with_ori=True)  # 5.2 ms
    # xy2_m  = ktool.get_invVR_mats_xys(invVR2s_m)
    # ori2_m = ktool.get_invVR_mats_oris(invVR2s_m)
    # assert np.all(ktool.get_oris(kpts2_m) == ktool.get_invVR_mats_oris(invVR2s_m))
    # assert np.all(ktool.get_xys(kpts2_m) == ktool.get_invVR_mats_xys(invVR2s_m))

    # The previous versions of this function were all roughly comparable.
    # The for loop one was the slowest. I'm deciding to go with the one
    # where there is no internal function definition. It was moderately faster,
    # and it gives us access to profile that function
    inliers_and_errors_list = [_test_hypothesis_inliers(Aff, invVR1s_m, xy2_m,
                                                        det2_m, ori2_m,
                                                        xy_thresh_sqrd,
                                                        scale_thresh_sqrd,
                                                        ori_thresh)
                               for Aff in Aff_mats]
    aff_inliers_list = [tup[0] for tup in inliers_and_errors_list]
    aff_errors_list  = [tup[1] for tup in inliers_and_errors_list]
    return aff_inliers_list, aff_errors_list, Aff_mats