OSCR

Movie reconstruction from mouse visual cortex activity.

Code ↔ Paper

12 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 12 matches
  1. [1] § Methods › State-of-the-art dynamic neural encoding model ↔ src/models/dwiseneuro.py, lines 343–405 · score 0.70 · SiLU, DwiseNeuro, Softplus, readout, kernel, cortex
  2. [2] § Results › Video reconstruction using state-of-the-art dynamic neural encoding models › Not all spatial and temporal frequencies are reconstructed equally ↔ scripts/analyse_reconstructions_gaussian_noise.ipynb, lines 11–81 · score 0.61 · phase inverted, Reconstructed Gaussian, Gaussian noise, temporal length constants, stimulus, mask
  3. [3] § Methods › Additional visual stimuli ↔ scripts/analyse_reconstructions_gaussian_noise.ipynb, lines 11–81 · score 0.60 · phase inverted, Gaussian noise, temporal length constant, stimuli
  4. [4] § Methods › Additional visual stimuli ↔ scripts/reconstruct_videos_gaussian_noise.py, lines 111–162 · score 0.60 · phase inverted, Gaussian noise, temporal length constant, stimuli
  5. [5] § Methods › Mask training ↔ utils_reconstruction/utils_reconstruction.py, lines 15–76 · score 0.57 · alpha blend, background video, layer, masks
  6. [6] § Results › Video reconstruction using state-of-the-art dynamic neural encoding models › Not all spatial and temporal frequencies are reconstructed equally ↔ scripts/analyse_reconstructions_drifting_gratings.ipynb, lines 153–289 · score 0.57 · temporal frequencies, temporal length constants, drifting, gratings, square, inverted
  7. [7] § Results › Video reconstruction using state-of-the-art dynamic neural encoding models ↔ scripts/predict_response_from_recon.py, lines 157–239 · score 0.55 · luminance matching, Poisson loss, full video, neurons, activity, correlation
  8. [8] § Results › Video reconstruction using state-of-the-art dynamic neural encoding models ↔ scripts/analyse_reconstructions_from_predicted_activity.ipynb, lines 495–575 · score 0.55 · evaluation mask threshold, Predicted activity, video correlation, diameter, ensembling, gradient
  9. [9] § Results › Video reconstruction using state-of-the-art dynamic neural encoding models › Not all spatial and temporal frequencies are reconstructed equally ↔ scripts/analyse_reconstructions_gaussian_noise.ipynb, lines 420–486 · score 0.54 · Gaussian noise, Shannon entropy, motion energy, temporal, frame, reconstructions
  10. [10] § Results › Video reconstruction using state-of-the-art dynamic neural encoding models › Not all spatial and temporal frequencies are reconstructed equally ↔ scripts/analyse_reconstructions_drifting_gratings.ipynb, lines 153–289 · score 0.53 · Reconstructed drifting grating, temporal frequencies, interleaved, ground truth, correlation, masked
  11. [11] § Results › Video reconstruction using state-of-the-art dynamic neural encoding models › Not all spatial and temporal frequencies are reconstructed equally ↔ scripts/analyse_reconstructions_gaussian_noise.ipynb, lines 211–349 · score 0.53 · Gaussian noise, temporal length constants, square, inverted, ground truth, ensembled
  12. [12] § Results › Video reconstruction using state-of-the-art dynamic neural encoding models › High-quality video reconstruction ↔ scripts/analyse_reconstructions.ipynb, lines 949–972 · score 0.51 · pupil diameter, motion energy, speed, eye, luminance, correlating

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Jupyter notebook · 569 lines · 29 KB · MIT · 4 matches

  1. # %%
  2. import sys
  3. sys.path.append('../')
  4. import numpy as np
  5. import matplotlib.pyplot as plt
  6. import scipy as sp
  7. from utils_reconstruction import image_similarity as imsim
  8. import tifffile
  9. # %%
  10. ## load reconstruction .npy files
  11. num_neurons = [7863, 7908, 8202, 7939, 8122]
  12. mouse_names = [
  13. "dynamic29515-10-12-Video-9b4f6a1a067fe51e15306b9628efea20",
  14. "dynamic29623-4-9-Video-9b4f6a1a067fe51e15306b9628efea20",
  15. "dynamic29647-19-8-Video-9b4f6a1a067fe51e15306b9628efea20",
  16. "dynamic29712-5-9-Video-9b4f6a1a067fe51e15306b9628efea20",
  17. "dynamic29755-2-8-Video-9b4f6a1a067fe51e15306b9628efea20",
  18. ]
  19. max_num_neurons = np.max(num_neurons)
  20. model_list = np.array([0,1,2,3,4,5,6]) # 1,2,3,4,5,6 # 0 held out!
  21. mice = range(0,3)
  22. reps = np.array([0,1,2,3,4])# range(0,5)
  23. spatial_length_constant = range(0,7)
  24. temporal_length_constant = range(0,7)
  25. eval_frame_skip = 30 # default 30
  26. mask_th = 1
  27. video_gt=np.nan*np.ones((len(mice),len(reps)*2,len(spatial_length_constant),len(temporal_length_constant),60,36,64))
  28. video_pred=np.nan*np.ones((model_list.size,len(mice),len(reps)*2,len(spatial_length_constant),len(temporal_length_constant),60,36,64))
  29. responses_pred_gt=np.nan*np.ones((model_list.size,len(mice),len(reps)*2,len(spatial_length_constant),len(temporal_length_constant),max_num_neurons,60))
  30. responses_pred_recon=np.nan*np.ones((model_list.size,len(mice),len(reps)*2,len(spatial_length_constant),len(temporal_length_constant),max_num_neurons,60))
  31. mask=np.nan*np.ones((len(mice),36,64))
  32. for model_n in model_list:
  33. for mouse in mice:
  34. for rep in range(0,len(reps)):#reps:
  35. for spatial_n in spatial_length_constant:
  36. for temporal_n in temporal_length_constant:
  37. datapath=f'../reconstructions/modelfold{model_n}_round4_Gausnoise/reconstruction_summary_m{mouse}_r{reps[rep]}s{spatial_n}t{temporal_n}.npy'
  38. print(datapath)
  39. data = np.load(datapath, allow_pickle=True).item()
  40. if model_n==model_list[0]:
  41. video_gt[mouse,rep,spatial_n,temporal_n] = data['video_gt']
  42. video_pred[model_n-1,mouse,rep,spatial_n,temporal_n] = data['video_pred']
  43. mask[mouse-1] = data['mask'][14:14+36,:]
  44. responses_pred_gt[model_n-1,mouse,rep,spatial_n,temporal_n,0:num_neurons[mouse],:] = data['responses_predicted_gt']
  45. responses_pred_recon[model_n-1,mouse-1,rep,spatial_n,temporal_n,0:num_neurons[mouse],:] = data['responses_predicted_full']
  46. # add phase inverted stimuli as additional reps
  47. datapath=f'../reconstructions/modelfold{model_n}_round5_Gausnoise_PhaseInverted/reconstruction_summary_m{mouse}_r{reps[rep]}s{spatial_n}t{temporal_n}.npy'
  48. print(datapath)
  49. data = np.load(datapath, allow_pickle=True).item()
  50. if model_n==model_list[0]:
  51. video_gt[mouse,rep+len(reps),spatial_n,temporal_n] = data['video_gt']
  52. video_pred[model_n-1,mouse,rep+len(reps),spatial_n,temporal_n] = data['video_pred']
  53. mask[mouse-1] = data['mask'][14:14+36,:]
  54. responses_pred_gt[model_n-1,mouse,rep+len(reps),spatial_n,temporal_n,0:num_neurons[mouse],:] = data['responses_predicted_gt']
  55. responses_pred_recon[model_n-1,mouse-1,rep+len(reps),spatial_n,temporal_n,0:num_neurons[mouse],:] = data['responses_predicted_full']
  56. reps = np.array([0,1,2,3,4,5,6,7,8,9])# range(0,5)
  57. # remove eval grace period
  58. video_gt = video_gt[:,:,:,:,eval_frame_skip:,:,:]
  59. video_pred = video_pred[:,:,:,:,:,eval_frame_skip:,:,:]
  60. responses_pred_gt = responses_pred_gt[:,:,:,:,:,:,eval_frame_skip:]
  61. responses_pred_recon = responses_pred_recon[:,:,:,:,:,:,eval_frame_skip:]
  62. print('video_gt: ', video_gt.shape, video_gt.min(), video_gt.max())
  63. print('video_pred: ', video_pred.shape, video_pred.min(), video_pred.max())
  64. print('mask: ', mask.shape, mask.min(), mask.max())
  65. video_pred = np.clip(video_pred, 0, 255) # not sure i need this... but just in case
  66. video_pred_Amean_all = np.nanmean(video_pred, axis=0) # mean across models, no smoothing
  67. # %%
  68. # correlation between video_gt and video_pred for each rep, spatial and temporal frequency
  69. print('video_gt: ', video_gt.shape, video_gt.min(), video_gt.max())
  70. print('video_pred_Amean_all: ', video_pred_Amean_all.shape, video_pred_Amean_all.min(), video_pred_Amean_all.max())
  71. all_corr = np.zeros((len(mice),len(reps),len(spatial_length_constant),len(temporal_length_constant)))
  72. all_corr_randvid = np.zeros((len(mice),len(reps),len(spatial_length_constant),len(temporal_length_constant),len(reps)-1))
  73. for mouse in mice:
  74. for rep in range(0,len(reps)):
  75. for spatial_n in spatial_length_constant:
  76. for temporal_n in temporal_length_constant:
  77. all_corr[mouse-1,rep,spatial_n,temporal_n] = imsim.reconstruction_video_corr(video_gt[mouse-1,rep,spatial_n,temporal_n],
  78. video_pred_Amean_all[mouse-1,rep,spatial_n,temporal_n],
  79. np.where(mask[mouse-1] >= mask_th,1,0))
  80. i = -1
  81. for randrep in range(0,len(reps)):
  82. if randrep != rep:
  83. i+=1
  84. all_corr_randvid[mouse-1,rep,spatial_n,temporal_n,i] = imsim.reconstruction_video_corr(video_gt[mouse-1,randrep,spatial_n,temporal_n],
  85. video_pred_Amean_all[mouse-1,rep,spatial_n,temporal_n],
  86. np.where(mask[mouse-1] >= mask_th,1,0))
  87. all_corr_randvid = np.nanmean(all_corr_randvid,axis=-1)
  88. # all corr across mice and reps
  89. all_corr = all_corr.reshape((len(mice)*len(reps),len(spatial_length_constant),len(temporal_length_constant)))
  90. all_corr_randvid = all_corr_randvid.reshape((len(mice)*(len(reps)),len(spatial_length_constant),len(temporal_length_constant)))
  91. # mean corr across mice and reps
  92. mean_corr_ensembled = np.nanmean(all_corr, axis=(0))
  93. mean_corr_randvid_ensembled = np.nanmean(all_corr_randvid, axis=(0))
  94. print('mean_corr: ', mean_corr_ensembled.shape)
  95. all_mean_corr = np.zeros((len(model_list),len(mice),len(reps),len(spatial_length_constant),len(temporal_length_constant)))
  96. all_mean_corr_randvid = np.zeros((len(model_list),len(mice),len(reps),len(spatial_length_constant),len(temporal_length_constant),len(reps)-1))
  97. for model_n in model_list:
  98. for mouse in mice:
  99. for rep in range(0,len(reps)):
  100. for spatial_n in spatial_length_constant:
  101. for temporal_n in temporal_length_constant:
  102. all_mean_corr[model_n-1,mouse-1,rep,spatial_n,temporal_n] = imsim.reconstruction_video_corr(video_gt[mouse-1,rep,spatial_n,temporal_n],
  103. video_pred[model_n-1,mouse-1,rep,spatial_n,temporal_n],
  104. np.where(mask[mouse-1] >= mask_th,1,0))
  105. i = -1
  106. for randrep in range(0,len(reps)):
  107. if randrep != rep:
  108. i+=1
  109. all_mean_corr_randvid[model_n-1,mouse-1,rep,spatial_n,temporal_n,i] = imsim.reconstruction_video_corr(video_gt[mouse-1,randrep,spatial_n,temporal_n],
  110. video_pred[model_n-1,mouse-1,rep,spatial_n,temporal_n],
  111. np.where(mask[mouse-1] >= mask_th,1,0))
  112. all_mean_corr_randvid = np.nanmean(all_mean_corr_randvid,axis=-1)
  113. # all corr across mice and reps
  114. all_mean_corr = np.moveaxis(np.moveaxis(all_mean_corr,(1,2),(0,1)).reshape((len(mice)*len(reps),len(model_list),len(spatial_length_constant),len(temporal_length_constant))),(0),(1))
  115. all_mean_corr_randvid = np.moveaxis(np.moveaxis(all_mean_corr_randvid,(1,2),(0,1)).reshape((len(mice)*len(reps),len(model_list),len(spatial_length_constant),len(temporal_length_constant))),(0),(1))
  116. all_mean_corr = np.nanmean(all_mean_corr, axis=(0))
  117. all_mean_corr_randvid = np.nanmean(all_mean_corr_randvid, axis=(0))
  118. mean_corr = np.nanmean(all_mean_corr, axis=(0))
  119. # %%
  120. ## stats
  121. # ttest between all_corr and all_corr_randvid at every spatial and temporal frequency
  122. pass_corr_essembled = np.zeros((len(spatial_length_constant),len(temporal_length_constant)))
  123. for spatial_n in spatial_length_constant:
  124. for temporal_n in temporal_length_constant:
  125. pass_corr_essembled[spatial_n,temporal_n] = sp.stats.ttest_rel(all_corr[:,spatial_n,temporal_n],all_corr_randvid[:,spatial_n,temporal_n])[1]
  126. print('pass_corr_essembled: ', pass_corr_essembled.shape)
  127. print(pass_corr_essembled<0.001)
  128. # test if difference is significant
  129. # ttest between all_corr and all_corr_randvid at every spatial and temporal frequency
  130. pass_corr_essembled = np.zeros((len(spatial_length_constant),len(temporal_length_constant)))
  131. for spatial_n in spatial_length_constant:
  132. for temporal_n in temporal_length_constant:
  133. pass_corr_essembled[spatial_n,temporal_n] = sp.stats.ttest_rel(all_corr[:,spatial_n,temporal_n],all_mean_corr[:,spatial_n,temporal_n])[1]
  134. print('pass_corr_essemble_improvement: ', pass_corr_essembled.shape)
  135. print(pass_corr_essembled<0.001)
  136. # %%
  137. # contrast adjust images
  138. # for each movie, take the mean image across all time points, then calculate the mean and std of that image
  139. # normalize the predicted images by subtracting the mean and dividing by the std
  140. mouse_n,trial_n,slc,tlc,frame_n, h_n, w_n = video_gt.shape[0],video_gt.shape[1],video_gt.shape[2],video_gt.shape[3],video_gt.shape[4],video_gt.shape[5],video_gt.shape[6]
  141. expanded_mask = np.expand_dims(np.where(mask>= mask_th,1,np.nan) ,axis=(1,2,3,4)).repeat(trial_n,axis=-6).repeat(slc,axis=-5) .repeat(tlc,axis=-4).repeat(frame_n,axis=-3)
  142. # get target mean and std
  143. video_gt_frame_mean = video_gt * expanded_mask
  144. target_means = np.nanmean(video_gt_frame_mean, axis=(-1,-2,-3))
  145. target_stds = np.nanstd(video_gt_frame_mean, axis=(-1,-2,-3))
  146. # get start mean and std
  147. video_pred_frame_mean = video_pred_Amean_all * expanded_mask
  148. start_means = np.nanmean(video_pred_frame_mean, axis=(-1,-2,-3))
  149. start_stds = np.nanstd(video_pred_frame_mean, axis=(-1,-2,-3))
  150. # expand dims of mean and std in time h and w
  151. target_means = np.expand_dims(target_means,axis=(4,5,6)).repeat(frame_n,axis=-3).repeat(h_n,axis=-2).repeat(w_n,axis=-1)
  152. target_stds = np.expand_dims(target_stds,axis=(4,5,6)).repeat(frame_n,axis=-3).repeat(h_n,axis=-2).repeat(w_n,axis=-1)
  153. start_means = np.expand_dims(start_means,axis=(4,5,6)).repeat(frame_n,axis=-3).repeat(h_n,axis=-2).repeat(w_n,axis=-1)
  154. start_stds = np.expand_dims(start_stds,axis=(4,5,6)).repeat(frame_n,axis=-3).repeat(h_n,axis=-2).repeat(w_n,axis=-1)
  155. # # z-score predicted video
  156. video_pred_zscore = (video_pred_Amean_all - start_means) / start_stds
  157. video_pred_norm = (video_pred_zscore*target_stds) + target_means
  158. video_pred_norm = np.clip(video_pred_norm, 0, 255)
  159. # reaply mask
  160. video_gt_masked = video_gt * expanded_mask#np.expand_dims(expanded_mask ,axis=(-3)).repeat(frame_n,axis=-3)
  161. video_pred_Amean_all_masked = video_pred_Amean_all * expanded_mask#p.expand_dims(expanded_mask ,axis=(-3)).repeat(frame_n,axis=-3)
  162. video_pred_norm_masked = video_pred_norm * expanded_mask#np.expand_dims(expanded_mask ,axis=(-3)).repeat(frame_n,axis=-3)
  163. video_gt_masked[np.isnan(video_gt_masked)] = 255/2
  164. video_pred_Amean_all_masked[np.isnan(video_pred_Amean_all_masked)] = 255/2
  165. video_pred_norm_masked[np.isnan(video_pred_norm_masked)] = 255/2
  166. # %%
  167. fig, axs = plt.subplots(3,2, figsize=(15,15))
  168. axs[1,0].imshow(mean_corr_ensembled, cmap='viridis', vmin=0, vmax=1)
  169. axs[1,0].invert_yaxis()
  170. axs[1,0].set_xticks(np.arange(7), np.array([0,1,2,4,8,16,32]).__mul__(1/30).round(2).astype(str))
  171. axs[1,0].set_yticks(np.arange(7), np.array([0,1,2,4,8,16,32]).__mul__(1/3.4).round(2).astype(str))
  172. axs[1,0].set_xlabel('Temporal [sec]')
  173. axs[1,0].set_ylabel('Spatial [deg]')
  174. #set axis square
  175. axs[1,0].set_box_aspect(1)
  176. axs[1,0].set_title('reconstruction correlation')
  177. axs[1,0].tick_params(bottom=True, top=True, left=True, right=True)
  178. for i in range(7):
  179. for j in range(7):
  180. c = mean_corr_ensembled[j,i]
  181. axs[1,0].text(i, j, str(round(c, 2)),
  182. va='center', ha='center')
  183. fig.colorbar(axs[1,1].imshow(mean_corr_ensembled-mean_corr, cmap='viridis', vmin=0, vmax=0.25),ax=axs)
  184. axs[1,1].invert_yaxis()
  185. axs[1,1].set_xticks(np.arange(7), np.array([0,1,2,4,8,16,32]).__mul__(1/30).round(2).astype(str))
  186. axs[1,1].set_yticks(np.arange(7), np.array([0,1,2,4,8,16,32]).__mul__(1/3.4).round(2).astype(str))
  187. axs[1,1].set_xlabel('Temporal [sec]')
  188. axs[1,1].set_ylabel('Spatial [deg]')
  189. #set axis square
  190. axs[1,1].set_box_aspect(1)
  191. axs[1,1].set_title('ensembling improvement')
  192. axs[1,1].tick_params(bottom=True, top=True, left=True, right=True)
  193. for i in range(7):
  194. for j in range(7):
  195. c = mean_corr_ensembled[j,i]-mean_corr[j,i]
  196. axs[1,1].text(i, j, str(round(c, 2)),
  197. va='center', ha='center')
  198. # now relative to pixels and frames
  199. axs[2,0].imshow(mean_corr_ensembled, cmap='viridis', vmin=0, vmax=1)
  200. axs[2,0].invert_yaxis()
  201. axs[2,0].set_xticks(np.arange(7), ['0','1','2','4','8','16','32'])
  202. axs[2,0].set_yticks(np.arange(7), ['0','1','2','4','8','16','32'])
  203. axs[2,0].set_xlabel('Temporal [frames]')
  204. axs[2,0].set_ylabel('Spatial [pix]')
  205. #set axis square
  206. axs[2,0].set_box_aspect(1)
  207. axs[2,0].tick_params(bottom=True, top=True, left=True, right=True)
  208. for i in range(7):
  209. for j in range(7):
  210. c = mean_corr_ensembled[j,i]
  211. axs[2,0].text(i, j, str(round(c, 2)),
  212. va='center', ha='center')
  213. axs[2,1].imshow(mean_corr_ensembled-mean_corr, cmap='viridis', vmin=0, vmax=0.25)
  214. axs[2,1].invert_yaxis()
  215. axs[2,1].set_xticks(np.arange(7), ['0','1','2','4','8','16','32'])
  216. axs[2,1].set_yticks(np.arange(7), ['0','1','2','4','8','16','32'])
  217. axs[2,1].set_xlabel('Temporal [frames]')
  218. axs[2,1].set_ylabel('Spatial [pix]')
  219. #set axis square
  220. axs[2,1].set_box_aspect(1)
  221. axs[2,1].tick_params(bottom=True, top=True, left=True, right=True)
  222. for i in range(7):
  223. for j in range(7):
  224. c = mean_corr_ensembled[j,i]-mean_corr[j,i]
  225. axs[2,1].text(i, j, str(round(c, 2)),
  226. va='center', ha='center')
  227. # apply crop to masks based
  228. mask_expanded = np.expand_dims(mask,axis=(1)).repeat(video_gt.shape[-3],axis=1)
  229. mask_all = np.where(np.sum(np.where(mask >= mask_th,1,0),axis=0) >= 1,1,0)
  230. mask_all_idx = np.where(mask_all == 1)
  231. h_min, h_max = np.min(mask_all_idx[0])-2, np.max(mask_all_idx[0])+2
  232. w_min, w_max = np.min(mask_all_idx[1])-2, np.max(mask_all_idx[1])+2
  233. h = h_max - h_min
  234. w = w_max - w_min
  235. # make square
  236. h_min, h_max = h_min - (w-h)//2, h_max + (w-h)//2
  237. h = h_max - h_min
  238. w = w_max - w_min
  239. print('h_min, h_max, w_min, w_max: ', h_min, h_max, w_min, w_max)
  240. print('h, w: ', h, w)
  241. vid_length = video_gt.shape[-3]
  242. videos_gt_tiled = video_gt[0,0,:,:,:,:,:]
  243. videos_gt_tiled = videos_gt_tiled[:,:,:,h_min:h_max,w_min:w_max]
  244. videos_gt_tiled = videos_gt_tiled[::-1,:,:,:,:] # flip spatial and temporal length constant orders
  245. videos_gt_tiled = np.moveaxis(np.moveaxis(videos_gt_tiled,-2,1).reshape((7*h,7,vid_length,w)),0,-2)
  246. videos_gt_tiled = np.moveaxis(np.moveaxis(videos_gt_tiled,-1,1).reshape((7*w,vid_length,7*h)),0,-1)
  247. videos_gt_tiled_masked = video_gt[0,0,:,:,:,:,:]
  248. videos_gt_tiled_masked = videos_gt_tiled_masked * np.where(mask_expanded[0,-1] >= mask_th,1,0) + (1-np.where(mask_expanded[0,-1] >= mask_th,1,0) )*255/2
  249. videos_gt_tiled_masked= videos_gt_tiled_masked[:,:,:,h_min:h_max,w_min:w_max]
  250. videos_gt_tiled_masked = videos_gt_tiled_masked[::-1,:,:,:,:] # flip spatial and temporal length constant orders
  251. videos_gt_tiled_masked = np.moveaxis(np.moveaxis(videos_gt_tiled_masked,-2,1).reshape((7*h,7,vid_length,w)),0,-2)
  252. videos_gt_tiled_masked = np.moveaxis(np.moveaxis(videos_gt_tiled_masked,-1,1).reshape((7*w,vid_length,7*h)),0,-1)
  253. videos_recon_tiled = video_pred_norm_masked[0,0,:,:,:,:,:]
  254. videos_recon_tiled = videos_recon_tiled * np.where(mask_expanded[0,-1] >= mask_th,1,0) + (1-np.where(mask_expanded[0,-1] >= mask_th,1,0))*255/2
  255. videos_recon_tiled = videos_recon_tiled[::-1,:,:,h_min:h_max,w_min:w_max]
  256. videos_recon_tiled = np.moveaxis(np.moveaxis(videos_recon_tiled,-2,1).reshape((7*h,7,vid_length,w)),0,-2)
  257. videos_recon_tiled = np.moveaxis(np.moveaxis(videos_recon_tiled,-1,1).reshape((7*w,vid_length,7*h)),0,-1)
  258. print('videos_gt_tiled: ', videos_gt_tiled.shape)
  259. axs[0,0].imshow(videos_gt_tiled_masked[-1], cmap='gray')
  260. axs[0,0].set_axis_off()
  261. axs[0,0].set_title('Ground truth')
  262. axs[0,1].imshow(videos_recon_tiled[-1], cmap='gray')
  263. axs[0,1].set_axis_off()
  264. axs[0,1].set_title('Reconstruction')
  265. fig.savefig('../reconstructions/Gaussian_noise_reconstruction.svg', format='svg', dpi=1200)
  266. # tiled video
  267. print('video_gt: ', video_gt.shape)
  268. videos_gt_tiled_masked = video_gt[0,:,:,:,:,:,:]
  269. videos_gt_tiled_masked = videos_gt_tiled_masked * np.where(mask_expanded[0,-1] >= mask_th,1,0) + (1-np.where(mask_expanded[0,-1] >= mask_th,1,0) )*255/2
  270. videos_gt_tiled_masked= videos_gt_tiled_masked[:,::-1,:,:,h_min:h_max,w_min:w_max]
  271. videos_recon_tiled = video_pred_norm_masked[0,:,:,:,:,:,:]
  272. videos_recon_tiled = videos_recon_tiled * np.where(mask_expanded[0,-1] >= mask_th,1,0) + (1-np.where(mask_expanded[0,-1] >= mask_th,1,0))*255/2
  273. videos_recon_tiled = videos_recon_tiled[:,::-1,:,:,h_min:h_max,w_min:w_max]
  274. videos_interleaved = np.concatenate((videos_gt_tiled_masked[:,None,:],videos_recon_tiled[:,None,:]), axis=1)
  275. videos_interleaved = np.moveaxis(np.moveaxis(videos_interleaved,-3,1).reshape((len(reps)*vid_length,2,7,7,h,w)),0,-3) # concatenate directions
  276. videos_interleaved = np.moveaxis(np.moveaxis(videos_interleaved,0,1).reshape((2*7,7,len(reps)*vid_length,h,w)),0,0) # tile gt and recon
  277. videos_interleaved = np.moveaxis(np.moveaxis(videos_interleaved,-2,1).reshape((2*7*h,7,len(reps)*vid_length,w)),0,-2) # tile sf
  278. videos_interleaved = np.moveaxis(np.moveaxis(videos_interleaved,-1,1).reshape((7*w,len(reps)*vid_length,7*h*2)),0,-1) # tile tf
  279. # save video as tiff
  280. tifffile.imwrite('../reconstructions/Gaussian_noise.tiff',
  281. videos_interleaved.astype('uint8'),
  282. imagej=True,
  283. metadata = {'unit': 'um','fps': 30.0,'axes': 'TYX',})
  284. # %%
  285. videos_gt_tiled_hyperstack = np.concatenate((videos_gt_tiled_masked[:,:,:,None],videos_recon_tiled[:,:,:,None]), axis=3).reshape(-1,2,30,30,31)
  286. videos_gt_tiled_hyperstack = np.moveaxis(videos_gt_tiled_hyperstack,(0,1,2,3,4),(1,2,0,3,4))
  287. print(videos_gt_tiled_hyperstack.shape)
  288. # # # save video as tiff
  289. tifffile.imwrite('../reconstructions/Gaussian_noise_hyperstack.tiff',
  290. videos_gt_tiled_hyperstack.astype('uint16'),
  291. imagej=True,
  292. metadata = {'unit': 'um','fps': 30.0,'axes': 'TZCYX',})
  293. # %% [markdown]
  294. # # Reconstructed entropy
  295. # %%
  296. # check this approach to calc entropy makes sense
  297. def video_entropy_in_space(video, mask):
  298. idx_inmask = mask.flatten() == 1
  299. img_entropy_all=[]
  300. for frame in range(video.shape[0]):
  301. video_frame = video[frame].flatten()
  302. video_frame = video_frame[idx_inmask]
  303. hist, _ = np.histogram(video_frame, bins=256//10, range=(0, 256), density=True)
  304. hist = hist[hist > 0]
  305. img_entropy = sp.stats.entropy(hist, base=2)
  306. img_entropy_all.append(img_entropy)
  307. img_entropy_all = np.array(img_entropy_all)
  308. return np.nanmean(img_entropy_all)#,corr_all
  309. def video_energy(video, mask):
  310. # ground_truth: (time, height, width)
  311. # mask: (height, width) or (time, height, width)
  312. # returns: (time, height, width)
  313. if len(mask.shape) == 3:
  314. mask = mask[0, :, :] # make 2D
  315. idx_inmask = mask.flatten() == 1
  316. video.swapaxes(0, -1) # height, width,time
  317. video = video.reshape(video.shape[0],-1) # flatten spatial dimensions
  318. video.swapaxes(-1, 0) # time, height*width
  319. video = video[:,idx_inmask] # mask spatial dimensions
  320. frame_energy = np.mean(np.abs(np.diff(video, axis=0)),axis=(1)) # sum of absolute differences (motion energy)
  321. return frame_energy
  322. # %%
  323. Entropy_in_space_gt = np.zeros((len(mice),len(reps),len(spatial_length_constant),len(temporal_length_constant)))
  324. # Entropy_in_timenspace_gt = np.zeros((len(mice),len(reps),len(spatial_length_constant),len(temporal_length_constant)))
  325. Motion_energy_gt = np.zeros((len(mice),len(reps),len(spatial_length_constant),len(temporal_length_constant)))
  326. Entropy_in_space_recon = np.zeros((len(mice),len(reps),len(spatial_length_constant),len(temporal_length_constant)))
  327. # Entropy_in_timenspace_recon = np.zeros((len(mice),len(reps),len(spatial_length_constant),len(temporal_length_constant)))
  328. Motion_energy_recon = np.zeros((len(mice),len(reps),len(spatial_length_constant),len(temporal_length_constant)))
  329. for mouse in mice:
  330. for rep in range(0,len(reps)):
  331. for spatial_n in spatial_length_constant:
  332. for temporal_n in temporal_length_constant:
  333. Entropy_in_space_gt[mouse,rep,spatial_n,temporal_n] = video_entropy_in_space(video_gt[mouse,rep,spatial_n,temporal_n,:,:,11:11+36],np.where(mask[mouse,:,11:11+36] >= 0,1,0))
  334. # Entropy_in_timenspace_gt[mouse,rep,spatial_n,temporal_n] = video_entropy_in_timenspace(video_gt[mouse,rep,spatial_n,temporal_n,:,:,11:11+36],np.where(mask[mouse,:,11:11+36] >= 0,1,0))
  335. Motion_energy_gt[mouse,rep,spatial_n,temporal_n] = video_energy(video_gt[mouse,rep,spatial_n,temporal_n,:,:,11:11+36],np.where(mask[mouse,:,11:11+36] >= 0,1,0)).mean()
  336. Entropy_in_space_recon[mouse,rep,spatial_n,temporal_n] = video_entropy_in_space(video_pred_norm_masked[mouse,rep,spatial_n,temporal_n],np.where(mask[mouse] >= mask_th,1,0))
  337. # Entropy_in_timenspace_recon[mouse,rep,spatial_n,temporal_n] = video_entropy_in_timenspace(video_pred_norm_masked[mouse,rep,spatial_n,temporal_n],np.where(mask[mouse] >= mask_th,1,0))
  338. Motion_energy_recon[mouse,rep,spatial_n,temporal_n] = video_energy(video_pred_norm_masked[mouse,rep,spatial_n,temporal_n],np.where(mask[mouse] >= mask_th,1,0)).mean()
  339. # %%
  340. fig, ax = plt.subplots(2,2, figsize=(10,10))
  341. ax[0,0].imshow(Entropy_in_space_gt.mean(axis=(0,1)), cmap='viridis', vmin=2.5, vmax=4.5)
  342. ax[0,0].invert_yaxis()
  343. ax[0,0].set_xticks(np.arange(7), ['0','1','2','4','8','16','32'])
  344. ax[0,0].set_yticks(np.arange(7), ['0','1','2','4','8','16','32'])
  345. ax[0,0].set_xlabel('Temporal [frames]')
  346. ax[0,0].set_ylabel('Spatial [pix]')
  347. #set axis square
  348. ax[0,0].set_box_aspect(1)
  349. ax[0,0].tick_params(bottom=True, top=True, left=True, right=True)
  350. for i in range(7):
  351. for j in range(7):
  352. c = Entropy_in_space_gt.mean(axis=(0,1))[j,i]
  353. ax[0,0].text(i, j, str(round(c, 2)),
  354. va='center', ha='center')
  355. ax[0,0].set_title('Shannon Entropy in space GT')
  356. ax[0,1].imshow(Motion_energy_gt.mean(axis=(0,1)), cmap='viridis', vmin=0, vmax=75)
  357. ax[0,1].invert_yaxis()
  358. ax[0,1].set_xticks(np.arange(7), ['0','1','2','4','8','16','32'])
  359. ax[0,1].set_yticks(np.arange(7), ['0','1','2','4','8','16','32'])
  360. ax[0,1].set_xlabel('Temporal [frames]')
  361. ax[0,1].set_ylabel('Spatial [pix]')
  362. #set axis square
  363. ax[0,1].set_box_aspect(1)
  364. ax[0,1].tick_params(bottom=True, top=True, left=True, right=True)
  365. for i in range(7):
  366. for j in range(7):
  367. c = Motion_energy_gt.mean(axis=(0,1))[j,i]
  368. ax[0,1].text(i, j, str(round(c, 2)),
  369. va='center', ha='center')
  370. ax[0,1].set_title('Motion energy GT')
  371. ax[1,0].imshow(Entropy_in_space_recon.mean(axis=(0,1)), cmap='viridis', vmin=2.5, vmax=4.5)
  372. ax[1,0].invert_yaxis()
  373. ax[1,0].set_xticks(np.arange(7), ['0','1','2','4','8','16','32'])
  374. ax[1,0].set_yticks(np.arange(7), ['0','1','2','4','8','16','32'])
  375. ax[1,0].set_xlabel('Temporal [frames]')
  376. ax[1,0].set_ylabel('Spatial [pix]')
  377. #se axis square
  378. ax[1,0].set_box_aspect(1)
  379. ax[1,0].tick_params(bottom=True, top=True, left=True, right=True)
  380. for i in range(7):
  381. for j in range(7):
  382. c = Entropy_in_space_recon.mean(axis=(0,1))[j,i]
  383. ax[1,0].text(i, j, str(round(c, 2)),
  384. va='center', ha='center')
  385. ax[1,0].set_title('Shannon Entropy in space Recon')
  386. ax[1,1].imshow(Motion_energy_recon.mean(axis=(0,1)), vmin=0, vmax=75)
  387. ax[1,1].invert_yaxis()
  388. ax[1,1].set_xticks(np.arange(7), ['0','1','2','4','8','16','32'])
  389. ax[1,1].set_yticks(np.arange(7), ['0','1','2','4','8','16','32'])
  390. ax[1,1].set_xlabel('Temporal [frames]')
  391. ax[1,1].set_ylabel('Spatial [pix]')
  392. #set axis square
  393. ax[1,1].set_box_aspect(1)
  394. ax[1,1].tick_params(bottom=True, top=True, left=True, right=True)
  395. for i in range(7):
  396. for j in range(7):
  397. c = Motion_energy_recon.mean(axis=(0,1))[j,i]
  398. ax[1,1].text(i, j, str(round(c, 2)),
  399. va='center', ha='center')
  400. ax[1,1].set_title('Motion energy recon')
  401. fig.savefig('../reconstructions/Gaussian_noise_entropy.svg', format='svg', dpi=1200)
  402. # %% [markdown]
  403. # # Phase inverted stimuli
  404. # %%
  405. all_corr_same_phase = np.zeros((len(mice),len(reps),len(spatial_length_constant),len(temporal_length_constant)))
  406. all_corr_opposite_phase = np.zeros((len(mice),len(reps),len(spatial_length_constant),len(temporal_length_constant)))
  407. all_corr_opposite_phase_recontorecon = np.zeros((len(mice),len(reps),len(spatial_length_constant),len(temporal_length_constant)))
  408. for mouse in mice:
  409. for rep in range(0,len(reps)):
  410. if rep > len(reps)//2-1: # phase 2
  411. rep_opposite_phase = rep - len(reps)//2
  412. elif rep <= len(reps)//2-1: # phase 1
  413. rep_opposite_phase = rep + len(reps)//2
  414. for spatial_n in spatial_length_constant:
  415. for temporal_n in temporal_length_constant:
  416. all_corr_same_phase[mouse-1,rep,spatial_n,temporal_n] = imsim.reconstruction_video_corr(video_gt[mouse-1,rep,spatial_n,temporal_n],
  417. video_pred_Amean_all[mouse-1,rep,spatial_n,temporal_n],
  418. np.where(mask[mouse-1] >= mask_th,1,0))
  419. all_corr_opposite_phase[mouse-1,rep,spatial_n,temporal_n] = imsim.reconstruction_video_corr(video_gt[mouse-1,rep_opposite_phase,spatial_n,temporal_n],
  420. video_pred_Amean_all[mouse-1,rep,spatial_n,temporal_n],
  421. np.where(mask[mouse-1] >= mask_th,1,0))
  422. all_corr_opposite_phase_recontorecon[mouse-1,rep,spatial_n,temporal_n] = imsim.reconstruction_video_corr(video_pred_Amean_all[mouse-1,rep_opposite_phase,spatial_n,temporal_n],
  423. video_pred_Amean_all[mouse-1,rep,spatial_n,temporal_n],
  424. np.where(mask[mouse-1] >= mask_th,1,0))
  425. # %%
  426. fig, ax = plt.subplots(2,2, figsize=(10,10))
  427. ax[0,0].imshow(all_corr_same_phase.mean(axis=(0,1)), cmap='RdYlGn', vmin=-1, vmax=1)
  428. ax[0,0].invert_yaxis()
  429. ax[0,0].set_xticks(np.arange(7), np.array([0,1,2,4,8,16,32]).__mul__(1/30).round(2).astype(str))
  430. ax[0,0].set_yticks(np.arange(7), np.array([0,1,2,4,8,16,32]).__mul__(1/3.4).round(2).astype(str))
  431. ax[0,0].set_xlabel('Temporal [sec]')
  432. ax[0,0].set_ylabel('Spatial [deg]')
  433. ax[0,0].set_title('corr between recon to same phase GT')
  434. ax[0,0].tick_params(bottom=True, top=True, left=True, right=True)
  435. for i in range(7):
  436. for j in range(7):
  437. c = all_corr_same_phase.mean(axis=(0,1))[j,i]
  438. ax[0,0].text(i, j, str(round(c, 2)),
  439. va='center', ha='center')
  440. ax[0,1].imshow(all_corr_opposite_phase.mean(axis=(0,1)), cmap='RdYlGn', vmin=-1, vmax=1)
  441. ax[0,1].invert_yaxis()
  442. ax[0,1].set_xticks(np.arange(7), np.array([0,1,2,4,8,16,32]).__mul__(1/30).round(2).astype(str))
  443. ax[0,1].set_yticks(np.arange(7), np.array([0,1,2,4,8,16,32]).__mul__(1/3.4).round(2).astype(str))
  444. ax[0,1].set_xlabel('Temporal [sec]')
  445. ax[0,1].set_ylabel('Spatial [deg]')
  446. ax[0,1].set_title('corr between recon to opposite phase GT')
  447. ax[0,1].tick_params(bottom=True, top=True, left=True, right=True)
  448. for i in range(7):
  449. for j in range(7):
  450. c = all_corr_opposite_phase.mean(axis=(0,1))[j,i]
  451. ax[0,1].text(i, j, str(round(c, 2)),
  452. va='center', ha='center')
  453. ax[1,0].imshow(all_corr_opposite_phase_recontorecon.mean(axis=(0,1)), cmap='RdYlGn', vmin=-1, vmax=1)
  454. ax[1,0].invert_yaxis()
  455. ax[1,0].set_xticks(np.arange(7), np.array([0,1,2,4,8,16,32]).__mul__(1/30).round(2).astype(str))
  456. ax[1,0].set_yticks(np.arange(7), np.array([0,1,2,4,8,16,32]).__mul__(1/3.4).round(2).astype(str))
  457. ax[1,0].set_xlabel('Temporal [sec]')
  458. ax[1,0].set_ylabel('Spatial [deg]')
  459. ax[1,0].set_title('corr between recon to recon of opposite phase GT')
  460. ax[1,0].tick_params(bottom=True, top=True, left=True, right=True)
  461. for i in range(7):
  462. for j in range(7):
  463. c = all_corr_opposite_phase_recontorecon.mean(axis=(0,1))[j,i]
  464. ax[1,0].text(i, j, str(round(c, 2)),
  465. va='center', ha='center')
  466. cbar1 = plt.colorbar(ax[1,0].images[0], ax=ax[1,0], orientation='vertical')
  467. fig.savefig('../reconstructions/Gaussian_noise_PhaseInversion.svg', format='svg', dpi=1200)

analyse_reconstructions_gaussian_noise.ipynb at commit eb5ad21, under MIT · at the source

Overview

  1. Sainsbury Wellcome Centre, University College London London United Kingdom
  2. Bioengineering Dept., Imperial College London United Kingdom
Institutions: Sainsbury Wellcome Centre (United Kingdom); Imperial College London (United Kingdom)
Journal: eLife, volume 14, article RP105081
Dates: published online 10 March 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.7554/elife.105081 · PMID 41804097 · PMCID PMC12975128 · OpenAlex W4408807840
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: optical imaging (calcium, voltage, 2-photon) (modality), mouse (organism), systems (subfield)
Methods: Connectivity, Machine learning, Physiology & signal measures
Keywords: vision, visual cortex, sensory reconstruction, deep learning, perception, calcium imaging, Mouse
MeSH: Image Processing, Computer-Assisted*, Neurons*, Visual Cortex*, Animals, Mice, Photic Stimulation (* major topic)
Journal subjects: Neuroscience
Topic: Neural dynamics and brain function (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Funding: Wellcome Trust (318818/Z/24/Z, 10.35802/306384, 10.35802/200790); Gatsby Charitable Foundation (GAT4057); European Molecular Biology Organization (ALTF 415-2024); Simons Foundation (564408); Engineering and Physical Sciences Research Council (EP/R035806/1); European Research Council (MotorAdapt 101169605)
Citations: cited by 2 papers (Europe PMC); 66 references in the paper

Abstract

The ability to reconstruct images represented by the brain has the potential to give us an intuitive understanding of what the brain sees. Reconstruction of visual input from human fMRI data has garnered significant attention in recent years. Comparatively less focus has been directed towards vision reconstruction from single-cell recordings, despite its potential to provide a more direct measure of the information represented by the brain. Here, we achieve high-quality reconstructions of natural movies presented to mice, from the activity of neurons in their visual cortex for the first time. Using our method of video optimization via backpropagation through a state-of-the-art dynamic neural encoding model, we reliably reconstruct 10 s movies at 30 Hz from two-photon calcium imaging data. We achieve a pixel-level correlation of 0.57 between ground-truth movies and single-trial reconstructions. Previous reconstructions based on awake mouse V1 neuronal responses to static images achieved a pixel-level correlation of 0.24 over a similar retinotopic area. We find that critical for high-quality reconstructions are the number of neurons in the dataset and the use of model ensembling. This paves the way for movie reconstruction to be used as a tool to investigate a variety of visual processing phenomena.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repositories

Its files are read in the Code ↔ Paper reader above, with 12 matches between paragraphs and lines of code.

lRomul/sensorium

License: MIT
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Commit: 6849050e74e2843fbd70b33af110d71638aa37bb, 22 November 2023
Languages: Python (24)
Size: 39 files, 24 scripts
Software Heritage: not archived
Found in: the text, “State-of-the-art dynamic neural encoding model”
Holds: README, license file, CITATION.cff, environment (Dockerfile, requirements.txt, setup.cfg)
Not found: tests, continuous integration, documentation
Tools: NumPy (13 files), PyTorch (12 files), pandas (1 file), Pillow (1 file)
Availability: 1 check, the latest on 30 September 2026: the link answers
  • 30 September 2026: the link answers
26 files

Joel-Bauer/movie_reconstruction_code

License: MIT
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Commit: eb5ad2143b5d54aad4ca44cb26b919cfab987920, 9 January 2026
Languages: Python (11), Jupyter (5)
Size: 30 files, 16 scripts
Software Heritage: not archived
Found in: “Data availability”
Holds: README, license file, 5 notebooks
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (13 files), Matplotlib (12 files), PyTorch (8 files), tifffile (6 files), SciPy (5 files), OpenCV (2 files)
Availability: 1 check, the latest on 30 September 2026: the link answers
  • 30 September 2026: the link answers
18 files

The paper's code and data availability statement is in the Data section.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 2 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 40 scripts, each with its path and the digest of its content;
  • 12 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

Datasets cited

Data availability

The code is available at https://github.com/Joel-Bauer/movie_reconstruction_code (copy archived at Bauer, 2025).

The following previously published datasets were used:

FaheyP TurishchevaP HanselL FroebeR PonderK VystrcilováM QiuY WillekeK BashiriM ToliasA SinzA EckerA 2023The Dynamic Sensorium competition for predicting large-scale mouse visual cortex activity from videos - DatasetG-Node GinSensorium2023Data

FaheyP TurishchevaP HanselL FroebeR PonderK VystrcilováM QiuY WillekeK BashiriM ToliasA SinzA EckerA 2023The Dynamic Sensorium competition for predicting large-scale mouse visual cortex activity from videos - DatasetG-Node Ginsensorium_2023_data

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 30 September 2026: the first record

Recorded: type, language, journal, volume, pages, dates, 3 authors, 7 keywords, 6 MeSH terms, 6 funders, 62 references.

Cite

This paper

Bauer, J., Margrie, T. W., & Clopath, C. (2026). Movie reconstruction from mouse visual cortex activity. eLife, 14, RP105081. https://doi.org/10.7554/elife.105081

BibTeX

@article{bauer2026movie,
author = {Bauer, Joel and Margrie, Troy W and Clopath, Claudia},
title = {{Movie reconstruction from mouse visual cortex activity}},
journal = {eLife},
year = {2026},
month = mar,
volume = {14},
pages = {RP105081},
publisher = {eLife Sciences Publications, Ltd},
issn = {2050-084X},
doi = {10.7554/elife.105081},
url = {https://doi.org/10.7554/elife.105081},
pmid = {41804097},
pmcid = {PMC12975128}
}

RIS

TY - JOUR
AU - Bauer, Joel
AU - Margrie, Troy W
AU - Clopath, Claudia
TI - Movie reconstruction from mouse visual cortex activity
T2 - eLife
J2 - Elife
PY - 2026
DA - 2026/03/10
VL - 14
SP - RP105081
SN - 2050-084X
PB - eLife Sciences Publications, Ltd
DO - 10.7554/elife.105081
UR - https://doi.org/10.7554/elife.105081
LA - en
ER -

CSL-JSON

{
"id": "10.7554/elife.105081",
"type": "article-journal",
"title": "Movie reconstruction from mouse visual cortex activity",
"container-title": "eLife",
"author": [
{
"family": "Bauer",
"given": "Joel"
},
{
"family": "Margrie",
"given": "Troy W"
},
{
"family": "Clopath",
"given": "Claudia"
}
],
"container-title-short": "Elife",
"volume": "14",
"page": "RP105081",
"DOI": "10.7554/elife.105081",
"PMID": "41804097",
"PMCID": "PMC12975128",
"ISSN": "2050-084X",
"publisher": "eLife Sciences Publications, Ltd",
"URL": "https://doi.org/10.7554/elife.105081",
"language": "en",
"issued": {
"date-parts": [
[
2026,
3,
10
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1002/advs.202520220 [code]
Learnable Diffusion Framework for Mouse V1 Neural Decoding.
Journal: Advanced science (Weinheim, Baden-Wurttemberg, Germany)
In common: OpenCV, Pillow, PyTorch, 4 other tools, systems, mouse, 6 references
[2] doi:10.1371/journal.pcbi.1014263 [code]
MIRAGE: Robust multi-modal architectures translate fMRI-to-image models from vision to mental imagery.
Journal: PLoS computational biology
In common: OpenCV, Pillow, PyTorch, 4 other tools, 5 references
[3] doi:10.7554/elife.107933 [code]
Modality-agnostic decoding of vision and language from fMRI.
Journal: eLife
In common: OpenCV, Pillow, PyTorch, 4 other tools, 5 references
[4] doi:10.1016/j.isci.2026.116206 [code]
Gut distension evokes rapid neural dynamics in vagal and hindbrain populations of larval zebrafish.
Journal: iScience
In common: tifffile, OpenCV, Pillow, 5 other tools, optical imaging (calcium, voltage, 2-photon), systems, 2 references
[5] doi:10.1016/j.patter.2026.101538 [code]
A multi-modal foundation model for brain disease diagnosis and medical imaging.
Journal: Patterns (New York, N.Y.)
In common: tifffile, OpenCV, Pillow, 5 other tools, 2 references
[6] doi:10.1007/s12021-026-09803-3 [code]
NeuroFusion: A Unified Framework for Generalized Visual Stimulus Decoding from fMRI Across Datasets and Subjects.
Journal: Neuroinformatics
In common: Pillow, PyTorch, pandas, 3 other tools, 4 references
[7] doi:10.1523/eneuro.0459-25.2026 [code]
Open-Source Platform for Adjustable Training Regimes in Freely Moving and Head-Fixed Mice.
Journal: eNeuro
In common: OpenCV, Pillow, PyTorch, 2 other tools, optical imaging (calcium, voltage, 2-photon), mouse, 3 references
[8] doi:10.1016/j.crmeth.2026.101476 [code]
Unsupervised deep learning enables blur-free resolution enhancement in two-photon microscopy.
Journal: Cell reports methods
In common: tifffile, OpenCV, PyTorch, 4 other tools, optical imaging (calcium, voltage, 2-photon), mouse, 2 references
[9] doi:10.1364/boe.600665 [code]
NeuroSeg-MF: robust neuron segmentation in two-photon Ca&lt;sup&gt;2+&lt;/sup&gt; imaging using multi-feature fusion and detection-guided SAM.
Journal: Biomedical optics express
In common: tifffile, OpenCV, Pillow, 5 other tools, optical imaging (calcium, voltage, 2-photon), 1 reference
[10] doi:10.1111/ejn.70582 [code]
Multifiber Array-Based Photometry System for Multiregional Functional Mapping in the Mouse Brain.
Journal: The European journal of neuroscience
In common: tifffile, OpenCV, Pillow, 4 other tools, optical imaging (calcium, voltage, 2-photon), systems, mouse, 1 reference

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.