Reverse-phi and Asymmetry of ON and OFF responses
We test Reverse-phi motion and the Asymmetry of ON and OFF responses using MotionClouds.
import os
import MotionClouds as mc
mc.figpath = '../files/2014-11-10_reverse-phi'
if not(os.path.isdir(mc.figpath)): os.mkdir(mc.figpath)
import scipy.ndimage as nd
import matplotlib.cm as cm
import numpy as np
%pylab inline
import holoviews as hv
#hv.notebook_extension('bokeh')
hv.notebook_extension()
nb_name = 'reverse_'
reverse phi motion generator¶
def toss():
return (np.random.rand()>.5)*2-1
def mcg(img, wait=12, term=6, shift_range=4, reverse=True, nb_test=1, random=False, verbose=False):
"""
From a (square, gray level) image, we write a funtion that returns a (3D ndarray) movie with a revese phi motion.
This movie consists of nb_test*(wait + 2*term) gray scale frames where:
nb_test: is the number of repetition (in case of random tosses of the shift)
wait: the number of frames we are waiting
term: the number of frames we are presenting one frame and the then the other
reverse: if we reverse the contrast
"""
if (nb_test <= 0):
return (movie[0, 0, 0])
period = wait + 2*term
movie = np.zeros([img.shape[0], img.shape[1], (nb_test*(wait + 2*term))])
for i_test in range(nb_test):
if random:
contrast = toss()
shift = toss() * np.random.randint(shift_range)
else:
contrast = 2*(reverse==False) - 1
shift = shift_range
if verbose: print('contrast=', contrast)
start = i_test*period + wait
movie[:, :, start:(start + term)] = img[:, :, np.newaxis]
img_shifted = np.roll(img, shift, 1) * contrast
movie[:, :, (start + term):(start + 2*term)] = img_shifted[:, :, np.newaxis]
return movie
Random Image¶
Let's first try with a binary random image:
%%opts Image style(cmap='gray')
pictogram = 2. * (np.random.rand(64, 64) > .5) - 1.
hv.Image(pictogram).hist()
The simplest movement is to have no reversal ($\phi$-motion) the image goes up:
movie = mcg(img=pictogram, reverse=False)
name = nb_name + 'pictogram-phi'
mc.anim_save(mc.rectif(movie), os.path.join(mc.figpath, name))
mc.in_show_video(name, figpath=mc.figpath)
The simplest movement is to have no reversal ($\phi$-motion) the image goes down:
movie = mcg(img=pictogram, reverse=False, shift_range =-4)
name = nb_name + 'pictogram-phidown'
mc.anim_save(mc.rectif(movie), os.path.join(mc.figpath, name))
mc.in_show_video(name, figpath=mc.figpath)
Flipping the contrast induces a reversal (reverse-$\phi$ motion):
movie = mcg(img=pictogram, reverse=True)
name = nb_name + 'pictogram-reversephi'
mc.anim_save(mc.rectif(movie), os.path.join(mc.figpath, name))
mc.in_show_video(name, figpath=mc.figpath)
Showing a sequence of such motions with random reversals and shifts:
movie = mcg(img=pictogram, nb_test=10, random=True)
name = nb_name + 'pictogram'
mc.anim_save(mc.rectif(movie), os.path.join(mc.figpath, name))
mc.in_show_video(name, figpath=mc.figpath)
trying out with a (natural) image¶
from scipy.misc import face
color = face()
print('Size of image @ loading ', color.shape)
image = color[:, (1024-768):, :].mean(axis=-1)
image = 2* (image - image.min()) / (image.max() - image.min()) - 1.
print('Size of image after square reshaping + gray levels ', image.shape)
%%opts Image style(cmap='gray')
hv.RGB(color) + hv.Image(image).hist()
movie = mcg(img=np.rot90(image, 3))
name = nb_name + 'natural'
mc.anim_save(mc.rectif(movie), os.path.join(mc.figpath, name))
mc.in_show_video(name, figpath=mc.figpath)
Showing a sequence of such motions with random reversals and shifts demonstrates that there is a "surprising" effect related to the reversal:
movie = mcg(img=np.rot90(image, 3), nb_test=10, random=True)
name = nb_name + 'natural_random'
mc.anim_save(mc.rectif(movie), os.path.join(mc.figpath, name))
mc.in_show_video(name, figpath=mc.figpath)
static MotionClouds¶
trying with just one frame of a motion cloud:
%%opts Image style(cmap='gray')
fx, fy, ft = mc.get_grids(mc.N_X, mc.N_Y, 1)
name = nb_name + 'MC1'
image = mc.rectif(mc.random_cloud(mc.envelope_gabor(fx, fy, ft)))
image = 2. * image.reshape((mc.N_X, mc.N_Y)) - 1.
image = np.rot90(image, 1)
hv.Image(image).hist()
The simplest movement is to have no reversal ($\phi$-motion) the image goes up:
movie = mcg(img=image, reverse=False)
name = nb_name + 'MC1-phi'
mc.anim_save(mc.rectif(movie), os.path.join(mc.figpath, name))
mc.in_show_video(name, figpath=mc.figpath)
Flipping the contrast induces a reversal (reverse-$\phi$ motion):
movie = mcg(img=image, reverse=True)
name = nb_name + 'MC1-reversephi'
mc.anim_save(mc.rectif(movie), os.path.join(mc.figpath, name))
mc.in_show_video(name, figpath=mc.figpath)
Showing a sequence of such motions with random reversals and shifts shows that the difference between a reversal or not is harder to catch than on a natural image:
movie = mcg(img=image, nb_test=10, random=True)
name = nb_name + 'MC1re-random'
mc.anim_save(mc.rectif(movie), os.path.join(mc.figpath, name))
mc.in_show_video(name, figpath=mc.figpath)
MotionClouds¶
trying now with a full motion cloud:
fx, fy, ft = mc.get_grids(mc.N_X, mc.N_Y, mc.N_frame)
name = nb_name + 'MC'
env = mc.envelope_gabor(fx, fy, ft)
env = rot90(env)
mc.figures(env, name, seed=12234565, figpath=mc.figpath)
mc.in_show_video(name, figpath=mc.figpath)
Now generate a reverse phi stimulus by showing just 2 frames the next one being filpped over:
i_frame = 4
mov = mc.random_cloud(env)
mov_ = np.zeros_like(mov)
print ('Size of movie @ loading ', mov_[:, :, (mc.N_frame//2-i_frame):(mc.N_frame//2)].shape, '\nSize of frame = ', mov[:, :, mc.N_frame//2-1].shape)
mov_[:, :, (mc.N_frame//2-i_frame):(mc.N_frame//2)] = mov[:, :, mc.N_frame//2-1][:, :, np.newaxis]
mov_[:, :, (mc.N_frame//2):(mc.N_frame//2+i_frame)] = mov[:, :, mc.N_frame//2][:, :, np.newaxis]
mov_ = mc.rectif(mov_, contrast=1.)
mc.anim_save(mov_, os.path.join(mc.figpath, name + '_just_Phi'))
mc.in_show_video(name + '_just_Phi', figpath=mc.figpath)
%%opts Image style(cmap='gray')
key_dims = [hv.Dimension('x',range=(-.5,.5)), hv.Dimension('y',range=(-.5,.5))]
one = hv.Image(mov_[:, :, mc.N_frame//2-i_frame].T, group='one frame', key_dimensions=key_dims)
two = hv.Image(mov_[:, :, mc.N_frame//2].T, group='second frame', key_dimensions=key_dims).hist()
one + two
i_frame = 4
mov = mc.random_cloud(env)
mov_ = np.zeros_like(mov)
print ('Size of movie @ loading ', mov_[:, :, (mc.N_frame//2-i_frame):(mc.N_frame//2)].shape, '\nSize of frame = ', mov[:, :, mc.N_frame//2-1].shape)
mov_[:, :, (mc.N_frame//2-i_frame):(mc.N_frame//2)] = mov[:, :, mc.N_frame//2-1][:, :, np.newaxis]
mov_[:, :, (mc.N_frame//2):(mc.N_frame//2+i_frame)] = -mov[:, :, mc.N_frame//2][:, :, np.newaxis]
mov_ = mc.rectif(mov_, contrast=1.)
mc.anim_save(mov_, os.path.join(mc.figpath, name + '_reverse_Phi'))
mc.in_show_video(name + '_reverse_Phi', figpath=mc.figpath)
%%opts Image style(cmap='gray')
resone = hv.Image(mov_[:, :, mc.N_frame//2-i_frame].T, group='one frame', key_dimensions=key_dims)
two = hv.Image(mov_[:, :, mc.N_frame//2].T, group='second frame', key_dimensions=key_dims).hist()
one + two
some book keeping for the notebook¶
%load_ext version_information
%version_information numpy, scipy, matplotlib, MotionClouds