We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Gallery example 'Using Polar and Log-Polar Transformations for Registration' needs fixing
## Description
The gallery example for Using Polar and Log-Polar Transformations for Registration seems to be broken as of 0.19.x. Was working in 0.18.x.

## Way to reproduce
Unsure if this is specific to the production doc build or an issue in the code. I am raising this issue to get it on the board before starting my testing.


## Version information
See description. I'll report back in this thread when I know more.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `doc/examples/registration/plot_register_rotation.py`
Content:
```
1 r"""
2 ==========================================================
3 Using Polar and Log-Polar Transformations for Registration
4 ==========================================================
5 
6 Phase correlation (``registration.phase_cross_correlation``) is an efficient
7 method for determining translation offset between pairs of similar images.
8 However this approach relies on a near absence of rotation/scaling differences
9 between the images, which are typical in real-world examples.
10 
11 To recover rotation and scaling differences between two images, we can take
12 advantage of two geometric properties of the log-polar transform and the
13 translation invariance of the frequency domain. First, rotation in Cartesian
14 space becomes translation along the angular coordinate (:math:`\theta`) axis
15 of log-polar space. Second, scaling in Cartesian space becomes translation
16 along the radial coordinate (:math:`\rho = \ln\sqrt{x^2 + y^2}`) of log-polar
17 space. Finally, differences in translation in the spatial domain do not impact
18 magnitude spectrum in the frequency domain.
19 
20 In this series of examples, we build on these concepts to show how the
21 log-polar transform ``transform.warp_polar`` can be used in conjunction with
22 phase correlation to recover rotation and scaling differences between two
23 images that also have a translation offset.
24 """
25 
26 ######################################################################
27 # Recover rotation difference with a polar transform
28 # ==================================================
29 #
30 # In this first example, we consider the simple case of two images that only
31 # differ with respect to rotation around a common center point. By remapping
32 # these images into polar space, the rotation difference becomes a simple
33 # translation difference that can be recovered by phase correlation.
34 
35 import numpy as np
36 import matplotlib.pyplot as plt
37 
38 from skimage import data
39 from skimage.registration import phase_cross_correlation
40 from skimage.transform import warp_polar, rotate, rescale
41 from skimage.util import img_as_float
42 
43 radius = 705
44 angle = 35
45 image = data.retina()
46 image = img_as_float(image)
47 rotated = rotate(image, angle)
48 image_polar = warp_polar(image, radius=radius, channel_axis=-1)
49 rotated_polar = warp_polar(rotated, radius=radius, channel_axis=-1)
50 
51 fig, axes = plt.subplots(2, 2, figsize=(8, 8))
52 ax = axes.ravel()
53 ax[0].set_title("Original")
54 ax[0].imshow(image)
55 ax[1].set_title("Rotated")
56 ax[1].imshow(rotated)
57 ax[2].set_title("Polar-Transformed Original")
58 ax[2].imshow(image_polar)
59 ax[3].set_title("Polar-Transformed Rotated")
60 ax[3].imshow(rotated_polar)
61 plt.show()
62 
63 shifts, error, phasediff = phase_cross_correlation(image_polar, rotated_polar)
64 print(f'Expected value for counterclockwise rotation in degrees: '
65       f'{angle}')
66 print(f'Recovered value for counterclockwise rotation: '
67       f'{shifts[0]}')
68 
69 ######################################################################
70 # Recover rotation and scaling differences with log-polar transform
71 # =================================================================
72 #
73 # In this second example, the images differ by both rotation and scaling (note
74 # the axis tick values). By remapping these images into log-polar space,
75 # we can recover rotation as before, and now also scaling, by phase
76 # correlation.
77 
78 # radius must be large enough to capture useful info in larger image
79 radius = 1500
80 angle = 53.7
81 scale = 2.2
82 image = data.retina()
83 image = img_as_float(image)
84 rotated = rotate(image, angle)
85 rescaled = rescale(rotated, scale, channel_axis=-1)
86 image_polar = warp_polar(image, radius=radius,
87                          scaling='log', channel_axis=-1)
88 rescaled_polar = warp_polar(rescaled, radius=radius,
89                             scaling='log', channel_axis=-1)
90 
91 fig, axes = plt.subplots(2, 2, figsize=(8, 8))
92 ax = axes.ravel()
93 ax[0].set_title("Original")
94 ax[0].imshow(image)
95 ax[1].set_title("Rotated and Rescaled")
96 ax[1].imshow(rescaled)
97 ax[2].set_title("Log-Polar-Transformed Original")
98 ax[2].imshow(image_polar)
99 ax[3].set_title("Log-Polar-Transformed Rotated and Rescaled")
100 ax[3].imshow(rescaled_polar)
101 plt.show()
102 
103 # setting `upsample_factor` can increase precision
104 shifts, error, phasediff = phase_cross_correlation(image_polar, rescaled_polar,
105                                                    upsample_factor=20)
106 shiftr, shiftc = shifts[:2]
107 
108 # Calculate scale factor from translation
109 klog = radius / np.log(radius)
110 shift_scale = 1 / (np.exp(shiftc / klog))
111 
112 print(f'Expected value for cc rotation in degrees: {angle}')
113 print(f'Recovered value for cc rotation: {shiftr}')
114 print()
115 print(f'Expected value for scaling difference: {scale}')
116 print(f'Recovered value for scaling difference: {shift_scale}')
117 
118 ######################################################################
119 # Register rotation and scaling on a translated image - Part 1
120 # =================================================================
121 #
122 # The above examples only work when the images to be registered share a
123 # center. However, it is more often the case that there is also a translation
124 # component to the difference between two images to be registered. One
125 # approach to register rotation, scaling and translation is to first correct
126 # for rotation and scaling, then solve for translation. It is possible to
127 # resolve rotation and scaling differences for translated images by working on
128 # the magnitude spectra of the Fourier-transformed images.
129 #
130 # In this next example, we first show how the above approaches fail when two
131 # images differ by rotation, scaling, and translation.
132 
133 from skimage.color import rgb2gray
134 from skimage.filters import window, difference_of_gaussians
135 from scipy.fft import fft2, fftshift
136 
137 angle = 24
138 scale = 1.4
139 shiftr = 30
140 shiftc = 15
141 
142 image = rgb2gray(data.retina())
143 translated = image[shiftr:, shiftc:]
144 rotated = rotate(translated, angle)
145 rescaled = rescale(rotated, scale)
146 sizer, sizec = image.shape
147 rts_image = rescaled[:sizer, :sizec]
148 
149 # When center is not shared, log-polar transform is not helpful!
150 radius = 705
151 warped_image = warp_polar(image, radius=radius, scaling="log")
152 warped_rts = warp_polar(rts_image, radius=radius, scaling="log")
153 shifts, error, phasediff = phase_cross_correlation(warped_image, warped_rts,
154                                                    upsample_factor=20)
155 shiftr, shiftc = shifts[:2]
156 klog = radius / np.log(radius)
157 shift_scale = 1 / (np.exp(shiftc / klog))
158 
159 fig, axes = plt.subplots(2, 2, figsize=(8, 8))
160 ax = axes.ravel()
161 ax[0].set_title("Original Image")
162 ax[0].imshow(image, cmap='gray')
163 ax[1].set_title("Modified Image")
164 ax[1].imshow(rts_image, cmap='gray')
165 ax[2].set_title("Log-Polar-Transformed Original")
166 ax[2].imshow(warped_image)
167 ax[3].set_title("Log-Polar-Transformed Modified")
168 ax[3].imshow(warped_rts)
169 fig.suptitle('log-polar-based registration fails when no shared center')
170 plt.show()
171 
172 print(f'Expected value for cc rotation in degrees: {angle}')
173 print(f'Recovered value for cc rotation: {shiftr}')
174 print()
175 print(f'Expected value for scaling difference: {scale}')
176 print(f'Recovered value for scaling difference: {shift_scale}')
177 
178 ######################################################################
179 # Register rotation and scaling on a translated image - Part 2
180 # =================================================================
181 #
182 # We next show how rotation and scaling differences, but not translation
183 # differences, are apparent in the frequency magnitude spectra of the images.
184 # These differences can be recovered by treating the magnitude spectra as
185 # images themselves, and applying the same log-polar + phase correlation
186 # approach taken above.
187 
188 # First, band-pass filter both images
189 image = difference_of_gaussians(image, 5, 20)
190 rts_image = difference_of_gaussians(rts_image, 5, 20)
191 
192 # window images
193 wimage = image * window('hann', image.shape)
194 rts_wimage = rts_image * window('hann', image.shape)
195 
196 # work with shifted FFT magnitudes
197 image_fs = np.abs(fftshift(fft2(wimage)))
198 rts_fs = np.abs(fftshift(fft2(rts_wimage)))
199 
200 # Create log-polar transformed FFT mag images and register
201 shape = image_fs.shape
202 radius = shape[0] // 8  # only take lower frequencies
203 warped_image_fs = warp_polar(image_fs, radius=radius, output_shape=shape,
204                              scaling='log', order=0)
205 warped_rts_fs = warp_polar(rts_fs, radius=radius, output_shape=shape,
206                            scaling='log', order=0)
207 
208 warped_image_fs = warped_image_fs[:shape[0] // 2, :]  # only use half of FFT
209 warped_rts_fs = warped_rts_fs[:shape[0] // 2, :]
210 shifts, error, phasediff = phase_cross_correlation(warped_image_fs,
211                                                    warped_rts_fs,
212                                                    upsample_factor=10)
213 
214 # Use translation parameters to calculate rotation and scaling parameters
215 shiftr, shiftc = shifts[:2]
216 recovered_angle = (360 / shape[0]) * shiftr
217 klog = shape[1] / np.log(radius)
218 shift_scale = np.exp(shiftc / klog)
219 
220 fig, axes = plt.subplots(2, 2, figsize=(8, 8))
221 ax = axes.ravel()
222 ax[0].set_title("Original Image FFT\n(magnitude; zoomed)")
223 center = np.array(shape) // 2
224 ax[0].imshow(image_fs[center[0] - radius:center[0] + radius,
225                       center[1] - radius:center[1] + radius],
226              cmap='magma')
227 ax[1].set_title("Modified Image FFT\n(magnitude; zoomed)")
228 ax[1].imshow(rts_fs[center[0] - radius:center[0] + radius,
229                     center[1] - radius:center[1] + radius],
230              cmap='magma')
231 ax[2].set_title("Log-Polar-Transformed\nOriginal FFT")
232 ax[2].imshow(warped_image_fs, cmap='magma')
233 ax[3].set_title("Log-Polar-Transformed\nModified FFT")
234 ax[3].imshow(warped_rts_fs, cmap='magma')
235 fig.suptitle('Working in frequency domain can recover rotation and scaling')
236 plt.show()
237 
238 print(f'Expected value for cc rotation in degrees: {angle}')
239 print(f'Recovered value for cc rotation: {recovered_angle}')
240 print()
241 print(f'Expected value for scaling difference: {scale}')
242 print(f'Recovered value for scaling difference: {shift_scale}')
243 
244 ######################################################################
245 # Some notes on this approach
246 # =================================================================
247 #
248 # It should be noted that this approach relies on a couple of parameters
249 # that have to be chosen ahead of time, and for which there are no clearly
250 # optimal choices:
251 #
252 # 1. The images should have some degree of bandpass filtering
253 # applied, particularly to remove high frequencies, and different choices here
254 # may impact outcome. The bandpass filter also complicates matters because,
255 # since the images to be registered will differ in scale and these scale
256 # differences are unknown, any bandpass filter will necessarily attenuate
257 # different features between the images. For example, the log-polar transformed
258 # magnitude spectra don't really look "alike" in the last example here. Yet if
259 # you look closely, there are some common patterns in those spectra, and they
260 # do end up aligning well by phase correlation as demonstrated.
261 #
262 # 2. Images must be windowed using windows with circular symmetry, to remove
263 # the spectral leakage coming from image borders. There is no clearly optimal
264 # choice of window.
265 #
266 # Finally, we note that large changes in scale will dramatically alter the
267 # magnitude spectra, especially since a big change in scale will usually be
268 # accompanied by some cropping and loss of information content. The literature
269 # advises staying within 1.8-2x scale change [1]_ [2]_. This is fine for most
270 # biological imaging applications.
271 #
272 # References
273 # ----------
274 #
275 # .. [1] B.S. Reddy and B.N. Chatterji. An FFT-based technique for translation,
276 #        rotation and scale- invariant image registration. IEEE Trans. Image
277 #        Processing, 5(8):1266–1271, 1996. :DOI:`10.1109/83.506761`
278 #
279 # .. [2] Tzimiropoulos, Georgios, and Tania Stathaki. "Robust FFT-based
280 #        scale-invariant image registration." In 4th SEAS DTC Technical
281 #        Conference. 2009. :DOI:`10.1109/TPAMI.2010.107`
282
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/doc/examples/registration/plot_register_rotation.py b/doc/examples/registration/plot_register_rotation.py
--- a/doc/examples/registration/plot_register_rotation.py
+++ b/doc/examples/registration/plot_register_rotation.py
@@ -60,7 +60,9 @@
 ax[3].imshow(rotated_polar)
 plt.show()
 
-shifts, error, phasediff = phase_cross_correlation(image_polar, rotated_polar)
+shifts, error, phasediff = phase_cross_correlation(image_polar,
+                                                   rotated_polar,
+                                                   normalization=None)
 print(f'Expected value for counterclockwise rotation in degrees: '
       f'{angle}')
 print(f'Recovered value for counterclockwise rotation: '
@@ -102,7 +104,8 @@
 
 # setting `upsample_factor` can increase precision
 shifts, error, phasediff = phase_cross_correlation(image_polar, rescaled_polar,
-                                                   upsample_factor=20)
+                                                   upsample_factor=20,
+                                                   normalization=None)
 shiftr, shiftc = shifts[:2]
 
 # Calculate scale factor from translation
@@ -151,7 +154,8 @@
 warped_image = warp_polar(image, radius=radius, scaling="log")
 warped_rts = warp_polar(rts_image, radius=radius, scaling="log")
 shifts, error, phasediff = phase_cross_correlation(warped_image, warped_rts,
-                                                   upsample_factor=20)
+                                                   upsample_factor=20,
+                                                   normalization=None)
 shiftr, shiftc = shifts[:2]
 klog = radius / np.log(radius)
 shift_scale = 1 / (np.exp(shiftc / klog))
@@ -209,7 +213,8 @@
 warped_rts_fs = warped_rts_fs[:shape[0] // 2, :]
 shifts, error, phasediff = phase_cross_correlation(warped_image_fs,
                                                    warped_rts_fs,
-                                                   upsample_factor=10)
+                                                   upsample_factor=10,
+                                                   normalization=None)
 
 # Use translation parameters to calculate rotation and scaling parameters
 shiftr, shiftc = shifts[:2]

{"golden_diff": "diff --git a/doc/examples/registration/plot_register_rotation.py b/doc/examples/registration/plot_register_rotation.py\n--- a/doc/examples/registration/plot_register_rotation.py\n+++ b/doc/examples/registration/plot_register_rotation.py\n@@ -60,7 +60,9 @@\n ax[3].imshow(rotated_polar)\n plt.show()\n \n-shifts, error, phasediff = phase_cross_correlation(image_polar, rotated_polar)\n+shifts, error, phasediff = phase_cross_correlation(image_polar,\n+                                                   rotated_polar,\n+                                                   normalization=None)\n print(f'Expected value for counterclockwise rotation in degrees: '\n       f'{angle}')\n print(f'Recovered value for counterclockwise rotation: '\n@@ -102,7 +104,8 @@\n \n # setting `upsample_factor` can increase precision\n shifts, error, phasediff = phase_cross_correlation(image_polar, rescaled_polar,\n-                                                   upsample_factor=20)\n+                                                   upsample_factor=20,\n+                                                   normalization=None)\n shiftr, shiftc = shifts[:2]\n \n # Calculate scale factor from translation\n@@ -151,7 +154,8 @@\n warped_image = warp_polar(image, radius=radius, scaling=\"log\")\n warped_rts = warp_polar(rts_image, radius=radius, scaling=\"log\")\n shifts, error, phasediff = phase_cross_correlation(warped_image, warped_rts,\n-                                                   upsample_factor=20)\n+                                                   upsample_factor=20,\n+                                                   normalization=None)\n shiftr, shiftc = shifts[:2]\n klog = radius / np.log(radius)\n shift_scale = 1 / (np.exp(shiftc / klog))\n@@ -209,7 +213,8 @@\n warped_rts_fs = warped_rts_fs[:shape[0] // 2, :]\n shifts, error, phasediff = phase_cross_correlation(warped_image_fs,\n                                                    warped_rts_fs,\n-                                                   upsample_factor=10)\n+                                                   upsample_factor=10,\n+                                                   normalization=None)\n \n # Use translation parameters to calculate rotation and scaling parameters\n shiftr, shiftc = shifts[:2]\n", "issue": "Gallery example 'Using Polar and Log-Polar Transformations for Registration' needs fixing\n## Description\r\nThe gallery example for Using Polar and Log-Polar Transformations for Registration seems to be broken as of 0.19.x. Was working in 0.18.x.\r\n\r\n## Way to reproduce\r\nUnsure if this is specific to the production doc build or an issue in the code. I am raising this issue to get it on the board before starting my testing.\r\n\r\n\r\n## Version information\r\nSee description. I'll report back in this thread when I know more.\r\n\n", "before_files": [{"content": "r\"\"\"\n==========================================================\nUsing Polar and Log-Polar Transformations for Registration\n==========================================================\n\nPhase correlation (``registration.phase_cross_correlation``) is an efficient\nmethod for determining translation offset between pairs of similar images.\nHowever this approach relies on a near absence of rotation/scaling differences\nbetween the images, which are typical in real-world examples.\n\nTo recover rotation and scaling differences between two images, we can take\nadvantage of two geometric properties of the log-polar transform and the\ntranslation invariance of the frequency domain. First, rotation in Cartesian\nspace becomes translation along the angular coordinate (:math:`\\theta`) axis\nof log-polar space. Second, scaling in Cartesian space becomes translation\nalong the radial coordinate (:math:`\\rho = \\ln\\sqrt{x^2 + y^2}`) of log-polar\nspace. Finally, differences in translation in the spatial domain do not impact\nmagnitude spectrum in the frequency domain.\n\nIn this series of examples, we build on these concepts to show how the\nlog-polar transform ``transform.warp_polar`` can be used in conjunction with\nphase correlation to recover rotation and scaling differences between two\nimages that also have a translation offset.\n\"\"\"\n\n######################################################################\n# Recover rotation difference with a polar transform\n# ==================================================\n#\n# In this first example, we consider the simple case of two images that only\n# differ with respect to rotation around a common center point. By remapping\n# these images into polar space, the rotation difference becomes a simple\n# translation difference that can be recovered by phase correlation.\n\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nfrom skimage import data\nfrom skimage.registration import phase_cross_correlation\nfrom skimage.transform import warp_polar, rotate, rescale\nfrom skimage.util import img_as_float\n\nradius = 705\nangle = 35\nimage = data.retina()\nimage = img_as_float(image)\nrotated = rotate(image, angle)\nimage_polar = warp_polar(image, radius=radius, channel_axis=-1)\nrotated_polar = warp_polar(rotated, radius=radius, channel_axis=-1)\n\nfig, axes = plt.subplots(2, 2, figsize=(8, 8))\nax = axes.ravel()\nax[0].set_title(\"Original\")\nax[0].imshow(image)\nax[1].set_title(\"Rotated\")\nax[1].imshow(rotated)\nax[2].set_title(\"Polar-Transformed Original\")\nax[2].imshow(image_polar)\nax[3].set_title(\"Polar-Transformed Rotated\")\nax[3].imshow(rotated_polar)\nplt.show()\n\nshifts, error, phasediff = phase_cross_correlation(image_polar, rotated_polar)\nprint(f'Expected value for counterclockwise rotation in degrees: '\n      f'{angle}')\nprint(f'Recovered value for counterclockwise rotation: '\n      f'{shifts[0]}')\n\n######################################################################\n# Recover rotation and scaling differences with log-polar transform\n# =================================================================\n#\n# In this second example, the images differ by both rotation and scaling (note\n# the axis tick values). By remapping these images into log-polar space,\n# we can recover rotation as before, and now also scaling, by phase\n# correlation.\n\n# radius must be large enough to capture useful info in larger image\nradius = 1500\nangle = 53.7\nscale = 2.2\nimage = data.retina()\nimage = img_as_float(image)\nrotated = rotate(image, angle)\nrescaled = rescale(rotated, scale, channel_axis=-1)\nimage_polar = warp_polar(image, radius=radius,\n                         scaling='log', channel_axis=-1)\nrescaled_polar = warp_polar(rescaled, radius=radius,\n                            scaling='log', channel_axis=-1)\n\nfig, axes = plt.subplots(2, 2, figsize=(8, 8))\nax = axes.ravel()\nax[0].set_title(\"Original\")\nax[0].imshow(image)\nax[1].set_title(\"Rotated and Rescaled\")\nax[1].imshow(rescaled)\nax[2].set_title(\"Log-Polar-Transformed Original\")\nax[2].imshow(image_polar)\nax[3].set_title(\"Log-Polar-Transformed Rotated and Rescaled\")\nax[3].imshow(rescaled_polar)\nplt.show()\n\n# setting `upsample_factor` can increase precision\nshifts, error, phasediff = phase_cross_correlation(image_polar, rescaled_polar,\n                                                   upsample_factor=20)\nshiftr, shiftc = shifts[:2]\n\n# Calculate scale factor from translation\nklog = radius / np.log(radius)\nshift_scale = 1 / (np.exp(shiftc / klog))\n\nprint(f'Expected value for cc rotation in degrees: {angle}')\nprint(f'Recovered value for cc rotation: {shiftr}')\nprint()\nprint(f'Expected value for scaling difference: {scale}')\nprint(f'Recovered value for scaling difference: {shift_scale}')\n\n######################################################################\n# Register rotation and scaling on a translated image - Part 1\n# =================================================================\n#\n# The above examples only work when the images to be registered share a\n# center. However, it is more often the case that there is also a translation\n# component to the difference between two images to be registered. One\n# approach to register rotation, scaling and translation is to first correct\n# for rotation and scaling, then solve for translation. It is possible to\n# resolve rotation and scaling differences for translated images by working on\n# the magnitude spectra of the Fourier-transformed images.\n#\n# In this next example, we first show how the above approaches fail when two\n# images differ by rotation, scaling, and translation.\n\nfrom skimage.color import rgb2gray\nfrom skimage.filters import window, difference_of_gaussians\nfrom scipy.fft import fft2, fftshift\n\nangle = 24\nscale = 1.4\nshiftr = 30\nshiftc = 15\n\nimage = rgb2gray(data.retina())\ntranslated = image[shiftr:, shiftc:]\nrotated = rotate(translated, angle)\nrescaled = rescale(rotated, scale)\nsizer, sizec = image.shape\nrts_image = rescaled[:sizer, :sizec]\n\n# When center is not shared, log-polar transform is not helpful!\nradius = 705\nwarped_image = warp_polar(image, radius=radius, scaling=\"log\")\nwarped_rts = warp_polar(rts_image, radius=radius, scaling=\"log\")\nshifts, error, phasediff = phase_cross_correlation(warped_image, warped_rts,\n                                                   upsample_factor=20)\nshiftr, shiftc = shifts[:2]\nklog = radius / np.log(radius)\nshift_scale = 1 / (np.exp(shiftc / klog))\n\nfig, axes = plt.subplots(2, 2, figsize=(8, 8))\nax = axes.ravel()\nax[0].set_title(\"Original Image\")\nax[0].imshow(image, cmap='gray')\nax[1].set_title(\"Modified Image\")\nax[1].imshow(rts_image, cmap='gray')\nax[2].set_title(\"Log-Polar-Transformed Original\")\nax[2].imshow(warped_image)\nax[3].set_title(\"Log-Polar-Transformed Modified\")\nax[3].imshow(warped_rts)\nfig.suptitle('log-polar-based registration fails when no shared center')\nplt.show()\n\nprint(f'Expected value for cc rotation in degrees: {angle}')\nprint(f'Recovered value for cc rotation: {shiftr}')\nprint()\nprint(f'Expected value for scaling difference: {scale}')\nprint(f'Recovered value for scaling difference: {shift_scale}')\n\n######################################################################\n# Register rotation and scaling on a translated image - Part 2\n# =================================================================\n#\n# We next show how rotation and scaling differences, but not translation\n# differences, are apparent in the frequency magnitude spectra of the images.\n# These differences can be recovered by treating the magnitude spectra as\n# images themselves, and applying the same log-polar + phase correlation\n# approach taken above.\n\n# First, band-pass filter both images\nimage = difference_of_gaussians(image, 5, 20)\nrts_image = difference_of_gaussians(rts_image, 5, 20)\n\n# window images\nwimage = image * window('hann', image.shape)\nrts_wimage = rts_image * window('hann', image.shape)\n\n# work with shifted FFT magnitudes\nimage_fs = np.abs(fftshift(fft2(wimage)))\nrts_fs = np.abs(fftshift(fft2(rts_wimage)))\n\n# Create log-polar transformed FFT mag images and register\nshape = image_fs.shape\nradius = shape[0] // 8  # only take lower frequencies\nwarped_image_fs = warp_polar(image_fs, radius=radius, output_shape=shape,\n                             scaling='log', order=0)\nwarped_rts_fs = warp_polar(rts_fs, radius=radius, output_shape=shape,\n                           scaling='log', order=0)\n\nwarped_image_fs = warped_image_fs[:shape[0] // 2, :]  # only use half of FFT\nwarped_rts_fs = warped_rts_fs[:shape[0] // 2, :]\nshifts, error, phasediff = phase_cross_correlation(warped_image_fs,\n                                                   warped_rts_fs,\n                                                   upsample_factor=10)\n\n# Use translation parameters to calculate rotation and scaling parameters\nshiftr, shiftc = shifts[:2]\nrecovered_angle = (360 / shape[0]) * shiftr\nklog = shape[1] / np.log(radius)\nshift_scale = np.exp(shiftc / klog)\n\nfig, axes = plt.subplots(2, 2, figsize=(8, 8))\nax = axes.ravel()\nax[0].set_title(\"Original Image FFT\\n(magnitude; zoomed)\")\ncenter = np.array(shape) // 2\nax[0].imshow(image_fs[center[0] - radius:center[0] + radius,\n                      center[1] - radius:center[1] + radius],\n             cmap='magma')\nax[1].set_title(\"Modified Image FFT\\n(magnitude; zoomed)\")\nax[1].imshow(rts_fs[center[0] - radius:center[0] + radius,\n                    center[1] - radius:center[1] + radius],\n             cmap='magma')\nax[2].set_title(\"Log-Polar-Transformed\\nOriginal FFT\")\nax[2].imshow(warped_image_fs, cmap='magma')\nax[3].set_title(\"Log-Polar-Transformed\\nModified FFT\")\nax[3].imshow(warped_rts_fs, cmap='magma')\nfig.suptitle('Working in frequency domain can recover rotation and scaling')\nplt.show()\n\nprint(f'Expected value for cc rotation in degrees: {angle}')\nprint(f'Recovered value for cc rotation: {recovered_angle}')\nprint()\nprint(f'Expected value for scaling difference: {scale}')\nprint(f'Recovered value for scaling difference: {shift_scale}')\n\n######################################################################\n# Some notes on this approach\n# =================================================================\n#\n# It should be noted that this approach relies on a couple of parameters\n# that have to be chosen ahead of time, and for which there are no clearly\n# optimal choices:\n#\n# 1. The images should have some degree of bandpass filtering\n# applied, particularly to remove high frequencies, and different choices here\n# may impact outcome. The bandpass filter also complicates matters because,\n# since the images to be registered will differ in scale and these scale\n# differences are unknown, any bandpass filter will necessarily attenuate\n# different features between the images. For example, the log-polar transformed\n# magnitude spectra don't really look \"alike\" in the last example here. Yet if\n# you look closely, there are some common patterns in those spectra, and they\n# do end up aligning well by phase correlation as demonstrated.\n#\n# 2. Images must be windowed using windows with circular symmetry, to remove\n# the spectral leakage coming from image borders. There is no clearly optimal\n# choice of window.\n#\n# Finally, we note that large changes in scale will dramatically alter the\n# magnitude spectra, especially since a big change in scale will usually be\n# accompanied by some cropping and loss of information content. The literature\n# advises staying within 1.8-2x scale change [1]_ [2]_. This is fine for most\n# biological imaging applications.\n#\n# References\n# ----------\n#\n# .. [1] B.S. Reddy and B.N. Chatterji. An FFT-based technique for translation,\n#        rotation and scale- invariant image registration. IEEE Trans. Image\n#        Processing, 5(8):1266\u20131271, 1996. :DOI:`10.1109/83.506761`\n#\n# .. [2] Tzimiropoulos, Georgios, and Tania Stathaki. \"Robust FFT-based\n#        scale-invariant image registration.\" In 4th SEAS DTC Technical\n#        Conference. 2009. :DOI:`10.1109/TPAMI.2010.107`\n", "path": "doc/examples/registration/plot_register_rotation.py"}], "after_files": [{"content": "r\"\"\"\n==========================================================\nUsing Polar and Log-Polar Transformations for Registration\n==========================================================\n\nPhase correlation (``registration.phase_cross_correlation``) is an efficient\nmethod for determining translation offset between pairs of similar images.\nHowever this approach relies on a near absence of rotation/scaling differences\nbetween the images, which are typical in real-world examples.\n\nTo recover rotation and scaling differences between two images, we can take\nadvantage of two geometric properties of the log-polar transform and the\ntranslation invariance of the frequency domain. First, rotation in Cartesian\nspace becomes translation along the angular coordinate (:math:`\\theta`) axis\nof log-polar space. Second, scaling in Cartesian space becomes translation\nalong the radial coordinate (:math:`\\rho = \\ln\\sqrt{x^2 + y^2}`) of log-polar\nspace. Finally, differences in translation in the spatial domain do not impact\nmagnitude spectrum in the frequency domain.\n\nIn this series of examples, we build on these concepts to show how the\nlog-polar transform ``transform.warp_polar`` can be used in conjunction with\nphase correlation to recover rotation and scaling differences between two\nimages that also have a translation offset.\n\"\"\"\n\n######################################################################\n# Recover rotation difference with a polar transform\n# ==================================================\n#\n# In this first example, we consider the simple case of two images that only\n# differ with respect to rotation around a common center point. By remapping\n# these images into polar space, the rotation difference becomes a simple\n# translation difference that can be recovered by phase correlation.\n\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nfrom skimage import data\nfrom skimage.registration import phase_cross_correlation\nfrom skimage.transform import warp_polar, rotate, rescale\nfrom skimage.util import img_as_float\n\nradius = 705\nangle = 35\nimage = data.retina()\nimage = img_as_float(image)\nrotated = rotate(image, angle)\nimage_polar = warp_polar(image, radius=radius, channel_axis=-1)\nrotated_polar = warp_polar(rotated, radius=radius, channel_axis=-1)\n\nfig, axes = plt.subplots(2, 2, figsize=(8, 8))\nax = axes.ravel()\nax[0].set_title(\"Original\")\nax[0].imshow(image)\nax[1].set_title(\"Rotated\")\nax[1].imshow(rotated)\nax[2].set_title(\"Polar-Transformed Original\")\nax[2].imshow(image_polar)\nax[3].set_title(\"Polar-Transformed Rotated\")\nax[3].imshow(rotated_polar)\nplt.show()\n\nshifts, error, phasediff = phase_cross_correlation(image_polar,\n                                                   rotated_polar,\n                                                   normalization=None)\nprint(f'Expected value for counterclockwise rotation in degrees: '\n      f'{angle}')\nprint(f'Recovered value for counterclockwise rotation: '\n      f'{shifts[0]}')\n\n######################################################################\n# Recover rotation and scaling differences with log-polar transform\n# =================================================================\n#\n# In this second example, the images differ by both rotation and scaling (note\n# the axis tick values). By remapping these images into log-polar space,\n# we can recover rotation as before, and now also scaling, by phase\n# correlation.\n\n# radius must be large enough to capture useful info in larger image\nradius = 1500\nangle = 53.7\nscale = 2.2\nimage = data.retina()\nimage = img_as_float(image)\nrotated = rotate(image, angle)\nrescaled = rescale(rotated, scale, channel_axis=-1)\nimage_polar = warp_polar(image, radius=radius,\n                         scaling='log', channel_axis=-1)\nrescaled_polar = warp_polar(rescaled, radius=radius,\n                            scaling='log', channel_axis=-1)\n\nfig, axes = plt.subplots(2, 2, figsize=(8, 8))\nax = axes.ravel()\nax[0].set_title(\"Original\")\nax[0].imshow(image)\nax[1].set_title(\"Rotated and Rescaled\")\nax[1].imshow(rescaled)\nax[2].set_title(\"Log-Polar-Transformed Original\")\nax[2].imshow(image_polar)\nax[3].set_title(\"Log-Polar-Transformed Rotated and Rescaled\")\nax[3].imshow(rescaled_polar)\nplt.show()\n\n# setting `upsample_factor` can increase precision\nshifts, error, phasediff = phase_cross_correlation(image_polar, rescaled_polar,\n                                                   upsample_factor=20,\n                                                   normalization=None)\nshiftr, shiftc = shifts[:2]\n\n# Calculate scale factor from translation\nklog = radius / np.log(radius)\nshift_scale = 1 / (np.exp(shiftc / klog))\n\nprint(f'Expected value for cc rotation in degrees: {angle}')\nprint(f'Recovered value for cc rotation: {shiftr}')\nprint()\nprint(f'Expected value for scaling difference: {scale}')\nprint(f'Recovered value for scaling difference: {shift_scale}')\n\n######################################################################\n# Register rotation and scaling on a translated image - Part 1\n# =================================================================\n#\n# The above examples only work when the images to be registered share a\n# center. However, it is more often the case that there is also a translation\n# component to the difference between two images to be registered. One\n# approach to register rotation, scaling and translation is to first correct\n# for rotation and scaling, then solve for translation. It is possible to\n# resolve rotation and scaling differences for translated images by working on\n# the magnitude spectra of the Fourier-transformed images.\n#\n# In this next example, we first show how the above approaches fail when two\n# images differ by rotation, scaling, and translation.\n\nfrom skimage.color import rgb2gray\nfrom skimage.filters import window, difference_of_gaussians\nfrom scipy.fft import fft2, fftshift\n\nangle = 24\nscale = 1.4\nshiftr = 30\nshiftc = 15\n\nimage = rgb2gray(data.retina())\ntranslated = image[shiftr:, shiftc:]\nrotated = rotate(translated, angle)\nrescaled = rescale(rotated, scale)\nsizer, sizec = image.shape\nrts_image = rescaled[:sizer, :sizec]\n\n# When center is not shared, log-polar transform is not helpful!\nradius = 705\nwarped_image = warp_polar(image, radius=radius, scaling=\"log\")\nwarped_rts = warp_polar(rts_image, radius=radius, scaling=\"log\")\nshifts, error, phasediff = phase_cross_correlation(warped_image, warped_rts,\n                                                   upsample_factor=20,\n                                                   normalization=None)\nshiftr, shiftc = shifts[:2]\nklog = radius / np.log(radius)\nshift_scale = 1 / (np.exp(shiftc / klog))\n\nfig, axes = plt.subplots(2, 2, figsize=(8, 8))\nax = axes.ravel()\nax[0].set_title(\"Original Image\")\nax[0].imshow(image, cmap='gray')\nax[1].set_title(\"Modified Image\")\nax[1].imshow(rts_image, cmap='gray')\nax[2].set_title(\"Log-Polar-Transformed Original\")\nax[2].imshow(warped_image)\nax[3].set_title(\"Log-Polar-Transformed Modified\")\nax[3].imshow(warped_rts)\nfig.suptitle('log-polar-based registration fails when no shared center')\nplt.show()\n\nprint(f'Expected value for cc rotation in degrees: {angle}')\nprint(f'Recovered value for cc rotation: {shiftr}')\nprint()\nprint(f'Expected value for scaling difference: {scale}')\nprint(f'Recovered value for scaling difference: {shift_scale}')\n\n######################################################################\n# Register rotation and scaling on a translated image - Part 2\n# =================================================================\n#\n# We next show how rotation and scaling differences, but not translation\n# differences, are apparent in the frequency magnitude spectra of the images.\n# These differences can be recovered by treating the magnitude spectra as\n# images themselves, and applying the same log-polar + phase correlation\n# approach taken above.\n\n# First, band-pass filter both images\nimage = difference_of_gaussians(image, 5, 20)\nrts_image = difference_of_gaussians(rts_image, 5, 20)\n\n# window images\nwimage = image * window('hann', image.shape)\nrts_wimage = rts_image * window('hann', image.shape)\n\n# work with shifted FFT magnitudes\nimage_fs = np.abs(fftshift(fft2(wimage)))\nrts_fs = np.abs(fftshift(fft2(rts_wimage)))\n\n# Create log-polar transformed FFT mag images and register\nshape = image_fs.shape\nradius = shape[0] // 8  # only take lower frequencies\nwarped_image_fs = warp_polar(image_fs, radius=radius, output_shape=shape,\n                             scaling='log', order=0)\nwarped_rts_fs = warp_polar(rts_fs, radius=radius, output_shape=shape,\n                           scaling='log', order=0)\n\nwarped_image_fs = warped_image_fs[:shape[0] // 2, :]  # only use half of FFT\nwarped_rts_fs = warped_rts_fs[:shape[0] // 2, :]\nshifts, error, phasediff = phase_cross_correlation(warped_image_fs,\n                                                   warped_rts_fs,\n                                                   upsample_factor=10,\n                                                   normalization=None)\n\n# Use translation parameters to calculate rotation and scaling parameters\nshiftr, shiftc = shifts[:2]\nrecovered_angle = (360 / shape[0]) * shiftr\nklog = shape[1] / np.log(radius)\nshift_scale = np.exp(shiftc / klog)\n\nfig, axes = plt.subplots(2, 2, figsize=(8, 8))\nax = axes.ravel()\nax[0].set_title(\"Original Image FFT\\n(magnitude; zoomed)\")\ncenter = np.array(shape) // 2\nax[0].imshow(image_fs[center[0] - radius:center[0] + radius,\n                      center[1] - radius:center[1] + radius],\n             cmap='magma')\nax[1].set_title(\"Modified Image FFT\\n(magnitude; zoomed)\")\nax[1].imshow(rts_fs[center[0] - radius:center[0] + radius,\n                    center[1] - radius:center[1] + radius],\n             cmap='magma')\nax[2].set_title(\"Log-Polar-Transformed\\nOriginal FFT\")\nax[2].imshow(warped_image_fs, cmap='magma')\nax[3].set_title(\"Log-Polar-Transformed\\nModified FFT\")\nax[3].imshow(warped_rts_fs, cmap='magma')\nfig.suptitle('Working in frequency domain can recover rotation and scaling')\nplt.show()\n\nprint(f'Expected value for cc rotation in degrees: {angle}')\nprint(f'Recovered value for cc rotation: {recovered_angle}')\nprint()\nprint(f'Expected value for scaling difference: {scale}')\nprint(f'Recovered value for scaling difference: {shift_scale}')\n\n######################################################################\n# Some notes on this approach\n# =================================================================\n#\n# It should be noted that this approach relies on a couple of parameters\n# that have to be chosen ahead of time, and for which there are no clearly\n# optimal choices:\n#\n# 1. The images should have some degree of bandpass filtering\n# applied, particularly to remove high frequencies, and different choices here\n# may impact outcome. The bandpass filter also complicates matters because,\n# since the images to be registered will differ in scale and these scale\n# differences are unknown, any bandpass filter will necessarily attenuate\n# different features between the images. For example, the log-polar transformed\n# magnitude spectra don't really look \"alike\" in the last example here. Yet if\n# you look closely, there are some common patterns in those spectra, and they\n# do end up aligning well by phase correlation as demonstrated.\n#\n# 2. Images must be windowed using windows with circular symmetry, to remove\n# the spectral leakage coming from image borders. There is no clearly optimal\n# choice of window.\n#\n# Finally, we note that large changes in scale will dramatically alter the\n# magnitude spectra, especially since a big change in scale will usually be\n# accompanied by some cropping and loss of information content. The literature\n# advises staying within 1.8-2x scale change [1]_ [2]_. This is fine for most\n# biological imaging applications.\n#\n# References\n# ----------\n#\n# .. [1] B.S. Reddy and B.N. Chatterji. An FFT-based technique for translation,\n#        rotation and scale- invariant image registration. IEEE Trans. Image\n#        Processing, 5(8):1266\u20131271, 1996. :DOI:`10.1109/83.506761`\n#\n# .. [2] Tzimiropoulos, Georgios, and Tania Stathaki. \"Robust FFT-based\n#        scale-invariant image registration.\" In 4th SEAS DTC Technical\n#        Conference. 2009. :DOI:`10.1109/TPAMI.2010.107`\n", "path": "doc/examples/registration/plot_register_rotation.py"}]}