Reputation: 153
I'm trying to display images in matplotlib transformed by a 3x3 matrix (using homogeneous coordinates). I have the following:
import numpy as np
import matplotlib as mp
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
class GenericTransform(mp.transforms.Transform):
input_dims = 2
output_dims = 2
is_separable = False
# expects a 3x3 matrix as a numpy array
def __init__(self, matrix):
super().__init__()
self.matrix = matrix
self.has_inverse = (np.linalg.det(matrix) != 0)
def transform(self, values):
# append 1 to each coordinate in `values`
hvals = np.insert(values, 2, 1, axis=1)
# do the transformations
thvals = np.matmul(self.matrix, hvals.transpose()).transpose()
# TODO: perspective divison?
# get rid of extra dimension and return
return thvals[:, 0:2]
def transform_non_affine(self, values):
return self.transform(values)
def inverted(self):
return GenericTransform(np.linalg.inv(self.matrix))
# add an image to the plot with a transformation
def addImage(filename, matrix):
transform = GenericTransform(matrix)
img = mpimg.imread(filename)
imgShow = plt.imshow(img, alpha=1)
imgShow.set_transform(transform + imgShow.get_transform())
I call addImage
with an image filename and a matrix, then call plt.show()
, and the transformation seems to be applied correctly. However, the image that gets drawn appears to be extended beyond what its bounds should be, as if to fill the axis-aligned box defined by the image's corners:
The line where there appears to be a "reflection" is where I want the image to simply be cut off. I'm not too familiar with matplotlib, so I'm confused as to why this happens. I was loosely following this. I also tried manually clip the image using a path:
# add an image to the plot with a transformation
def addImage(filename, matrix):
transform = GenericTransform(matrix)
img = mpimg.imread(filename)
x = img.shape[1]
y = img.shape[0]
path = transform.transform_path(mp.path.Path([[0,0], [x,0], [x,y], [0,y], [0,0]]))
patch = mp.patches.PathPatch(path, facecolor="none")
imgShow = plt.imshow(img, alpha=1, clip_on=True, clip_path=patch)
imgShow.set_transform(transform + imgShow.get_transform())
but this didn't quite work how I wanted. Specifically, whenever I resized the window or moved the plot around, the clipped areas remained stationary as the image in the background moved. How can I get it to clip my image correctly?
Also, I'm aware that the way I do the transformations might be wrong for perspective transformations, but I'd like to figure that out on my own.
Upvotes: 1
Views: 608
Reputation: 153
After a few hours, I finally figured it out. I didn't quite understand how transformations were being applied to the clip path. Ultimately, this worked:
def addImage(filename, matrix):
transform = GenericTransform(matrix)
img = mpimg.imread(filename)
imgShow = plt.imshow(img, alpha=1, clip_on=True, interpolation="bilinear")
oldTransform = imgShow.get_transform()
imgShow.set_transform(transform + oldTransform)
w = img.shape[1]
h = img.shape[0]
path = transform.transform_path(mp.path.Path([[0,0], [w,0], [w,h], [0,h], [0,0]]))
patch = mp.patches.PathPatch(path, facecolor="none")
patch.set_transform(oldTransform)
imgShow.set_clip_path(patch)
Upvotes: 2