Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
| Download
Views: 6249
License: OTHER
1
r"""
2
=====
3
Swirl
4
=====
5
6
Image swirling is a non-linear image deformation that creates a whirlpool
7
effect.
8
9
Image warping
10
`````````````
11
When applying a geometric transformation on an image, we typically make use of
12
a reverse mapping, i.e., for each pixel in the output image, we compute its
13
corresponding position in the input. The reason is that, if we were to do it
14
the other way around (map each input pixel to its new output position), some
15
pixels in the output may be left empty. On the other hand, each output
16
coordinate has exactly one corresponding location in (or outside) the input
17
image, and even if that position is non-integer, we may use interpolation to
18
compute the corresponding image value.
19
20
Performing a reverse mapping
21
````````````````````````````
22
To perform a geometric warp in ``skimage``, you simply need to provide the
23
reverse mapping to the ``skimage.transform.warp`` function. E.g., consider the
24
case where we would like to shift an image 50 pixels to the left. The reverse
25
mapping for such a shift would be::
26
27
def shift_left(xy):
28
xy[:, 0] += 50
29
return xy
30
31
The corresponding call to warp is::
32
33
from skimage.transform import warp
34
warp(image, shift_left)
35
36
The swirl transformation
37
````````````````````````
38
Consider the coordinate :math:`(x, y)` in the output image. The reverse
39
mapping for the swirl transformation first computes, relative to a center
40
:math:`(x_0, y_0)`, its polar coordinates,
41
42
.. math::
43
44
\theta = \arctan(y/x)
45
46
\rho = \sqrt{(x - x_0)^2 + (y - y_0)^2},
47
48
and then transforms them according to
49
50
.. math::
51
52
r = \ln(2) \, \mathtt{radius} / 5
53
54
\phi = \mathtt{rotation}
55
56
s = \mathtt{strength}
57
58
\theta' = \phi + s \, e^{-\rho / r + \theta}
59
60
where ``strength`` is a parameter for the amount of swirl, ``radius`` indicates
61
the swirl extent in pixels, and ``rotation`` adds a rotation angle. The
62
transformation of ``radius`` into :math:`r` is to ensure that the
63
transformation decays to :math:`\approx 1/1000^{\mathsf{th}}` within the
64
specified radius.
65
"""
66
67
from __future__ import division
68
69
from matplotlib.widgets import Slider
70
import matplotlib.pyplot as plt
71
72
import numpy as np
73
from scipy import ndimage
74
from skimage import io, transform
75
76
77
def _swirl_mapping(xy, center, rotation, strength, radius):
78
"""Compute the coordinate mapping for a swirl transformation.
79
80
"""
81
x, y = xy.T
82
x0, y0 = center
83
rho = np.sqrt((x - x0)**2 + (y - y0)**2)
84
85
# Ensure that the transformation decays to approximately 1/1000-th
86
# within the specified radius.
87
radius = radius / 5 * np.log(2)
88
89
theta = rotation + strength * \
90
np.exp(-rho / radius) + \
91
np.arctan2(y - y0, x - x0)
92
93
xy[..., 0] = x0 + rho * np.cos(theta)
94
xy[..., 1] = y0 + rho * np.sin(theta)
95
96
return xy
97
98
def swirl(image, center=None, strength=1, radius=100, rotation=0):
99
"""Perform a swirl transformation.
100
101
Parameters
102
----------
103
image : ndarray
104
Input image.
105
center : (x,y) tuple or (2,) ndarray
106
Center coordinate of transformation.
107
strength : float
108
The amount of swirling applied.
109
radius : float
110
The extent of the swirl in pixels. The effect dies out
111
rapidly beyond `radius`.
112
rotation : float
113
Additional rotation applied to the image.
114
115
Returns
116
-------
117
swirled : ndarray
118
Swirled version of the input.
119
120
"""
121
122
if center is None:
123
center = np.array(image.shape)[:2] / 2
124
125
warp_args = {'center': center,
126
'rotation': rotation,
127
'strength': strength,
128
'radius': radius}
129
130
return transform.warp(image, _swirl_mapping, map_args=warp_args)
131
132
133
# Read the input image, and compute its center
134
mona = io.imread('../../images/mona_lisa.jpg')
135
h, w, d = mona.shape
136
center = np.array([w/2, h/2])
137
138
# Construct three outputs: input image, swirled and deswirled
139
f, (ax0, ax1, ax2) = plt.subplots(1, 3)
140
plt.subplots_adjust(bottom=0.5)
141
142
# Swirl the input image with fixed parameters
143
mona_swirled = swirl(mona, center=center, rotation=0, strength=10, radius=100)
144
145
source = ax0.imshow(mona, interpolation='nearest')
146
ax0.set_title('Click to move\nthe red dot\n(the transform center)')
147
ax0.set_xlabel('Original Mona Lisa')
148
149
swirled = ax1.imshow(mona_swirled, interpolation='nearest')
150
ax1.set_xlabel('Swirled Mona Lisa')
151
152
deswirled = ax2.imshow(mona_swirled, interpolation='nearest')
153
ax2.set_xlabel('Restored using\nyour choice of\nparameters')
154
155
# Plot a dot to indicate the center-point of the reverse transform
156
center += [10, -5]
157
center_dot, = ax0.plot(center[0], center[1], 'ro')
158
ax0.axis('image')
159
160
def update(event=None):
161
"""This function will be executed each time the interactive sliders are
162
changed or when clicking the input image to adjust the center-point. It
163
reads the new parameters, and performs the deswirl accordingly.
164
165
Note that the swirl is always performed using a fixed center, strength and
166
radius, so that you can investigate the sensitivity of the inverse
167
transform with regards to the parameters.
168
169
"""
170
# Mouse click detected on input image -- set center position
171
if hasattr(event, 'inaxes') and event.inaxes is ax0:
172
center[:] = [event.xdata, event.ydata]
173
174
# Perform deswirl and update the output image
175
out_deswirl = swirl(mona_swirled,
176
center=center, rotation=-np.deg2rad(rotation.val),
177
strength=-strength.val, radius=radius.val)
178
179
deswirled.set_data(out_deswirl)
180
181
# Re-position the center dot according to the clicked position
182
center_dot.set_xdata(center[0])
183
center_dot.set_ydata(center[1])
184
185
plt.draw()
186
187
# Set up the parameter sliders
188
ax_rotation = plt.axes([0.25, 0.15, 0.65, 0.03])
189
rotation = Slider(ax_rotation, 'Rotation', 0, 360, valinit=0)
190
ax_strength = plt.axes([0.25, 0.25, 0.65, 0.03])
191
strength = Slider(ax_strength, 'Strength', -50, 50, valinit=10+10)
192
ax_radius = plt.axes([0.25, 0.35, 0.65, 0.03])
193
radius = Slider(ax_radius, 'Radius', 0, 250, valinit=100-20)
194
195
# Trigger an update whenever the parameters change
196
rotation.on_changed(update)
197
strength.on_changed(update)
198
radius.on_changed(update)
199
200
# Also trigger an update whenever the mouse is clicked on the input image
201
# (setting the center point)
202
f.canvas.mpl_connect('button_press_event', update)
203
204
# Do a single update when we start the program
205
update(None)
206
207
plt.show()
208
209