Apply transformation to image and canvas

Background

I have an image in figure like this:

from skimage import data
import matplotlib.pyplot as plt

fig, ax = plt.subplots(sharex='all', sharey='all')
ax.imshow(data.coins())
ax.set_ylim(1500, 0)
ax.set_xlim(-1500, 1500)
plt.show()

Challenges

I would like to apply a transformation to an image (rotation + translation). When I tried, the transformation was applied to the image but its background (canvas?) remained in place:

from skimage import data
import matplotlib.pyplot as plt
import numpy as np

matrix = np.array([[np.cos(np.deg2rad(5)), -np.sin(np.deg2rad(5)), 0],
                   [np.sin(np.deg2rad(5)), np.cos(np.deg2rad(5)), -200],
                   [0, 0, 1]])
tform_euclidean = transform.EuclideanTransform(matrix)
img_warp_euclidean = transform.warp(data.coins(), tform_euclidean)

fig, ax = plt.subplots(sharex='all', sharey='all')
ax.imshow(img_warp_euclidean)
ax.set_ylim(1500, 0)
ax.set_xlim(-1500, 1500)
plt.show()

Is there a way to apply the transformation to the image plus its canvas?

Hi @Olivier,

Is this (or something similar) what you’re after?

If so, then you can do it like this:

matrix = np.array([[np.cos(np.deg2rad(5)), -np.sin(np.deg2rad(5)), 0],
                   [np.sin(np.deg2rad(5)), np.cos(np.deg2rad(5)), -200],
                   [0, 0, 1]])
tform_euclidean = transform.EuclideanTransform(matrix)


coins_img = data.coins()

# Compute the max of the bounding box that maps into the image.
# Note that we need 
pts = np.array([[0,0]])
for x,y in itertools.product( [0,coins_img.shape[0]],[0,coins_img.shape[1]] ):
    tformed_pt = tform_euclidean.inverse([y,x]) # need to flip xy
    pts = np.concatenate( (pts,tformed_pt), axis=0 )

# ceil and flip x-y back
max_pt = np.flip(np.ceil( np.amax( pts, axis=0 )))

# tell transform.warp what the new (bigger) bounding box is
img_warp_euclidean = transform.warp( data.coins(), tform_euclidean, output_shape=max_pt )

fig, ax = plt.subplots(sharex='all', sharey='all')
ax.imshow(img_warp_euclidean)
ax.set_ylim(1500, 0)
ax.set_xlim(-1500, 1500)
plt.show()

Hopefully the comments are helpful, but post back if something is unclear
John

2 Likes

Thanks @bogovicj!
This is already helpful. Just two more questions:
why do the edges seem more “pixelised” in your example?
My original intention was to not see any bounding box. Is it possible to not see the dark background left after the transformation?

Hi @Olivier,

probably because i took a pretty low-res screenshot

My original intention was to not see any bounding box. Is it possible to not see the dark background left after the transformation?

As I understand it, that would be matplotlib’s job more than scikit-image’s job, I imagine something like this should work:

  1. Generate a matplotlib patch matching your image
  2. Transform the image as above (resulting in a too-big bounding box)
  3. Transform the patch to match it.
  4. Clip the image with the transformed patch (which i think should fix the bounding box)

John

Great, I will look into this. Many thanks!

Good luck! :four_leaf_clover:
and please post back if you manage to get it working!

You can use cval=np.nan in the call to warp to fill the parts outside the image with nan, which matplotlib renders as transparent.

1 Like

Thank you @jni, this completes nicely the solution proposed by @bogovicj :slight_smile:

matrix = np.array([[np.cos(np.deg2rad(5)), -np.sin(np.deg2rad(5)), 0],
                   [np.sin(np.deg2rad(5)), np.cos(np.deg2rad(5)), -200],
                   [0, 0, 1]])
tform_euclidean = transform.EuclideanTransform(matrix)
coins_img = data.coins()

# Compute the max of the bounding box that maps into the image.
# Note that we need
pts = np.array([[0, 0]])
for x, y in itertools.product([0, coins_img.shape[0]], [0, coins_img.shape[1]]):
    tformed_pt = tform_euclidean.inverse([y, x])  # need to flip xy
    pts = np.concatenate((pts, tformed_pt), axis=0)

# ceil and flip x-y back
max_pt = np.flip(np.ceil(np.amax(pts, axis=0)))

# tell transform.warp what the new (bigger) bounding box is
img_warp_euclidean = transform.warp(data.coins(), tform_euclidean, output_shape=max_pt, cval=np.nan)

fig, ax = plt.subplots(sharex='all', sharey='all')
ax.imshow(img_warp_euclidean)
ax.set_ylim(1500, 0)
ax.set_xlim(-1500, 1500)
plt.show()

3 Likes