0
votes

I have an application where I am using matplotlib to display elliptical regions on an image. To do this I'm using mpl.patches.Circle along with mp.patches.Affine2D to warp unit circles into an elliptical shape.

import numpy as np
import matplotlib as mpl
import pyplot as plt

invVR_mats = np.array([
   [[   7.80247545,    0.        ,   92.9254837 ],
    [  -3.46026921,   10.85727882,   17.53866959],
    [   0.        ,    0.        ,    1.        ]],

   [[  11.42656994,    0.        ,   76.86006927],
    [  -3.26515651,    9.61946297,   24.79440498],
    [   0.        ,    0.        ,    1.        ]],

   [[  10.40444851,    0.        ,  140.62428284],
    [ -10.94557095,   10.59212685,   24.91024971],
    [   0.        ,    0.        ,    1.       ]],])

invVR_aff2Ds = [mpl.transforms.Affine2D(invVR)
                for invVR in invVR_mats]
ell_actors = [mpl.patches.Circle((0, 0), 1, transform=invVR)
              for invVR in invVR_aff2Ds]
coll = mpl.collections.PatchCollection(ell_actors) 

plt.figure()
ax = plt.gca()
ax.set_ylim(0, 100)
ax.set_xlim(0, 300)

ax.add_collection(coll)

There is a point in my application that the ellipses in one image are put in correspondence with ellipses from a second image using a homography matrix. So far I have been using it to warp points from image1 into image2.

I would like to get a visual idea of how these ellipses warp into image2. I can transform my affine matrices with this homography matrix, but the resulting matrix is no longer affine. (I believe it represents a general conic, either a circle, ellipse, hyperbola, or parabola)

from numpy.core.umath_tests import matrix_multiply
H = np.array([[ -0.70098,   0.12273,   5.18734],
              [  0.12444,  -0.63474,  14.13995],
              [  0.00004,   0.00025,  -0.64873]]) 

HinvVR_mats = matrix_multiply(H, invVR_mats)
print(HinvVR_mats)
#---------
np.array([
   [[ -5.89405808e+00,   1.33251383e+00,  -5.77990446e+01],
    [  3.16731132e+00,  -6.89154916e+00,   1.45711021e+01],
    [ -5.52968284e-04,   2.71431970e-03,  -6.40628313e-01]],

   [[ -8.41052966e+00,   1.18059669e+00,  -4.56470140e+01],
    [  3.49444781e+00,  -6.10585793e+00,   7.96641640e+00],
    [ -3.59226330e-04,   2.40486574e-03,  -6.39456996e-01]],

   [[ -8.63666024e+00,   1.29997173e+00,  -9.03302348e+01],
    [  8.24232128e+00,  -6.72324660e+00,   1.58277039e+01],
    [ -2.32021480e-03,   2.64803171e-03,  -6.36877466e-01]]])

If I de-homogenize only the last column I can find the center of where the ellipse was projected, but I would like to see some shape information as well.

So far the best I've done is just de-homogenizing the last column and ignoring the values in [:, 2, 0] and [:, 2, 1]

HinvVR_mats = np.divide(HinvVR_mats , HinvVR_mats[:, None, None, 2, 2])
print(HinvVR_mats)

array([[[  9.20043332e+00,  -2.08001083e+00,   9.02224323e+01],
    [ -4.94407015e+00,   1.07574845e+01,  -2.27450173e+01],
    [  8.63165541e-04,  -4.23696494e-03,   1.00000000e+00]],

   [[  1.31526118e+01,  -1.84624877e+00,   7.13840248e+01],
    [ -5.46471120e+00,   9.54850438e+00,  -1.24580956e+01],
    [  5.61767769e-04,  -3.76079354e-03,   1.00000000e+00]],

   [[  1.35609449e+01,  -2.04116458e+00,   1.41832989e+02],
    [ -1.29417694e+01,   1.05565779e+01,  -2.48520394e+01],
    [  3.64311021e-03,  -4.15783546e-03,   1.00000000e+00]]])

Is there a way I can tranform mpl.patches.Circle (or any other patch for that matter) using a non-affine matrix. The documentation seems to suggest it is possible, but I'm not seeing any way to go about it.

I have

1
I don't understand enough of the transform stack as I should, but mpl does support non-affine transformations matplotlib.org/users/transforms_tutorial.html is not a bad place to start.tacaswell

1 Answers

0
votes

I was able to solve this by looking at the tutorial posted by tcaswell

I had to create my own tranformation class though which looked like this

class HomographyTransform(mpl.transforms.Transform):
    """
    References: 
        http://stackoverflow.com/questions/28401788/using-homogeneous-transforms-non-affine-with-matplotlib-patches?noredirect=1#comment45156353_28401788
        http://matplotlib.org/users/transforms_tutorial.html
    """
    input_dims = 2
    output_dims = 2
    is_separable = False

    def __init__(self, H, axis=None, use_rmin=True):
        mpl.transforms.Transform.__init__(self)
        self._axis = axis
        self._use_rmin = use_rmin
        self.H = H

    def transform_non_affine(self, input_xy):
        """
        The input and output are Nx2 numpy arrays.
        """
        import vtool as vt
        _xys = input_xy.T
        xyz  = vt.add_homogenous_coordinate(_xys)
        xyz_t = vt.matrix_multiply(self.H, xyz)
        xy_t  = vt.remove_homogenous_coordinate(xyz_t)
        output_xy = xy_t.T
        return output_xy
    #transform_non_affine.__doc__ = mpl.transforms.Transform.transform_non_affine.__doc__

    def transform_path_non_affine(self, path):
        vertices = path.vertices
        if len(vertices) == 2 and vertices[0, 0] == vertices[1, 0]:
            return mpl.path.Path(self.transform(vertices), path.codes)
        ipath = path.interpolated(path._interpolation_steps)
        return mpl.path.Path(self.transform(ipath.vertices), ipath.codes)
    #transform_path_non_affine.__doc__ = mpl.transforms.Transform.transform_path_non_affine.__doc__

The functions called by my own library vtool are:

def add_homogenous_coordinate(_xys):
    assert _xys.shape[0] == 2
    _zs = np.ones((1, _xys.shape[1]), dtype=_xys.dtype)
    _xyzs = np.vstack((_xys, _zs))
    return _xyzs


def remove_homogenous_coordinate(_xyzs):
    assert _xyzs.shape[0] == 3
    _xys = np.divide(_xyzs[0:2], _xyzs[None, 2])
    return _xys

and matrix_multiply is the same matrix_multiply used earlier.

and my function to create the transform matrices currently looks like this:

def get_invVR_aff2Ds(kpts, H=None):
    """ Returns matplotlib keypoint transformations (circle -> ellipse) """
    #invVR_mats = ktool.get_invV_mats(kpts, with_trans=True, with_ori=True)
    invVR_mats = ktool.get_invVR_mats3x3(kpts)
    if H is None:
        invVR_aff2Ds = [mpl.transforms.Affine2D(invVR)
                        for invVR in invVR_mats]
    else: 
        # not actually affine
        invVR_aff2Ds = [HomographyTransform(H.dot(invVR))
                        for invVR in invVR_mats]
    return invVR_aff2Ds