# Changing the global phase of a Motion Cloud

Motion Clouds were defined in the origin to define parameterized moving textures. In that other post, we defined a simple code to generate static images using a simple code. Can we generate a series of images while changing the phase globally? Let's first generate a static motion cloud:

In :
import MotionClouds as mc
mc.N_frame, seed = 1, 42
fx, fy, ft = mc.get_grids(mc.N_X, mc.N_Y, mc.N_frame)
name = 'phase'
env = mc.envelope_gabor(fx, fy, ft, V_X=0., V_Y=0., B_V=0)
z = mc.rectif(mc.random_cloud(env, seed=seed))
print(z.shape)
z = z.reshape((mc.N_X, mc.N_Y))

(256, 256, 1)

In :
import matplotlib.pyplot as plt
%matplotlib inline
fig, ax = plt.subplots(figsize=(10,10))
_ = ax.imshow(z.T, cmap=plt.gray()) The function generating the texture takes an envelope and multiplies it with a random phase matrix to generate the image:

In :
help(mc.random_cloud)

Help on function random_cloud in module MotionClouds:

random_cloud(envelope, seed=None, impulse=False, events=None, do_amp=True)
Returns a Motion Cloud movie as a 3D matrix from a given envelope.

It first creates a random phase spectrum, multiplies with the envelope and
then it computes the inverse FFT to obtain the spatiotemporal stimulus.

Options are:
* use a specific seed to specify the RNG's seed,
* test the impulse response of the kernel by setting impulse to True
* test the effect of randomizing amplitudes too by setting do_amp to True
shape

# TODO : issue a warning if more than 10% of the energy of the envelope falls off the Fourier cube
# TODO : use a safety sphere to ensure all orientations are evenly chosen



To globally change the phase of the gabors in the texture, a "trick" is to multiply this same envelope (such that we keep it). If we keep the same seed, we keep the same phase scrambling. Then, we perform for each coefficient in the fourier space a rotation in the complex plane (that is, by multiplying by $\exp(i\cdot\phi)$). We perform this globally on all points of the Fourier space:

In :
N_phase = 4
fig_width = 21
import numpy as np

fig, axs = plt.subplots(1, N_phase, figsize=(fig_width, fig_width/N_phase))
for i_ax, phase in enumerate(np.linspace(0, 2*np.pi, N_phase, endpoint=False)):
im = mc.rectif(mc.random_cloud(env*np.exp(1j * phase), seed=seed))

axs[i_ax].imshow(im[:, :, 0], cmap=plt.gray())
axs[i_ax].text(5, 29, r'$\phi=%.1f$°' % (phase*180/np.pi), color='white', fontsize=32)
axs[i_ax].set_xticks([])
axs[i_ax].set_yticks([])
#plt.tight_layout()
fig.subplots_adjust(hspace = .0, wspace = .0, left=0.0, bottom=0., right=1., top=1.)

#import os
#fig.savefig(os.path.join('../figs', 'orientation_tuning.png')) Notice that white features become black with a multiplication of $\exp(i\cdot\pi)=-1$

For fun, let us now try to generate an animation:

In :
import imageio
from pygifsicle import optimize
N_phase = 32
fps, dpi = 8, (128, 128)

for i_phase, phase in enumerate(np.linspace(0, 2*np.pi, N_phase, endpoint=False)):
im = (mc.rectif(mc.random_cloud(env*np.exp(1j * phase), seed=seed))*255).astype(np.uint8)
imageio.imsave(f"/tmp/MC_phase_{i_phase:02d}.png", im, dpi=dpi)

gifname = '../files/2020-01-08-MC_phase.gif'
with imageio.get_writer(gifname, mode='I', fps=fps) as writer:
for i_phase in range(N_phase):
optimize(str(gifname))
from IPython.display import Image
Image(gifname, width=2000)

Out:
<IPython.core.display.Image object>

For even more fun, let us now try to generate an animation of this process, but now for a natural scene:

In :
N_phase = 32
fps, dpi = 8, (128, 128)

url = 'https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2Fwww.trbimg.com%2Fimg-57c7186a%2Fturbine%2Fmc-paul-stanley-kiss-allentown-fair-20160831&f=1&nofb=1'
image.shape

Out:
(1365, 2048, 3)
In :
image = image.sum(axis=-1)
image.shape

Out:
(1365, 2048)
In :
image = image[:-1:2, ::2]
image.shape

Out:
(682, 1024)
In :
from SLIP import Image
slip = Image('https://raw.githubusercontent.com/bicv/SLIP/master/default_param.py')
slip.set_size(image)

In :
image = slip.whitening(image)

In :
Fz = np.fft.fftn(image)

In :
phase = 0
im = np.fft.ifftn(Fz*np.exp(1j * phase)).real

In :
mc.rectif(im).min(), mc.rectif(im).max()

Out:
(0.08385036684021319, 1.0)
In :
for i_phase, phase in enumerate(np.linspace(0, 2*np.pi, N_phase, endpoint=False)):
# print(i_phase, phase, f"/tmp/NI_phase_{i_phase:02d}.png")
im = np.fft.ifftn(Fz*np.exp(1j * phase)).real
im = slip.dewhitening(im)
im = (mc.rectif(im)*255).astype(np.uint8)
imageio.imsave(f"/tmp/NI_phase_{i_phase:02d}.png", im, dpi=dpi)

In :
gifname = '../files/2020-01-08-NI_phase.gif'
with imageio.get_writer(gifname, mode='I', fps=fps) as writer:
for i_phase in range(N_phase):
optimize(str(gifname)) Note, that doing a smooth variation of phase produces slight movements of the features, at the scale of each texton.

### some book keeping for the notebook¶

In :
%load_ext watermark
%watermark

2020-01-24T10:44:40+01:00

CPython 3.7.6
IPython 7.11.1

compiler   : Clang 11.0.0 (clang-1100.0.33.16)
system     : Darwin
release    : 19.2.0
machine    : x86_64
processor  : i386
CPU cores  : 36
interpreter: 64bit

In :
%load_ext watermark
%watermark -i -h -m -v -p MotionClouds,numpy,SLIP,LogGabor,SparseEdges,matplotlib,scipy,pillow,imageio  -r -g -b

The watermark extension is already loaded. To reload it, use:
2020-01-24T10:44:40+01:00

CPython 3.7.6
IPython 7.11.1

MotionClouds 20180606
numpy 1.18.1
SLIP 20191113
LogGabor 20191113
SparseEdges 20191118
matplotlib 3.1.2
scipy 1.4.1
pillow not installed
imageio 2.6.1

compiler   : Clang 11.0.0 (clang-1100.0.33.16)
system     : Darwin
release    : 19.2.0
machine    : x86_64
processor  : i386
CPU cores  : 36
interpreter: 64bit
host name  : fortytwo