See https://laurentperrinet.github.io/sciblog/posts/2014-11-10_orientation.html
Re-compile this presentation using https://laurentperrinet.github.io/sciblog/files/2016-09-26_Perrinet16journalClub/2016-09-26_Perrinet16journalClub.ipynb
import numpy as np
import MotionClouds as mc
fx, fy, ft = mc.get_grids(mc.N_X, mc.N_Y, mc.N_frame)
name = 'balaV1'
N_X = fx.shape[0]
width = 29.7*256/1050
sf_0 = 4.*width/N_X
B_V = 2.5 # BW temporal frequency (speed plane thickness)
B_sf = sf_0 # BW spatial frequency
theta = 0.0 # Central orientation
B_theta_low, B_theta_high = np.pi/32, 2*np.pi
B_V = 0.5
seed=12234565
mc1 = mc.envelope_gabor(fx, fy, ft, V_X=0., V_Y=0., B_V=B_V, sf_0=sf_0, B_sf=B_sf, theta=theta, B_theta=B_theta_low)
import numpy as np
import MotionClouds as mc
mc.figpath = figpath
fx, fy, ft = mc.get_grids(mc.N_X, mc.N_Y, mc.N_frame)
name = 'balaV1'
N_X = fx.shape[0]
width = 29.7*256/1050
sf_0 = 4.*width/N_X
B_V = 2.5 # BW temporal frequency (speed plane thickness)
B_sf = sf_0 # BW spatial frequency
theta = 0.0 # Central orientation
B_theta_low, B_theta_high = np.pi/32, 2*np.pi
B_V = 0.5
seed=12234565
mc1 = mc.envelope_gabor(fx, fy, ft, V_X=0., V_Y=0., B_V=B_V, sf_0=sf_0, B_sf=B_sf, theta=theta, B_theta=B_theta_low)
name_ = name + '_1'
mc.figures(mc1, name_, seed=seed)
mc.in_show_video(name_, figpath=figpath)
mc2 = mc.envelope_gabor(fx, fy, ft, V_X=0., V_Y=0., B_V=B_V, sf_0=sf_0, B_sf=B_sf, theta=theta, B_theta=B_theta_high)
name_ = name + '_2'
mc.figures(mc2, name_, seed=seed)
mc.in_show_video(name_, figpath=figpath)
Loi de Von Mises (loi normale circulaire) telle que $f(\theta+\pi) = f(\theta)$ définie par :
$$ f(\theta) \propto e^{\kappa{cos(2(\theta - m))}} $$Par analogie avec la déviation standard d'une loi Gaussienne, on définit $\kappa = \frac {1}{\sigma^{2}}$.
def envelope(th, theta, B_theta):
if B_theta==np.inf:
env = np.ones_like(th)
elif B_theta==0:
env = np.zeros_like(th)
env[np.argmin(th < theta)] = 1.
else:
env = np.exp((np.cos(2*(th-theta))-1)/4/B_theta**2)
return env/env.max()
N_theta = 12
bins = 180
th = np.linspace(0, np.pi, bins, endpoint=False)
fig, axs = plt.subplots(1, 2, figsize=(fig_width, fig_width/phi/2))
for i, B_theta_ in enumerate([np.pi/12, np.pi/4]):#[0, np.pi/64, np.pi/32, np.pi/16, np.pi/8, np.pi/4, np.pi/2, np.inf]:
for theta, color in zip(np.linspace(0, np.pi, N_theta, endpoint=False),
[plt.cm.hsv(h) for h in np.linspace(0, 1, N_theta)]):
axs[i].plot(th*180/np.pi, envelope(th, theta, B_theta_), alpha=.6, color=color, lw=3)
axs[i].fill_between(th*180/np.pi, 0, envelope(th, theta, B_theta_), alpha=.1, color=color)
axs[i].set_xlim([0, 180])
axs[i].set_ylim([0, 1.1])
axs[i].set_xticks(np.linspace(0, 180, 5, endpoint=True) )#to specify number of tick…
for ax in axs:
for item in ([ax.title, ax.xaxis.label, ax.yaxis.label] +
ax.get_xticklabels() + ax.get_yticklabels()):
item.set_fontsize(fontsize)
fx, fy, ft = mc.get_grids(mc.N_X, mc.N_Y, 1)
N_theta = 6
bw_values = np.pi*np.logspace(-2, -5, N_theta, base=2)
fig, axs = plt.subplots(1, N_theta, figsize=(fig_width, fig_width/N_theta))
for i_ax, B_theta in enumerate(bw_values):
mc_i = mc.envelope_gabor(fx, fy, ft, V_X=0., V_Y=0.,
theta=np.pi/2, B_theta=B_theta)
im = mc.random_cloud(mc_i)
axs[i_ax].imshow(im[:, :, 0], cmap=plt.gray())
axs[i_ax].text(5, 29, r'$B_\theta=%.1f$°' % (B_theta*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.)
name = 'smooth'
theta_phi = np.pi * (3 - np.sqrt(5)) # https://en.wikipedia.org/wiki/Golden_angle
if mc.check_if_anim_exist(name):
fx, fy, ft = mc.get_grids(mc.N_X, mc.N_Y, mc.N_frame//2)
smooth = (ft - ft.min())/(ft.max() - ft.min()) # smoothly progress from 0. to 1.
seed = 123456
B_theta_ = B_theta_high*2.**-np.arange(5)
B_theta_ = np.hstack((B_theta_, B_theta_[1:-1][::-1]))
print('Smoothly changing B_theta along', B_theta_, '... and so on...')
im = np.empty(shape=(mc.N_X, mc.N_Y, 0))
N = len(B_theta_)
im = np.empty(shape=(mc.N_X, mc.N_Y, 0))
for i_theta, B_theta in enumerate(B_theta_):
im_old = mc.random_cloud(mc.envelope_gabor(fx, fy, ft, theta=theta_phi, B_theta=B_theta, V_X=0), seed=seed)
im_new = mc.random_cloud(mc.envelope_gabor(fx, fy, ft, theta=theta_phi, B_theta=B_theta_[(i_theta+1) % N], V_X=0), seed=seed)
im = np.concatenate((im, (1.-smooth)*im_old+smooth*im_new), axis=-1)
mc.anim_save(mc.rectif(im), os.path.join(mc.figpath, name))
#mc.in_show_video(name, figpath=figpath)
show_video(os.path.join(mc.figpath, name), height=.8*height, width=.8*height)
show_image('https://raw.githubusercontent.com/laurentperrinet/OBV1/master/figs/Ben_Kenobi.png', height=height*.8)
show_image('https://raw.githubusercontent.com/laurentperrinet/OBV1/master/figs/Fig-1-a-Model-Schematic-The-model-consists-of-the-three-major-subsystems-retina.png', height=.4*height)
show_image('https://github.com/laurentperrinet/OBV1/raw/master/figs/pasturel_result.png', height=.4*height)
show_image('https://raw.githubusercontent.com/laurentperrinet/OBV1/master/figs/nn3977-F1.jpg', height=height*.8)
''The compass within'', Nathan W Schultheiss & A David Redish (Nature Neuroscience 18, 482–483 (2015) doi:10.1038/nn.3977 http://www.nature.com/neuro/journal/v18/n4/full/nn.3977.html)
show_image('https://raw.githubusercontent.com/laurentperrinet/OBV1/master/figs/future_model.png', height=height)
show_image('https://raw.githubusercontent.com/laurentperrinet/OBV1/master/figs/ringRecurrent.png', height=height)