📚 The CoCalc Library - books, templates and other resources
License: OTHER
r"""1=====2Swirl3=====45Image swirling is a non-linear image deformation that creates a whirlpool6effect.78Image warping9`````````````10When applying a geometric transformation on an image, we typically make use of11a reverse mapping, i.e., for each pixel in the output image, we compute its12corresponding position in the input. The reason is that, if we were to do it13the other way around (map each input pixel to its new output position), some14pixels in the output may be left empty. On the other hand, each output15coordinate has exactly one corresponding location in (or outside) the input16image, and even if that position is non-integer, we may use interpolation to17compute the corresponding image value.1819Performing a reverse mapping20````````````````````````````21To perform a geometric warp in ``skimage``, you simply need to provide the22reverse mapping to the ``skimage.transform.warp`` function. E.g., consider the23case where we would like to shift an image 50 pixels to the left. The reverse24mapping for such a shift would be::2526def shift_left(xy):27xy[:, 0] += 5028return xy2930The corresponding call to warp is::3132from skimage.transform import warp33warp(image, shift_left)3435The swirl transformation36````````````````````````37Consider the coordinate :math:`(x, y)` in the output image. The reverse38mapping for the swirl transformation first computes, relative to a center39:math:`(x_0, y_0)`, its polar coordinates,4041.. math::4243\theta = \arctan(y/x)4445\rho = \sqrt{(x - x_0)^2 + (y - y_0)^2},4647and then transforms them according to4849.. math::5051r = \ln(2) \, \mathtt{radius} / 55253\phi = \mathtt{rotation}5455s = \mathtt{strength}5657\theta' = \phi + s \, e^{-\rho / r + \theta}5859where ``strength`` is a parameter for the amount of swirl, ``radius`` indicates60the swirl extent in pixels, and ``rotation`` adds a rotation angle. The61transformation of ``radius`` into :math:`r` is to ensure that the62transformation decays to :math:`\approx 1/1000^{\mathsf{th}}` within the63specified radius.64"""6566from __future__ import division6768from matplotlib.widgets import Slider69import matplotlib.pyplot as plt7071import numpy as np72from scipy import ndimage73from skimage import io, transform747576def _swirl_mapping(xy, center, rotation, strength, radius):77"""Compute the coordinate mapping for a swirl transformation.7879"""80x, y = xy.T81x0, y0 = center82rho = np.sqrt((x - x0)**2 + (y - y0)**2)8384# Ensure that the transformation decays to approximately 1/1000-th85# within the specified radius.86radius = radius / 5 * np.log(2)8788theta = rotation + strength * \89np.exp(-rho / radius) + \90np.arctan2(y - y0, x - x0)9192xy[..., 0] = x0 + rho * np.cos(theta)93xy[..., 1] = y0 + rho * np.sin(theta)9495return xy9697def swirl(image, center=None, strength=1, radius=100, rotation=0):98"""Perform a swirl transformation.99100Parameters101----------102image : ndarray103Input image.104center : (x,y) tuple or (2,) ndarray105Center coordinate of transformation.106strength : float107The amount of swirling applied.108radius : float109The extent of the swirl in pixels. The effect dies out110rapidly beyond `radius`.111rotation : float112Additional rotation applied to the image.113114Returns115-------116swirled : ndarray117Swirled version of the input.118119"""120121if center is None:122center = np.array(image.shape)[:2] / 2123124warp_args = {'center': center,125'rotation': rotation,126'strength': strength,127'radius': radius}128129return transform.warp(image, _swirl_mapping, map_args=warp_args)130131132# Read the input image, and compute its center133mona = io.imread('../../images/mona_lisa.jpg')134h, w, d = mona.shape135center = np.array([w/2, h/2])136137# Construct three outputs: input image, swirled and deswirled138f, (ax0, ax1, ax2) = plt.subplots(1, 3)139plt.subplots_adjust(bottom=0.5)140141# Swirl the input image with fixed parameters142mona_swirled = swirl(mona, center=center, rotation=0, strength=10, radius=100)143144source = ax0.imshow(mona, interpolation='nearest')145ax0.set_title('Click to move\nthe red dot\n(the transform center)')146ax0.set_xlabel('Original Mona Lisa')147148swirled = ax1.imshow(mona_swirled, interpolation='nearest')149ax1.set_xlabel('Swirled Mona Lisa')150151deswirled = ax2.imshow(mona_swirled, interpolation='nearest')152ax2.set_xlabel('Restored using\nyour choice of\nparameters')153154# Plot a dot to indicate the center-point of the reverse transform155center += [10, -5]156center_dot, = ax0.plot(center[0], center[1], 'ro')157ax0.axis('image')158159def update(event=None):160"""This function will be executed each time the interactive sliders are161changed or when clicking the input image to adjust the center-point. It162reads the new parameters, and performs the deswirl accordingly.163164Note that the swirl is always performed using a fixed center, strength and165radius, so that you can investigate the sensitivity of the inverse166transform with regards to the parameters.167168"""169# Mouse click detected on input image -- set center position170if hasattr(event, 'inaxes') and event.inaxes is ax0:171center[:] = [event.xdata, event.ydata]172173# Perform deswirl and update the output image174out_deswirl = swirl(mona_swirled,175center=center, rotation=-np.deg2rad(rotation.val),176strength=-strength.val, radius=radius.val)177178deswirled.set_data(out_deswirl)179180# Re-position the center dot according to the clicked position181center_dot.set_xdata(center[0])182center_dot.set_ydata(center[1])183184plt.draw()185186# Set up the parameter sliders187ax_rotation = plt.axes([0.25, 0.15, 0.65, 0.03])188rotation = Slider(ax_rotation, 'Rotation', 0, 360, valinit=0)189ax_strength = plt.axes([0.25, 0.25, 0.65, 0.03])190strength = Slider(ax_strength, 'Strength', -50, 50, valinit=10+10)191ax_radius = plt.axes([0.25, 0.35, 0.65, 0.03])192radius = Slider(ax_radius, 'Radius', 0, 250, valinit=100-20)193194# Trigger an update whenever the parameters change195rotation.on_changed(update)196strength.on_changed(update)197radius.on_changed(update)198199# Also trigger an update whenever the mouse is clicked on the input image200# (setting the center point)201f.canvas.mpl_connect('button_press_event', update)202203# Do a single update when we start the program204update(None)205206plt.show()207208209