OSCR

Protocol for simultaneous in vivo two-photon imaging and locomotion quantification during olfactory stimulation and pharmacology in walking Drosophila.

Code ↔ Paper

2 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 2 matches
  1. [1] § Step-by-step method details › Data analysis ↔ cia_utils.py, lines 743–848 · score 0.67 · ball radius, fictrac dat, frame rate, mm
  2. [2] § Step-by-step method details › Data analysis ↔ main.py, lines 124–184 · score 0.53 · ball radius, frame rate, dat, fictrac

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

Python · 1,617 lines · 61 KB · no license · 1 match

  1. # -*- coding: utf-8 -*-
  2. #-*- coding : utf-8-*-
  3. # coding:unicode_escape
  4. import os
  5. import cv2
  6. import json
  7. import pandas as pd
  8. import numpy as np
  9. import matplotlib.pyplot as plt
  10. import matplotlib.patches as patches
  11. from matplotlib.colors import NoNorm
  12. from glob import glob
  13. from scipy.stats import circmean
  14. from cia_consts import *
  15. from cia_photodiode import get_stim_range, get_all_sti_pos, find_nearest
  16. import imageio
  17. from matplotlib.animation import FuncAnimation
  18. COLORS = ["k", "r", "g", "b", "y", "c", "m", "gray", "pink", "springgreen", "deepskyblue", "yellow",]
  19. def load_memmap(filename, mode='r'):
  20. file_to_load = filename
  21. filename = os.path.split(filename)[-1]
  22. fpart = filename.split('_')[1:-1] # The filename encodes the structure of the map
  23. d1, d2, d3, T, order = int(fpart[-9]), int(fpart[-7]), int(fpart[-5]), int(fpart[-1]), fpart[-3]
  24. Yr = np.memmap(file_to_load, mode=mode, shape=(d1 * d2 * d3, T), dtype=np.float32, order=order)
  25. if d3 == 1:
  26. dims = (d1, d2)
  27. else:
  28. dims = (d1, d2, d3)
  29. return np.reshape(Yr.T, [T] + list(dims), order='F')
  30. def calc_avg_frame(m, parent):
  31. avg_frame = np.mean(m, axis=0)
  32. eq = norm_img(avg_frame)
  33. cv2.imwrite(parent + "/i_avg.png", eq)
  34. std_frame = np.std(m, axis=0)
  35. eq2 = norm_img(std_frame)
  36. cv2.imwrite(parent + "/i_std.png", eq2)
  37. max_frame = np.max(m, axis=0)
  38. eq3 = norm_img(max_frame)
  39. cv2.imwrite(parent + "/i_max.png", eq3)
  40. eqc = np.transpose([eq3, eq2, eq], (1, 2, 0))
  41. cv2.imwrite(parent + "/i_all.png", eqc)
  42. # cluster_roi(m, parent)
  43. # show_cluster_roi(m, parent)
  44. # th2, res = cv2.threshold(eq3.astype(np.uint8), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
  45. # cv2.imwrite(parent + "/i_test.png", res)
  46. def cluster_roi(m, parent):
  47. eq3 = norm_img(np.max(m, axis=0))*2 # NOTE: m: 1400*128*128
  48. th2, img = cv2.threshold(eq3.astype(np.uint8), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
  49. #img = cv2.morphologyEx(img, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_RECT, (1, 1)))
  50. p_l = np.nonzero(img)
  51. n = len(p_l[0])
  52. cluster_m = np.zeros(img.shape, dtype=np.uint8)
  53. # for i in range(n):
  54. # print(i)
  55. # for j in range(n):
  56. # cor_map[i, j] = cor(m[:, p_l[0][i], p_l[1][i]], m[:, p_l[0][j], p_l[1][j]])
  57. X = m[:, p_l[0], p_l[1]].T # NOTE: n*1400
  58. from pyclustering.cluster.kmeans import kmeans
  59. from pyclustering.cluster.center_initializer import kmeans_plusplus_initializer
  60. from pyclustering.utils.metric import type_metric, distance_metric
  61. metric = distance_metric(type_metric.USER_DEFINED, func=lambda a, b: 1-cor(a, b))
  62. initial_centers = kmeans_plusplus_initializer(X, 10).initialize()
  63. kmeans_instance = kmeans(X, initial_centers, metric=metric)
  64. kmeans_instance.process()
  65. clusters = kmeans_instance.get_clusters()
  66. pred = np.zeros((X.shape[0]))
  67. cf = []
  68. for i, c in enumerate(clusters):
  69. pred[c] = i + 1
  70. cf.extend(c)
  71. plot_fast_cor_map(X, cf)
  72. # plt.show()
  73. plt.savefig(parent + "/i_cor.png")
  74. # from sklearn.cluster import KMeans, DBSCAN
  75. # estimator = KMeans(n_clusters=20, max_iter=500)
  76. # estimator = DBSCAN(eps=0.8, min_samples=50, metric=lambda a, b: 1-cor(a, b))
  77. # estimator.fit(X)
  78. # pred = estimator.labels_
  79. cluster_m[p_l[0], p_l[1]] = pred
  80. cv2.imwrite(parent + "/i_test.png", norm_img(cluster_m))
  81. for i in range(int(np.max(pred))):
  82. show_largest_for_roi(m, cluster_m, i)
  83. plt.savefig(parent + "/i_largest_%d.png" % i)
  84. # plt.imshow(cluster_m)
  85. def plot_cor_map(X, cf):
  86. n = len(cf)
  87. cor_map = np.zeros((n, n))
  88. for i, cf1 in enumerate(cf):
  89. print(i, "/", n)
  90. for j, cf2 in enumerate(cf):
  91. cor_map[i][j] = cor(X[cf1], X[cf2])
  92. plt.imshow(cor_map, cmap="bwr")
  93. plt.colorbar()
  94. def plot_fast_cor_map(X, cf):
  95. from scipy.stats import zscore
  96. Xz = zscore(X, axis=1)
  97. n = len(cf)
  98. cor_map = np.zeros((n, n))
  99. for i, cf1 in enumerate(cf):
  100. print(i, "/", n)
  101. for j, cf2 in enumerate(cf):
  102. cor_map[i][j] = np.dot(Xz[cf1], Xz[cf2])/n
  103. plt.imshow(cor_map, cmap="bwr")
  104. plt.colorbar()
  105. def show_cluster_roi(m, parent):
  106. cluster_m = cv2.imread(parent + "/i_test.png")
  107. for k, i in enumerate(np.unique(cluster_m)):
  108. show_largest_for_roi(m, cluster_m, i)
  109. plt.savefig(parent + "/i_largest_%d.png" % k)
  110. def show_largest_for_roi(m, cluster_m, label):
  111. p_l = np.nonzero(cluster_m == label)
  112. X = m[:, p_l[0], p_l[1]]
  113. n = X.shape[1]
  114. # if n < 1600:
  115. # cor_map = np.zeros((n, n))
  116. # for i in range(n):
  117. # print(i, "/", n)
  118. # for j in range(n):
  119. # cor_map[i][j] = cor(X[:, i], X[:, j])
  120. # plt.imshow(cor_map)
  121. # plt.colorbar()
  122. # plt.show()
  123. f = np.argmax(np.sum(X, axis=1))
  124. plt.figure(figsize=(6, 3))
  125. plt.title(str(f))
  126. fig, axs = plt.subplots(1, 2)
  127. axs[0].imshow(m[f])
  128. axs[0].scatter(p_l[1], p_l[0], alpha=1, color="r", s=1)
  129. axs[1].imshow(m[f])
  130. def norm_img(img):
  131. pmax = np.percentile(img, 99)
  132. pmin = np.percentile(img, 1)
  133. r_norm = 255.0 * (img - pmin) / (pmax - pmin)
  134. np.clip(r_norm, 0, 255, r_norm)
  135. return r_norm.astype(np.uint8)
  136. def load_roi(roi_file, shape):
  137. if roi_file.endswith(".zip"):
  138. return load_roi_zip(roi_file, shape)
  139. elif roi_file.endswith(".npy"):
  140. roi = np.load(roi_file, allow_pickle=True)
  141. names = np.arange(len(roi))
  142. xy = [r[:, 0, :] for r in roi]
  143. return names, roi_contours_to_points(xy, shape), xy
  144. def load_roi_zip(roizip, shape):
  145. from read_roi import read_roi_zip# read_roi_file
  146. rois = read_roi_zip(roizip)
  147. xy = []
  148. names = []
  149. for d in rois.values():
  150. if d["type"] == "polygon":
  151. xy.append(np.array(tuple(zip(d["x"], d["y"]))))
  152. names.append(d["name"])
  153. elif d["type"] == "rectangle":
  154. l, t, w, h = d["left"], d["top"], d["width"], d["height"]
  155. xy.append(np.array([[l, t], [l + w, t], [l + w, t + h], [l, t + h]]))
  156. names.append(d["name"])
  157. return names, roi_contours_to_points(xy, shape), xy
  158. def roi_contours_to_points(contours_xy, shape):
  159. xy = []
  160. for c in contours_xy:
  161. temp = np.zeros(shape)
  162. cv2.drawContours(temp, [c.astype(int)], 0, color=1, thickness=-1)
  163. xy.append(temp.nonzero())
  164. # plt.imshow(temp, cmap="Greys_r")
  165. # plt.show()
  166. return xy
  167. def calc_all_roi_F(roizip, m, parent):
  168. print("Do calc F...")
  169. names, points, contours = load_roi(roizip, m[0].shape)
  170. ret = [[] for i in range(len(names))]
  171. for i, mi in enumerate(m):
  172. for j, xyj in enumerate(points):
  173. ret[j].append(float(mi[xyj].mean()))
  174. # plot_lines(ret, names)
  175. plot_rois(contours, m)
  176. plt.savefig(parent + "/roi.png")
  177. F = pd.DataFrame(np.array(ret).T, columns=names)
  178. F.to_csv(parent + "/F.csv", index=False)
  179. plot_lines([F[n] for n in names], names)
  180. plt.savefig(parent + "/F.png")
  181. zs_csv = parent + "/zscore.csv"
  182. zs = get_zscore(F)
  183. zs.to_csv(zs_csv, index=False)
  184. write_roi_dFF_video(zs, contours, m, zs_csv)
  185. dFF_csv = parent + "/dFF.csv"
  186. dFF = get_dFF(F)
  187. dFF.to_csv(dFF_csv, index=False)
  188. write_roi_dFF_video(dFF, contours, m, dFF_csv)
  189. print(dFF_csv)
  190. return dFF_csv, zs_csv
  191. def write_roi_dFF_video(dFF, contours, m, name):
  192. return
  193. # fig = plt.figure(figsize=(6, 6))
  194. # ax = plt.gca()
  195. # ax.set_position([0, 0, 1, 1], which="both")
  196. # fig.canvas.draw()
  197. # w, h = fig.canvas.get_width_height()
  198. if os.path.exists(name + "_roi.avi"):
  199. return
  200. h, w = m[0].shape
  201. output_video = cv2.VideoWriter(name + "_roi.avi", cv2.VideoWriter_fourcc(*"DIVX"), 30, (w, h))
  202. cmap = plt.cm.get_cmap("Greys_r")
  203. dFFn = norm_img(np.array(dFF))
  204. for i, mi in enumerate(m):
  205. print(i)
  206. # ax.cla()
  207. # ax.imshow(mi, cmap="Greys_r")
  208. # for j, xys in enumerate(contours):
  209. # ax.add_patch(plt.Polygon(xys, linewidth=0, color=cmap(dFFn.loc[i, j])))
  210. # write_fig_to_video(output_video, fig, w, h)
  211. img = np.zeros(mi.shape, dtype=np.uint8)#np.array(mi, dtype=np.uint8)
  212. for j, xys in enumerate(contours):
  213. cv2.drawContours(img, [contours[j].astype(int)], 0, color=cmap(dFFn[i, j])[0]*255, thickness=-1)
  214. img_bgr = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
  215. output_video.write(img_bgr)
  216. output_video.release()
  217. def write_zscore_video(m, name, pv_idx=None, contours=None):
  218. # n, h, w = m.shape
  219. cmap = plt.cm.get_cmap("plasma")
  220. mg = np.array([cv2.GaussianBlur(mi, (9, 9), 0) for mi in m])
  221. me = np.mean(mg, axis=0)
  222. m1 = mg - me
  223. m1[m1 < 0] = 0
  224. m1 = norm_img(m1)
  225. m2 = []
  226. for i, mi in enumerate(m1):
  227. img = mi.astype(np.uint8)#cv2.GaussianBlur(mi.astype(np.uint8), (3, 3), 0)
  228. if pv_idx is not None and i < len(pv_idx) and pv_idx[i] >= 0:
  229. # cv2.putText(img, str(pva[i]), (6, 40), cv2.FONT_HERSHEY_PLAIN, 1, (255, 255, 128), 1)
  230. cv2.drawContours(img, [contours[pv_idx[i]].astype(int)], 0, color=(255, 255, 255), thickness=1)
  231. m2.append(img)
  232. write_video(name, (cmap(m2)[:,:,:,:3] * 255).astype(np.uint8), cv2.COLOR_RGB2BGR, FICTRAC_RATE)
  233. # m_mean = np.mean(m2, axis=0)
  234. # m_std = np.std(m2, axis=0)
  235. # mz = []
  236. # for mi in m2:
  237. # zs = (mi - m_mean) / m_std
  238. # zs = np.nan_to_num(zs)
  239. # zs[zs<0]=0
  240. # mz.append(zs)
  241. # mz = norm_img(mz)
  242. # write_video(name + "_zscore.avi", (cmap(mz)[:,:,:,:3] * 255).astype(np.uint8), cv2.COLOR_RGB2BGR, FICTRAC_RATE)
  243. # def write_fig_to_video(output_video, fig, w, h, desc=None, save_img_path=None):
  244. # fig.canvas.draw()
  245. # img = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8)
  246. # img.shape = (h*2, w*2, 3)
  247. # img_bgr = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
  248. # desc and cv2.putText(img_bgr, desc, (6, 20), cv2.FONT_HERSHEY_PLAIN, 1, (255, 255, 128), 1)
  249. # output_video.write(img_bgr)
  250. # save_img_path and cv2.imwrite(save_img_path, img_bgr)
  251. # return img_bgr
  252. def plot_lines(lines, names, ylim=None, xlim_r=1):
  253. n = len(lines)
  254. fig, axes = plt.subplots(n, 1, sharex=True, figsize=(30, 12), dpi=300)
  255. plt.subplots_adjust(left=0.05, right=0.99, top=0.95, bottom=0.05, hspace=0)
  256. for i, r in enumerate(lines):
  257. ax = axes[n - i - 1]
  258. ax.plot(r, c=COLORS[i%12])
  259. ax.set_ylabel(names[i], rotation=0, fontsize=6)
  260. if ylim is not None:
  261. ax.set_ylim(ylim)
  262. if xlim_r is not None:
  263. ax.set_xlim((0, xlim_r * len(r)))
  264. def plot_lines_with_err(py_l, name, USE_SEM=False):
  265. py = np.nanmean(py_l, axis=0)
  266. pe = np.nanstd(py_l, axis=0)
  267. if USE_SEM:
  268. pe = pe / np.sqrt(np.count_nonzero(~np.isnan(py_l), axis=0))
  269. plt.figure()
  270. plt.plot(np.transpose(py_l), alpha=0.1)
  271. plt.plot(py, "--")
  272. plt.fill_between(range(len(py_l[0])), py - pe, py + pe, alpha=0.1)
  273. plt.savefig(name)
  274. plt.close()
  275. def plot_lines_in_one(lines, names):
  276. plt.figure(figsize=(15, 10), dpi=300)
  277. for i, n in enumerate(lines):
  278. plt.plot(n, linewidth=1, c=COLORS[i%12], alpha=0.5)
  279. def plot_pva(ax, dff, unwrap=False, c="k", offset=0):
  280. # max_idx = np.argmax(dff.T, axis=0) + offset
  281. pv_dir, pv_len = calc_pva(dff)
  282. if unwrap:
  283. pv_dir = unwrap_dir(pv_dir)
  284. pi = dff.shape[1]/2
  285. plot_angle(ax, (np.array(pv_dir)+np.pi)*pi/np.pi+offset, c, pi)
  286. def plot_hot(df, save_name, img_rate, is_PB=False):
  287. plt.figure(figsize=(24, 2), dpi=300)
  288. plt.subplots_adjust(left=0.02, right=0.99)
  289. dff = df.to_numpy()
  290. plt.pcolor(dff.T, cmap="jet")
  291. if is_PB:
  292. plot_pva(plt.gca(), dff[:, :9])
  293. plot_pva(plt.gca(), dff[:, 9:], offset=9)
  294. else:
  295. plot_pva(plt.gca(), dff)
  296. frames = len(dff)
  297. seconds = int(frames / img_rate)
  298. labels = np.linspace(0, seconds, 9)
  299. plt.xticks(labels * img_rate, labels)
  300. # plt.colorbar()
  301. plt.savefig(save_name + ".png")
  302. plt.figure(figsize=(24, 2), dpi=300)
  303. plt.subplots_adjust(left=0.02, right=0.99)
  304. if is_PB:
  305. plot_pva(plt.gca(), dff[:, :9], unwrap=True)
  306. plot_pva(plt.gca(), dff[:, 9:], unwrap=True, c="gray", offset=9)
  307. else:
  308. plot_pva(plt.gca(), dff, unwrap=True)
  309. plt.xticks(labels * img_rate, labels)
  310. plt.xlim(0, seconds * img_rate)
  311. plt.savefig(save_name + "_unwrap.png")
  312. def lim_dir(dir1, pi=np.pi):
  313. if dir1 > pi:
  314. dir1 -= 2*pi
  315. elif dir1 < -pi:
  316. dir1 += 2*pi
  317. return dir1
  318. def lim_dir_l(dir_l, pi=np.pi):
  319. dir1 = dir_l.copy()
  320. dir1[dir1 > pi] -= 2*pi
  321. dir1[dir1 < -pi] += 2*pi
  322. return dir1
  323. def unwrap_dir(v, pi=np.pi):
  324. ret = []
  325. li = v[0]
  326. offset = 0
  327. for i in v:
  328. i += offset
  329. d = i - li
  330. if d > pi:
  331. offset -= 2*pi
  332. i -= 2*pi
  333. elif d < -pi:
  334. offset += 2*pi
  335. i += 2*pi
  336. ret.append(i)
  337. li = i
  338. return ret
  339. def unwrap_dir_win(v, rate, sec=10):
  340. win = int(sec*rate)
  341. ret = []
  342. for i in range(0, len(v), win):
  343. u = unwrap_dir(v[i:i + win])
  344. ret.extend([d-u[0] for d in u])
  345. return ret
  346. def plot_angle(ax, al, c, pi=np.pi, xs=None):
  347. # al = unwrap_dir(al) #[lim_dir(a-2) for a in al]
  348. if xs is None:
  349. xs = range(len(al))
  350. last = 0
  351. start = 0
  352. for i, a in enumerate(al):
  353. if abs(a - last) > pi:
  354. ax.plot(xs[start:i], al[start:i], c=c, lw=0.6)
  355. start = i
  356. last = a
  357. ax.plot(xs[start:len(al)], al[start:], c=c, lw=0.6)
  358. def plot_scatter(ax, al, c, xs=None):
  359. if xs is None:
  360. xs = range(len(al))
  361. ax.scatter(xs, al, c=c, s=0.1)
  362. def rotate_unit_vec(rad):
  363. return np.array([np.cos(rad), np.sin(rad)])
  364. def calc_pva(f):
  365. # pv_len = np.max(f, axis=1)
  366. # pv_dir = (np.argmax(f, axis=1) + 0.5) / 8 * np.pi - np.pi
  367. vs = np.array([rotate_unit_vec(2*np.pi*r/f.shape[1]-np.pi) for r in range(f.shape[1])])
  368. pv = f.dot(vs)
  369. pv_len = np.sqrt(np.sum(pv ** 2, axis=1)) / f.shape[1]
  370. pv_dir = np.arctan2(pv[:, 1], pv[:, 0])
  371. return pv_dir, pv_len
  372. def bump_amplitude(f):
  373. bump_a = np.max(f, axis=1) - np.min(f, axis=1)
  374. nor_bump_a = []
  375. max = np.mean(bump_a[np.argpartition(bump_a,-50)[-50:]])
  376. min = np.min(bump_a)
  377. for x in bump_a:
  378. x = float(x - min) / (max - min)
  379. nor_bump_a.append(x)
  380. return nor_bump_a
  381. def bin_bump_speed(f,ft_speed,ft_vx,ft_vy,ft_vz): #for now, ft_speed-mm/s, ft_vx`ft_vy`ft_vz-rad/s
  382. nor_bump_a = bump_amplitude(f)
  383. all_speed = {'0-':[], '0~1':[] , '1~2':[],'2~3':[],'3~4':[],'4~5':[],'5~6':[],'6~7':[],'7~8':[],'8+':[]}
  384. forward_speed = {'0-':[], '0~1':[] , '1~2':[],'2~3':[],'3~4':[],'4~5':[],'5~6':[],'6~7':[],'7~8':[],'8~9':[],'9~10':[],'10+':[]}
  385. lateral_speed = {'-4-':[], '-4~-3':[] , '-3~-2':[],'-2~-1':[],'-1~0':[],'0~1':[],'1~2':[],'2~3':[],'3~4':[],'4+':[]}
  386. # rotation_speed = {}
  387. for i in range(len(ft_speed) - 10):
  388. mean_ft_speed = np.mean(ft_speed[i:i+10])
  389. mean_ft_vx = np.mean(ft_vx[i:i+10] * 8) # forward speed
  390. mean_ft_vy = np.mean(ft_vy[i:i+10] * 8) # lateral speed
  391. if mean_ft_speed < 0:
  392. all_speed['0-'].append(np.mean(nor_bump_a[i:i+10]))
  393. elif mean_ft_speed > 0 and mean_ft_speed <= 1:
  394. all_speed['0~1'].append(np.mean(nor_bump_a[i:i+10]))
  395. elif mean_ft_speed > 1 and mean_ft_speed <= 2:
  396. all_speed['1~2'].append(np.mean(nor_bump_a[i:i+10]))
  397. elif mean_ft_speed > 2 and mean_ft_speed <= 3:
  398. all_speed['2~3'].append(np.mean(nor_bump_a[i:i+10]))
  399. elif mean_ft_speed > 3 and mean_ft_speed <= 4:
  400. all_speed['3~4'].append(np.mean(nor_bump_a[i:i+10]))
  401. elif mean_ft_speed > 4 and mean_ft_speed <= 5:
  402. all_speed['4~5'].append(np.mean(nor_bump_a[i:i+10]))
  403. elif mean_ft_speed > 5 and mean_ft_speed <= 6:
  404. all_speed['5~6'].append(np.mean(nor_bump_a[i:i+10]))
  405. elif mean_ft_speed > 6 and mean_ft_speed <= 7:
  406. all_speed['6~7'].append(np.mean(nor_bump_a[i:i+10]))
  407. elif mean_ft_speed > 8:
  408. all_speed['8+'].append(np.mean(nor_bump_a[i:i+10]))
  409. if mean_ft_vx < 0:
  410. forward_speed['0-'].append(np.mean(nor_bump_a[i:i+10]))
  411. elif mean_ft_vx > 0 and mean_ft_vx <= 1:
  412. forward_speed['0~1'].append(np.mean(nor_bump_a[i:i + 10]))
  413. elif mean_ft_vx > 1 and mean_ft_vx <= 2:
  414. forward_speed['1~2'].append(np.mean(nor_bump_a[i:i + 10]))
  415. elif mean_ft_vx > 2 and mean_ft_vx <= 3:
  416. forward_speed['2~3'].append(np.mean(nor_bump_a[i:i + 10]))
  417. elif mean_ft_vx > 3 and mean_ft_vx <= 4:
  418. forward_speed['3~4'].append(np.mean(nor_bump_a[i:i + 10]))
  419. elif mean_ft_vx > 4 and mean_ft_vx <= 5:
  420. forward_speed['4~5'].append(np.mean(nor_bump_a[i:i + 10]))
  421. elif mean_ft_vx > 5 and mean_ft_vx <= 6:
  422. forward_speed['5~6'].append(np.mean(nor_bump_a[i:i + 10]))
  423. elif mean_ft_vx > 6 and mean_ft_vx <= 7:
  424. forward_speed['6~7'].append(np.mean(nor_bump_a[i:i + 10]))
  425. elif mean_ft_vx > 7 and mean_ft_vx <= 8:
  426. forward_speed['7~8'].append(np.mean(nor_bump_a[i:i + 10]))
  427. elif mean_ft_vx > 8 and mean_ft_vx <= 9:
  428. forward_speed['8~9'].append(np.mean(nor_bump_a[i:i + 10]))
  429. elif mean_ft_vx > 10:
  430. forward_speed['10+'].append(np.mean(nor_bump_a[i:i + 10]))
  431. if mean_ft_vy < -4:
  432. lateral_speed['-4-'].append(np.mean(nor_bump_a[i:i+10]))
  433. elif mean_ft_vy > -4 and mean_ft_vy < -3:
  434. lateral_speed['-4~-3'].append(np.mean(nor_bump_a[i:i+10]))
  435. elif mean_ft_vy > -3 and mean_ft_vy < -2:
  436. lateral_speed['-3~-2'].append(np.mean(nor_bump_a[i:i+10]))
  437. elif mean_ft_vy > -2 and mean_ft_vy < -1:
  438. lateral_speed['-2~-1'].append(np.mean(nor_bump_a[i:i+10]))
  439. elif mean_ft_vy > -1 and mean_ft_vy < 0:
  440. lateral_speed['-1~0'].append(np.mean(nor_bump_a[i:i+10]))
  441. elif mean_ft_vy > 0 and mean_ft_vy < 1:
  442. lateral_speed['0~1'].append(np.mean(nor_bump_a[i:i+10]))
  443. elif mean_ft_vy > 1 and mean_ft_vy < 2:
  444. lateral_speed['1~2'].append(np.mean(nor_bump_a[i:i+10]))
  445. elif mean_ft_vy > 2 and mean_ft_vy < 3:
  446. lateral_speed['2~3'].append(np.mean(nor_bump_a[i:i+10]))
  447. elif mean_ft_vy > 3 and mean_ft_vy < 4:
  448. lateral_speed['3~4'].append(np.mean(nor_bump_a[i:i+10]))
  449. elif mean_ft_vy > 4:
  450. lateral_speed['4+'].append(np.mean(nor_bump_a[i:i+10]))
  451. return all_speed, forward_speed, lateral_speed
  452. def unify_sample(f, ts, fps, n): # no interp
  453. idx = []
  454. inter = 1.0 / fps
  455. i = 0
  456. for j in range(n):
  457. t = j * inter
  458. if ts[i+1] < t:
  459. while i < len(ts) - 1 and ts[i+1] < t:
  460. i += 1
  461. if i+1 >= len(f):
  462. break
  463. if t < ts[i]:
  464. idx.append(i)
  465. continue
  466. if i >= len(ts) - 1:
  467. break
  468. if t - ts[i] > ts[i+1] - t:
  469. idx.append(i+1)
  470. else:
  471. idx.append(i)
  472. return f[idx], idx
  473. def calc_offset(ft, pv):
  474. n = min(len(ft), len(pv))
  475. o = ft[:n] - pv[:n]
  476. # o[o < 0] += 2*np.pi
  477. o[o > np.pi] -= 2*np.pi
  478. o[o < -np.pi] += 2*np.pi
  479. return o
  480. def plot_slide_cor(ax, ft, pv, rate):
  481. for win_sec in [10]:#4, 16, 64
  482. win = int(win_sec * rate)
  483. ax.plot(*slide_cor(ft, pv, win, rate))
  484. def circular_std(o):
  485. o = o[~np.isnan(o)]
  486. # s = np.sin(o)
  487. # c = np.cos(o)
  488. # return np.sqrt(-2 * np.log(np.sqrt(np.sum(s) ** 2 + np.sum(c) ** 2) / np.linalg.norm(o)))
  489. from scipy.stats import circstd
  490. return circstd(o)
  491. def format_time(t):
  492. if t < 60:
  493. return "%.2f" % t
  494. return "%d:%.2f" % (t//60, t%60)
  495. def non_nan(s):
  496. return s[~np.isnan(s)]
  497. def plot_dFF_MB_grating(dFF_name, exp_info, unify_rate=None, use_fictrac = False):
  498. parent = os.path.dirname(dFF_name)
  499. if use_fictrac:
  500. fictrac_name = get_ft_dat(parent)
  501. ft = pd.read_csv(fictrac_name, header=None).to_numpy() # (15079frame, 23field) 50Hz
  502. bar_name = get_ft_bar(parent)
  503. pd_name = get_pd_h5(parent)
  504. dFF = pd.read_csv(dFF_name).to_numpy()[:, 1:] # (2200frame, 16roi) 6.736Hz
  505. import h5py
  506. ni = h5py.File(pd_name, "r")
  507. pd_info_raw = ni['AI']["photodiode"][:, 0] # (1640499frame,) 5000Hz
  508. frame_counter = ni['CI']["FrameCounter"][:, 0]
  509. frame_out = ni['DI']["FrameOut"][:, 0]
  510. img_rate = exp_info["frameRate"] # only one z-step in this experiment
  511. if not unify_rate:
  512. unify_rate = img_rate
  513. use_img_rate = True
  514. seconds = len(pd_info_raw) / PD_RATE # int(len(dFF) / img_rate)
  515. n = int(seconds * unify_rate)
  516. seconds = n / unify_rate
  517. frame_start = np.nonzero(np.diff(frame_counter) > 0)[0]
  518. volume_frame = 1
  519. frame_time = frame_start[int(exp_info["steps"] / 2)::volume_frame] / PD_RATE
  520. # smooth filter for PD data
  521. n = 100
  522. pd_info_raw = np.convolve(pd_info_raw, np.ones((n,)) / n, mode='same')
  523. temp = get_all_sti_pos(pd_info_raw > 0.02)
  524. sti_position_duration = []
  525. for cc in temp:
  526. if cc[1] > PD_RATE:
  527. sti_position_duration.append(cc)
  528. sti_ima_frame = []
  529. for cc in sti_position_duration:
  530. sti_ima_frame.append([find_nearest(frame_start,cc[0]),find_nearest(frame_start,cc[0]+cc[1])])
  531. fig, axs = plt.subplots(np.shape(dFF)[1]+1, 1, figsize=(20, 10), dpi=300, sharex=True)
  532. axs[0].set_title(parent.split('\\')[-2] + ' ' + parent.split('\\')[-1])
  533. ts = np.linspace(1,len(dFF),len(dFF)) / img_rate
  534. for i in range(np.shape(dFF)[1]):
  535. axs[i].plot(ts, dFF[:,i])
  536. axs[i].set_ylim( np.min(dFF[:,i]), np.max(dFF[:,i]) )
  537. for kk in sti_ima_frame:
  538. axs[i].add_patch(
  539. patches.Rectangle(
  540. (kk[0] / img_rate, np.min(dFF[:,i])),
  541. (kk[1]-kk[0]) / img_rate,
  542. np.max(dFF[:,i]),
  543. edgecolor=None,
  544. facecolor='blue',
  545. alpha = 0.3
  546. ))
  547. axs[i].set_ylabel('ROI' + str(i + 1) + ' _deltaF/F')
  548. axs[i].set_xlabel('t/s')
  549. plt.savefig(os.path.join(parent,'all_ROI.png'))
  550. plt.savefig(os.path.join(parent, 'all_ROI.pdf'))
  551. each_sti = []
  552. for kk in sti_ima_frame:
  553. each_sti.append((dFF[kk[0] - 100 : kk[1] + 100,:]).tolist())
  554. pass
  555. json.dump(each_sti, open(parent + "/each_sti.txt", "w"))
  556. ###########################################################################
  557. # m = load_memmap(glob(os.path.join(parent, "*.mmap"))[0])
  558. #
  559. # m2 = []
  560. # cmap = plt.cm.get_cmap("gray")
  561. # for i, mi in enumerate(m):
  562. # img = mi.astype(np.uint8)#cv2.GaussianBlur(mi.astype(np.uint8), (3, 3), 0)
  563. # m2.append(img)
  564. # write_video(parent + "/mc_sti.avi",(cmap(m2)[:,:,:,:3] * 255).astype(np.uint8),need_time=False,sti_img_frame=sti_ima_frame)
  565. # try:
  566. # whole_sti_start_end = (dFF[sti_ima_frame[0][0] - 150 : sti_ima_frame[-1][-1] + 150]).tolist()
  567. # whole_sti_pos = sti_ima_frame - sti_ima_frame[0][0] + 150
  568. # except:
  569. # whole_sti_start_end = []
  570. # whole_sti_pos = np.nan
  571. # json.dump( [whole_sti_start_end, whole_sti_pos.tolist()]
  572. # , open(parent + "/whole_sti_start_end.txt", "w"))
  573. pass
  574. def plot_dFF_MB_UVorLaser(dFF_name, exp_info, unify_rate=None, use_fictrac = False):
  575. parent = os.path.dirname(dFF_name)
  576. if use_fictrac:
  577. fictrac_name = get_fictrac_dat(parent)
  578. ft = pd.read_csv(fictrac_name, header=None).to_numpy() # (15079frame, 23field) 50Hz
  579. bar_name = get_ft_bar(parent)
  580. pd_name = get_pd_h5(parent)
  581. dFF = pd.read_csv(dFF_name).to_numpy()[:, 1:] # (2200frame, 16roi) 6.736Hz
  582. import h5py
  583. ni = h5py.File(pd_name, "r")
  584. pd_info_raw = ni['AI']["UVLED"][:, 0] # the DAQ input of the matlab (UV or laser signal & fictrac trigger signal)
  585. frame_counter = ni['CI']["FrameCounter"][:, 0]
  586. frame_out = ni['DI']["FrameOut"][:, 0]
  587. img_rate = exp_info["frameRate"] # only one z-step in this experiment
  588. if not unify_rate:
  589. unify_rate = img_rate
  590. use_img_rate = True
  591. seconds = len(pd_info_raw) / PD_RATE # int(len(dFF) / img_rate)
  592. n = int(seconds * unify_rate)
  593. seconds = n / unify_rate
  594. frame_start = np.nonzero(np.diff(frame_counter) > 0)[0]
  595. if exp_info.get("zFastEnable"):
  596. img_rate = exp_info["frameRate"] / (exp_info["steps"] + exp_info["flybackFrames"])
  597. volume_frame = exp_info["steps"] + exp_info["flybackFrames"]
  598. frame_time = frame_start[int(exp_info["steps"] / 2)::volume_frame] / PD_RATE
  599. frame_start=frame_start[int(exp_info["steps"] / 2)::volume_frame]
  600. else:
  601. img_rate = exp_info["frameRate"]
  602. volume_frame = 1
  603. frame_time = frame_start / PD_RATE
  604. # volume_frame = 1
  605. # frame_time = frame_start[int(exp_info["steps"] / 2)::volume_frame] / PD_RATE
  606. #a = pd_info_raw > 0.04
  607. temp = get_all_sti_pos(pd_info_raw > 0.8)
  608. sti_position_duration = []
  609. for cc in temp:
  610. if cc[1] > PD_RATE/2:
  611. sti_position_duration.append(cc)
  612. sti_ima_frame = []
  613. for cc in sti_position_duration:
  614. sti_ima_frame.append([find_nearest(frame_start,cc[0]),find_nearest(frame_start,cc[0]+cc[1])])
  615. fig, axs = plt.subplots(np.shape(dFF)[1]+1, 1, figsize=(20, 10), dpi=300, sharex=True)
  616. axs[0].set_title(parent.split('\\')[-2] + ' ' + parent.split('\\')[-1])
  617. ts = np.linspace(1,len(dFF),len(dFF)) / img_rate
  618. for i in range(np.shape(dFF)[1]):
  619. axs[i].plot(ts, dFF[:,i])
  620. axs[i].set_ylim( np.min(dFF[:,i]), np.max(dFF[:,i]) )
  621. for kk in sti_ima_frame:
  622. axs[i].add_patch(
  623. patches.Rectangle(
  624. (kk[0] / img_rate, np.min(dFF[:,i])),
  625. (kk[1]-kk[0]) / img_rate,
  626. np.max(dFF[:,i]),
  627. edgecolor=None,
  628. facecolor='blue',
  629. alpha = 0.3
  630. ))
  631. axs[i].set_ylabel('ROI' + str(i + 1))
  632. axs[i].set_xlabel('t/s')
  633. plt.savefig(os.path.join(parent,'all_ROI.png'))
  634. # plt.savefig(os.path.join(parent, 'all_ROI.pdf'))
  635. each_sti = []
  636. for kk in sti_ima_frame:
  637. each_sti.append((dFF[kk[0] - int(img_rate * 2) : kk[1] + int(img_rate * 2),:]).tolist())
  638. pass
  639. json.dump(each_sti, open(parent + "/each_sti.txt", "w"))
  640. try:
  641. whole_sti_start_end = (dFF[sti_ima_frame[0][0] - 150 : sti_ima_frame[-1][-1] + 150]).tolist()
  642. whole_sti_pos = sti_ima_frame - sti_ima_frame[0][0] + 150
  643. except:
  644. whole_sti_start_end = whole_sti_pos = []
  645. json.dump( [whole_sti_start_end, whole_sti_pos.tolist()]
  646. , open(parent + "/whole_sti_start_end.txt", "w"))
  647. # ########################################################################### write video ######################################
  648. # m = load_memmap(glob(os.path.join(parent, "*.mmap"))[0])
  649. #
  650. # m2 = []
  651. # cmap = plt.cm.get_cmap("gray")
  652. # for i, mi in enumerate(m):
  653. # img = mi.astype(np.uint8)#cv2.GaussianBlur(mi.astype(np.uint8), (3, 3), 0)
  654. # m2.append(img)
  655. # write_video(parent + "/mc_sti.avi",(cmap(m2)[:,:,:,:3] * 255).astype(np.uint8),need_time=False,sti_img_frame=sti_ima_frame)
  656. def plot_dFF_MB_fictrac(dFF_name, exp_info, unify_rate=None, use_fictrac = True):
  657. parent = os.path.dirname(dFF_name)
  658. if use_fictrac:
  659. fictrac_name = get_fictrac_dat(parent)
  660. ft = pd.read_csv(fictrac_name, header=None).to_numpy() # (15079frame, 23field) 50Hz
  661. #bar_name = get_ft_bar(parent)
  662. pd_name = get_pd_h5(parent)
  663. dFF = pd.read_csv(dFF_name).to_numpy()[:, 1:] # (2200frame, 16roi) 6.736Hz
  664. import h5py
  665. ni = h5py.File(pd_name, "r")
  666. pd_info_raw = ni['AI']["side_camera"][:, 0] # the DAQ input of the matlab (UV or laser signal & fictrac trigger signal)
  667. frame_counter = ni['CI']["FrameCounter"][:, 0]
  668. frame_out = ni['DI']["FrameOut"][:, 0]
  669. frame_start = np.nonzero(np.diff(frame_counter) > 0)[0]
  670. if exp_info.get("zFastEnable"):
  671. img_rate = exp_info["frameRate"] / (exp_info["steps"] + exp_info["flybackFrames"])
  672. volume_frame = exp_info["steps"] + exp_info["flybackFrames"]
  673. frame_time = frame_start[int(exp_info["steps"] / 2)::volume_frame] / PD_RATE
  674. frame_start=frame_start[int(exp_info["steps"] / 2)::volume_frame]
  675. else:
  676. img_rate = exp_info["frameRate"]
  677. volume_frame = 1
  678. frame_time = frame_start / PD_RATE
  679. unify_rate = img_rate
  680. # "int(exp_info["steps"]/2)" get the middle frame of each volumn
  681. #fictrac_start = np.nonzero(np.diff(pd_info_raw) > 1)[0]
  682. temp = get_all_sti_pos(pd_info_raw > 1)
  683. sti_position_duration = []
  684. for cc in temp:
  685. if cc[1] > 100:
  686. sti_position_duration.append(cc)
  687. fictrac_start = []
  688. for cc in sti_position_duration:
  689. fictrac_start.append(find_nearest(frame_start,cc[0]))
  690. fictrac_time = np.array(sti_position_duration)[:,0] / PD_RATE
  691. sti_ima_frame = [find_nearest(frame_start, sti_position_duration[0][0]), find_nearest(frame_start,sti_position_duration[-1][0])]
  692. dFF_unifyToFictrac = dFF[fictrac_start][0:-1]
  693. #Fictrac_data = ft[1:-2][:] # discard the first and last frame
  694. if len(dFF_unifyToFictrac) > len(ft):
  695. dFF_unifyToFictrac = dFF_unifyToFictrac[:len(ft)]
  696. else:
  697. ft = ft[:len(dFF_unifyToFictrac)]
  698. Fictrac_data = ft
  699. ft_speed = Fictrac_data[:, 18] * FICTRAC_RATE * BALL_RADIUS # mm/s all_speed
  700. ft_vy = -Fictrac_data[:, 5] * FICTRAC_RATE * BALL_RADIUS # rad/s lateral_speed
  701. ft_vx = Fictrac_data[:, 6] * FICTRAC_RATE * BALL_RADIUS # rad/s forward_speed
  702. ft_vz = -Fictrac_data[:, 7] * FICTRAC_RATE # rad/s rotation_speed
  703. for roi_index in range(np.shape(dFF)[1]):
  704. window = np.ones(int(10)) / float(10)
  705. dFF_unifyToFictrac_avg = np.convolve(dFF_unifyToFictrac[:,roi_index], window, 'same')
  706. ft_speed_avg = np.convolve(ft_speed, window, 'same')
  707. ft_vx_avg = np.convolve(ft_vx, window, 'same')
  708. ft_vy_avg = np.convolve(ft_vy, window, 'same')
  709. ft_vz_avg = np.convolve(ft_vz, window, 'same')
  710. ts = np.linspace(1, len(ft_speed_avg), len(ft_speed_avg)) / FICTRAC_RATE
  711. fig, axs = plt.subplots(4, 1, figsize=(20, 10), dpi=300, sharex=True)
  712. axs[0].set_title(os.path.basename(os.path.dirname(parent)) + os.path.basename(parent))
  713. axs[0].plot(ts, dFF_unifyToFictrac_avg, 'black')
  714. axs[0].set_ylabel('$\Delta$F/F')
  715. twin0 = axs[0].twinx()
  716. twin0.plot(ts, ft_speed_avg, 'red')
  717. twin0.set_ylabel('all speed mm/s', color='r', fontsize=8)
  718. axs[1].plot(ts, dFF_unifyToFictrac_avg, 'black')
  719. axs[1].set_ylabel('$\Delta$F/F')
  720. twin0 = axs[1].twinx()
  721. twin0.plot(ts, ft_vx_avg, 'g')
  722. twin0.set_ylabel('forward speed mm/s', color='g', fontsize=8)
  723. axs[2].plot(ts, dFF_unifyToFictrac_avg, 'black')
  724. axs[2].set_ylabel('$\Delta$F/F')
  725. twin0 = axs[2].twinx()
  726. twin0.plot(ts, ft_vy_avg, 'b')
  727. twin0.set_ylabel('lateral speed mm/s', color='b', fontsize=8)
  728. axs[3].plot(ts, dFF_unifyToFictrac_avg, 'black')
  729. axs[3].set_ylabel('$\Delta$F/F')
  730. twin0 = axs[3].twinx()
  731. twin0.plot(ts, ft_vz_avg, 'orange')
  732. twin0.set_ylabel('rotation speed rad/s', color='orange', fontsize=8)
  733. axs[3].set_xlabel('t/s')
  734. plt.savefig(os.path.join(os.path.dirname(dFF_name), dFF_name +str(roi_index+1)+ "_fictrac.png"))
  735. json.dump([dFF_unifyToFictrac.tolist(), ft_speed.tolist(), ft_vx.tolist(), ft_vy.tolist(), ft_vz.tolist()], open(parent + "/dFF_speed.txt", "w"))
  736. # seconds = len(pd_info_raw) / PD_RATE # int(len(dFF) / img_rate)
  737. # n = int(seconds * unify_rate)
  738. # #dFF_info, dff_unify_idx = unify_sample(dFF, frame_time, unify_rate, n)
  739. # ft_info, ft_unify_idx = unify_sample(ft, fictrac_time - fictrac_time[0], unify_rate, n)
  740. pass
  741. def plot_dFF_fictrac(dFF_name, exp_info, unify_rate=None):
  742. parent = os.path.dirname(dFF_name)
  743. fictrac_name = get_ft_dat(parent)
  744. bar_name = get_ft_bar(parent)
  745. pd_name = get_pd_h5(parent)
  746. ft = pd.read_csv(fictrac_name, header=None).to_numpy() # (15079frame, 23field) 50Hz
  747. dFF = pd.read_csv(dFF_name).to_numpy()[:, 1:] # (2200frame, 16roi) 6.736Hz
  748. import h5py
  749. ni = h5py.File(pd_name, "r")
  750. pd_info_raw = ni['AI']["photodiode"][:, 0] # (1640499frame,) 5000Hz
  751. frame_counter = ni['CI']["FrameCounter"][:, 0]
  752. frame_out = ni['DI']["FrameOut"][:, 0]
  753. use_img_rate = False
  754. img_rate = exp_info["frameRate"]/(exp_info["steps"] + exp_info["flybackFrames"])
  755. if not unify_rate:
  756. unify_rate = img_rate
  757. use_img_rate = True
  758. seconds = len(pd_info_raw) / PD_RATE #int(len(dFF) / img_rate)
  759. n = int(seconds * unify_rate)
  760. seconds = n / unify_rate
  761. frame_start = np.nonzero(np.diff(frame_counter) > 0)[0]
  762. volume_frame = exp_info["steps"] + exp_info["flybackFrames"]
  763. frame_time = frame_start[int(exp_info["steps"]/2)::volume_frame] / PD_RATE
  764. # frame_time = np.arange(frame_start[0]/PD_RATE, seconds, 1.0/img_rate)
  765. pd_info = down_sample(pd_info_raw[:int(seconds * PD_RATE)], n)
  766. pd_info_50 = down_sample(pd_info_raw[:int(seconds * PD_RATE)], int(seconds * 50))
  767. # frame_info = down_sample(frame_out[:int(seconds * PD_RATE)], n)/10.0
  768. dFF_info, dff_unify_idx = unify_sample(dFF, frame_time, unify_rate, n)
  769. ts = ft[:, -2]/1000
  770. if max(ts - ts[0]) <= 0:
  771. ts = np.arange(0, len(ts)) / FICTRAC_RATE
  772. ft_info, ft_unify_idx = unify_sample(ft, ts - ts[0], unify_rate, n)
  773. ft_bar_info = load_ft_bar(bar_name)
  774. if ft_bar_info is not None:
  775. ft_bar, ft_unify_idx = unify_sample(ft_bar_info, ts - ts[0], unify_rate, n)
  776. pd_thresh = 0.14
  777. ft_range = get_stim_range(pd_info_50 > pd_thresh)
  778. ft_range_t = np.array(ft_range[0]) / 50
  779. ft_range_u = (ft_range_t * unify_rate).astype(int)
  780. dff_range_t = [frame_time[0], frame_time[-1]]
  781. ft_unify_idx = np.concatenate([[0]*ft_range_u[0], ft_unify_idx]).astype(int)
  782. json.dump([ft_range_t.tolist(), dff_range_t, ft_unify_idx.tolist(), dff_unify_idx, ft_bar_info.tolist() if ft_bar_info is not None else []], open(dFF_name + "_time_info.txt", "w"))
  783. # exit(0)
  784. # ft_xs = np.arange(len(ft_info)) + ft_range_u[0]
  785. ft_heading = np.concatenate([[np.nan]*ft_range_u[0], ft_info[:, 16]])
  786. # ft_heading = lim_dir_l(ft_heading)
  787. if ft_bar_info is not None:
  788. ft_bar = np.concatenate([[np.nan]*ft_range_u[0], 2*np.pi - ft_bar])
  789. # ft_bar = lim_dir_l(ft_bar)
  790. else:
  791. ft_bar = ft_heading
  792. ft_speed = np.concatenate([[np.nan]*ft_range_u[0], ft_info[:, 18]]) * FICTRAC_RATE * BALL_RADIUS # mm/s
  793. ft_vy = np.concatenate([[np.nan]*ft_range_u[0], -ft_info[:, 5]]) * FICTRAC_RATE # rad/s
  794. ft_vx = np.concatenate([[np.nan]*ft_range_u[0], ft_info[:, 6]]) * FICTRAC_RATE
  795. ft_vz = np.concatenate([[np.nan]*ft_range_u[0], -ft_info[:, 7]]) * FICTRAC_RATE
  796. #########################################################################################################################################################
  797. all_speed, forward_speed, lateral_speed = bin_bump_speed(dFF_info,ft_speed,ft_vx,ft_vy,ft_vz)
  798. json.dump([all_speed, forward_speed, lateral_speed], open(parent + "/bin_bump_speed.txt", "w"))
  799. #return
  800. ###############################################################################################################################################################
  801. roi_n = dFF_info.shape[1]
  802. if use_img_rate:
  803. fig, axs = plt.subplots(roi_n, 1, figsize=(10, 30), dpi=300, sharex=True)
  804. pv_dir, pv_len = calc_pva(dFF_info)
  805. cor_s, cor_e = int(ft_range_u[0]+5*unify_rate), min(ft_range_u[1], len(ft_bar), len(pv_dir))
  806. ft_bar_cor = ft_bar[cor_s:cor_e]
  807. for i in range(roi_n):
  808. dffi = dFF_info[cor_s:cor_e, i]
  809. axs[i].scatter(ft_bar_cor, dffi, s=1)
  810. plt.savefig(parent + "/ft_pv_tune")
  811. #return
  812. fig, axs = plt.subplots(10, 1, figsize=(20, 10), dpi=300, sharex=True)
  813. plt.subplots_adjust(left=0.02, right=0.99)
  814. axs[0].pcolor(dFF_info.T, cmap="jet")
  815. is_pb = dFF_name.find("-PB-") > 0
  816. if is_pb:
  817. pv_dir, pv_len = calc_pva(dFF_info[:, :9])
  818. # pv_dir = lim_dir_l(np.array(pv_dir))
  819. pv_dir2, pv_len2 = calc_pva(dFF_info[:, 9:])
  820. # pv_dir2 = lim_dir_l(np.array(pv_dir2))
  821. # max_idx = np.argmax(dFF_info.T[:9], axis=0)
  822. # pv_dir = (max_idx + 0.5)*np.pi/4.5
  823. plot_angle(axs[0], (pv_dir+np.pi) * 4.5 / np.pi + 9, "k", 4.5)
  824. else:
  825. pv_dir, pv_len = calc_pva(dFF_info)
  826. # pv_dir = lim_dir_l(np.array(pv_dir))
  827. pv_dir += np.pi
  828. pv_dir2, pv_len2 = pv_dir, pv_len
  829. # max_idx = np.argmax(dFF_info.T, axis=0)
  830. # pv_dir = (max_idx + 0.5)*np.pi/8
  831. pi = roi_n / 2
  832. # plot_angle(axs[0], (np.array(pv_dir) + np.pi) * pi / np.pi, "k", pi)
  833. plot_angle(axs[0], pv_dir * pi / np.pi, "k", pi)
  834. # axs[1].plot(frame_info, alpha=0.2, c="k")
  835. axs[1].plot(pd_info, c="b")
  836. plot_angle(axs[2], pv_dir, c="k")
  837. # if is_pb:
  838. # ft_bar = -ft_bar
  839. # plot_angle(axs[2], pv_dir2, c="gray")
  840. plot_angle(axs[2], ft_bar, "b") # bar
  841. # plot_angle(axs[2], ft_heading, "g") # heading
  842. axs[2].set_ylim(0, 2*np.pi)
  843. # axs[2].set_ylim(-np.pi, np.pi)
  844. # axs[2].add_patch(plt.Rectangle((0, -np.pi), n, np.pi / 4, alpha=0.1, color="k", linewidth=0))
  845. # axs[2].add_patch(plt.Rectangle((0, 3*np.pi/4), n, np.pi / 4, alpha=0.1, color="k", linewidth=0))
  846. axs[2].add_patch(plt.Rectangle((0, 0), n, np.pi / 4, alpha=0.1, color="k", linewidth=0))
  847. axs[2].add_patch(plt.Rectangle((0, 7*np.pi/4), n, np.pi / 4, alpha=0.1, color="k", linewidth=0))
  848. offset = calc_offset(ft_bar, pv_dir)
  849. axs[3].plot(offset)
  850. # plot_angle(axs[4], lim_dir_l(pv_dir), c="k")
  851. ft_bar_correct = lim_dir_l(ft_bar - circmean(non_nan(offset)))
  852. ft_bar_correct[ft_bar_correct < 0] += 2 * np.pi
  853. plot_angle(axs[0], ft_bar_correct / (2*np.pi) * roi_n, "r") # heading - offset
  854. # axs[4].set_ylim(-np.pi, np.pi)
  855. plot_scatter(axs[5], unwrap_dir_win(ft_bar, unify_rate), "b")
  856. plot_scatter(axs[5], unwrap_dir_win(pv_dir, unify_rate), "k")
  857. axs[5].set_ylim(-6, 6)
  858. axs[6].plot(unwrap_dir(ft_bar), c="b", lw=0.5)
  859. axs[6].plot(unwrap_dir(ft_heading), c="g", lw=0.5)
  860. axs[6].plot(unwrap_dir(smooth_angle(pv_dir, 10)), "k--", lw=0.5)
  861. axs[6].plot(unwrap_dir(pv_dir), c="k", lw=0.5)
  862. plot_slide_cor(axs[-3], ft_bar, pv_dir, unify_rate)
  863. axs[-3].set_ylim(-1, 1)
  864. axs[-2].plot(ft_speed, c="r") # speed
  865. axs[-1].plot(zscore1(ft_speed), c="r", alpha=0.5)
  866. axs[-1].plot(zscore1(pv_len), c="k", alpha=0.5)
  867. # plt.show()
  868. ticks = np.arange(0, len(pd_info), 5*unify_rate)
  869. axs[-1].set_xticks(ticks)
  870. axs[-1].set_xticklabels(["%.1f"%l for l in ticks/unify_rate])
  871. cor_s, cor_e = int(ft_range_u[0]+5*unify_rate), min(ft_range_u[1], len(ft_bar), len(pv_dir))
  872. if cor_s > cor_e:
  873. print("error: ft range not found!")
  874. exit(0)
  875. ft, pv = ft_bar[cor_s:cor_e], pv_dir[cor_s:cor_e]
  876. cor_unwrap = cor(unwrap_dir(ft), unwrap_dir(pv))
  877. cor_cir = cir_cor(ft, pv)
  878. axs[0].set_title("%s fps: %.2f, speed: %.2f, dFF (%s, %s), fictrac (%s, %s), dFF~heading: %.2f (unwrap) %.2f (circ)" % (
  879. os.path.basename(parent), unify_rate, np.nanmean(ft_speed),
  880. format_time(dff_range_t[0]), format_time(dff_range_t[1]), format_time(ft_range_t[0]), format_time(ft_range_t[1]),
  881. cor_unwrap, cor_cir
  882. ))
  883. plt.savefig(os.path.join(os.path.dirname(dFF_name), dFF_name + "_fictrac.png"))
  884. json.dump({"cor_unwrap": cor_unwrap, "cor_cir": cor_cir}, open(parent + "/info.txt", "w"))
  885. ft_pv = [
  886. [cor_s/unify_rate, cor_e/unify_rate], ft.tolist(), pv.tolist(), pv_len[cor_s:cor_e].tolist(), ft_speed[cor_s:cor_e].tolist(),
  887. ft_vx[cor_s:cor_e].tolist(), ft_vy[cor_s:cor_e].tolist(), ft_vz[cor_s:cor_e].tolist()
  888. ]
  889. if pv_dir2 is not None:
  890. ft_pv.append(pv_dir2[cor_s:cor_e].tolist())
  891. ft_pv.append(pv_len2[cor_s:cor_e].tolist())
  892. json.dump(ft_pv, open(parent + "/ft_pv.txt", "w"))
  893. json.dump(offset.tolist(),open(parent + "/offset.txt", "w"))
  894. plt.figure()
  895. plt.hist(offset, bins=50)
  896. plt.title("%s fps: %.2f, mean: %.2f SD: %.2f cSD: %.2f" % (
  897. os.path.basename(parent), unify_rate,
  898. np.nanmean(offset), np.nanstd(offset), circular_std(offset)
  899. ))
  900. plt.savefig(os.path.join(os.path.dirname(dFF_name), dFF_name + "_offset.png"))
  901. # ft_win = window * FICTRAC_RATE
  902. # dFF_win = window * IMG_RATE
  903. # s = int(min(len(ft) / ft_win, len(dFF) / dFF_win))
  904. # cor_dir, cor_speed = [], []
  905. # for i in range(s):
  906. # ft_s = ft[int(i * ft_win): int((i+1) * ft_win)]
  907. # dFF_s = dFF[int(i * dFF_win): int((i+1) * dFF_win)]
  908. # pv_dir, pv_len = calc_pva(dFF_s)
  909. # # plt.figure("pv_dir");plt.plot(unwrap_dir(pv_dir))
  910. # # plt.figure("heading");plt.plot(unwrap_dir(ft_s[:, 16]))
  911. # # plt.figure("pv_len");plt.plot(pv_len)
  912. # # plt.figure("speed");plt.plot(ft_s[:, 18])
  913. # # plt.show()
  914. # cor_dir.append(cor_equal_len(unwrap_dir(pv_dir), unwrap_dir(ft_s[:, 16])))
  915. # cor_speed.append(cor_equal_len(pv_len, ft_s[:, 18]))
  916. # plt.plot(cor_dir)
  917. # plt.plot(cor_speed)
  918. # plt.show()
  919. def plot_rois(rois, m):
  920. plt.figure(figsize=(3, 3), dpi=300)
  921. plt.axis("off")
  922. ax = plt.gca()
  923. ax.set_position([0, 0, 1, 1], which="both")
  924. ax.imshow(norm_img(np.std(m, axis=0)), cmap=plt.cm.gray)
  925. for i, xys in enumerate(rois):
  926. ax.add_patch(plt.Polygon(xys, alpha=0.6, fill=False, linewidth=1, color=COLORS[i%12]))
  927. x, y = xys.min(axis=0)/2 + xys.max(axis=0)/2
  928. ax.text(x, y, str(i), color="r")
  929. def get_part(fname):
  930. if fname.find("-PB-") >= 0:
  931. return "PB"
  932. elif fname.find("-FB-") >= 0:
  933. return "FB"
  934. elif fname.find("MB") >= 0:
  935. return "manual draw"
  936. elif fname.find("57C10") >= 0:
  937. return "manual draw"
  938. elif fname.find("D161") >= 0:
  939. return "manual draw"
  940. elif fname.find("BU") >= 0:
  941. return "manual draw"
  942. elif fname.find("AOTU") >= 0:
  943. return "manual draw"
  944. elif fname.find("EB") >= 0:
  945. # return "manual draw"
  946. return "EB"
  947. else:
  948. return "manual draw"
  949. def plot_fictrac(fname, parent, win=10, step=1, speed_min=0.5):
  950. df = pd.read_csv(fname, header=None).to_numpy().T
  951. heading = df[16]
  952. speed = df[18]
  953. inv = speed*FICTRAC_RATE < speed_min
  954. heading[inv] = np.nan
  955. stand = (np.count_nonzero(inv) / len(heading))
  956. print("stand still %.2f" % stand)
  957. vec_x, vec_y = np.cos(df[16]), np.sin(df[16])
  958. mx, my = [], []
  959. for i in range(0, len(heading) - win * FICTRAC_RATE, step * FICTRAC_RATE):
  960. mx.append(np.nanmean(vec_x[i: i+win * FICTRAC_RATE]))
  961. my.append(np.nanmean(vec_y[i: i+win * FICTRAC_RATE]))
  962. mean_vec = np.nanmean(np.sqrt(np.array(mx)**2+np.array(my)**2))
  963. plt.figure()
  964. plt.title("walk %.2f, mean %.2f" % (1-stand, mean_vec))
  965. plt.axis("equal")
  966. plt.hist2d(mx, my, bins=20, range=((-1, 1), (-1, 1)))
  967. plt.savefig(parent + "_mean_vec.png")
  968. plt.figure()
  969. plt.plot(speed*FICTRAC_RATE)
  970. plt.savefig(fname + "_speed.png")
  971. return
  972. def get_pd_h5(parent):
  973. pp = os.path.dirname(parent)
  974. #if (not parent[-1].isdigit()) or parent[-1] == 'D' or parent[-1] == 'g' or parent[-1] == 'r' or parent[-1] == 'V' or parent[-6:] == 'MBON11':n
  975. if (not parent[-3:].isdigit()):
  976. ppn = os.path.basename(parent).rstrip("+") + '_000'
  977. else:
  978. ppn = os.path.basename(parent).rstrip("-").rstrip("+").replace("_0", "_0")
  979. return os.path.join(pp, "PD_" + ppn + "/Episode001.h5")
  980. def get_fictrac_dat(parent):
  981. file_folder = os.path.basename(parent)
  982. temp = file_folder.split('_')
  983. if len(temp) == 5:
  984. data_folder = temp[2][0:2] + temp[3][0] + '-0'
  985. elif len(temp) == 6:
  986. data_folder = temp[2][0:2] + temp[3][0] + '-' + str(int(temp[-1]))
  987. if temp[0] == 'D161':
  988. data_folder = data_folder.replace('L','')
  989. if temp[0] == 'VC102':
  990. # if len(temp) == 5:
  991. # data_folder = temp[2][0:2] + temp[3] + '-0'
  992. # elif len(temp) == 6:
  993. # data_folder = temp[2][0:2] + temp[3] + '-' + str(int(temp[-1]))
  994. if 'R' in data_folder:
  995. data_folder = data_folder.replace('R', '')
  996. elif 'L' in data_folder:
  997. data_folder = data_folder.replace('L', '')
  998. return glob(os.path.join(os.path.dirname(parent), data_folder)+ "/*.dat")[0]
  999. pass
  1000. def get_ft_dat(parent):
  1001. pp = os.path.dirname(parent)
  1002. ppn = os.path.basename(parent).rstrip("-").rstrip("+")
  1003. #ppn = os.path.basename(parent)
  1004. tt = ppn.split("-")
  1005. if len(tt) > 2:
  1006. return glob(os.path.join(pp, tt[2] + "-" + tt[-1]) + "/*.dat")[0]
  1007. else:
  1008. t3 = tt[0].split("_F")
  1009. t4 = t3[1].split("_")
  1010. t5 = t4[-1].split("-")
  1011. if len(tt) == 2:
  1012. n = tt[-1]
  1013. else:
  1014. n = t5[-1]
  1015. if n == 'CLOSED':
  1016. n = '000'
  1017. return glob(os.path.join(pp, "F" + t4[0] + "-" + str(int(n))) + "/*.dat")[0]
  1018. def get_ft_bar(parent):
  1019. return os.path.join(os.path.dirname(get_ft_dat(parent)), "bar_position.mat")
  1020. def write_fictrac_stim_dFF_video(parent):
  1021. ft_name = get_ft_dat(parent)
  1022. ft_folder = os.path.dirname(ft_name)
  1023. ft_range_t, dff_range, ft_idx, dff_idx, ft_bar_info = json.load(open(parent + "/zscore.csv_time_info.txt", "r"))
  1024. m = load_memmap(glob(os.path.join(parent, "*.mmap"))[0])
  1025. m_color = m[dff_idx]
  1026. c = json.load(open(parent + "/ft_pv.txt", "r")) #50Hz, fictrac start +5s
  1027. t_range, heading, pv, pvl, speed, vx, vy, vx, pv2, pvl2 = c
  1028. pva = np.concatenate([[np.nan] * int((ft_range_t[0]+5)*50), pv]) # after proc_csv
  1029. names, points, contours = load_roi(parent + "/roi.npy", m[0].shape)
  1030. roi_n = len(contours) - 1 # 16 or 18
  1031. pv_contour_idx = ((pva*roi_n/(2*np.pi)) - 0.5).astype(int) + 1 # (0~16)->(0-15)+1
  1032. write_zscore_video(m_color, parent + "/v_color.avi", pv_contour_idx, contours)
  1033. # return
  1034. if not ft_bar_info:
  1035. df = pd.read_csv(ft_name, header=None).to_numpy().T
  1036. heading = 2*np.pi - df[16]
  1037. else:
  1038. heading = ft_bar_info
  1039. m_stim = []
  1040. w, h = 320, 160
  1041. barh = 5
  1042. for d in heading:
  1043. img = np.zeros((h, w), dtype=np.uint8)
  1044. mid = int(d * w / (2*np.pi))
  1045. img[:, mid - barh:mid + barh] = 255
  1046. if mid < barh:
  1047. img[:, 0 :mid + barh] = 255
  1048. img[:, w-(barh-mid):w] = 255
  1049. if mid + barh >= w:
  1050. img[:, mid:w] = 255
  1051. img[:,0:mid+barh-w] = 255
  1052. m_stim.append(img)
  1053. write_video(parent + "/v_stim.avi", np.array(m_stim)[ft_idx], fps=FICTRAC_RATE)
  1054. #embed_video(ft_folder + "/fictrac-debug.avi", parent + "/v_stim.avi", parent + "/v_color.avi", ft_range_t[0], ft_range_t[0])
  1055. embed_video(os.path.join(ft_folder,"fictrac-debug.avi"), os.path.join(parent,'v_stim.avi') , os.path.join(parent,'v_color.avi'), ft_range_t[0],
  1056. ft_range_t[0])
  1057. def realtime_heading_PVA(frames, heading, pv,parent):
  1058. filenames = []
  1059. for i in range(0,len(heading),200):
  1060. plt.figure(figsize=(30, 3))
  1061. if i > 100:
  1062. plt.plot(heading[i-100:i])
  1063. plt.plot(pv[i-100:i])
  1064. else:
  1065. plt.plot(heading[0:i])
  1066. plt.plot(pv[0:i])
  1067. filename = f'{i}.png'
  1068. filenames.append(filename)
  1069. plt.savefig(filename)
  1070. plt.close()
  1071. with imageio.get_writer('mygif.gif', mode='I') as writer:
  1072. for filename in filenames:
  1073. image = imageio.imread(filename)
  1074. writer.append_data(image)
  1075. fourcc = cv2.VideoWriter_fourcc('D', 'I', 'V', 'X')
  1076. video = cv2.VideoWriter(os.path.join(parent,'heading_pva.avi'), fourcc, 10, (3000,300))
  1077. for item in filenames:
  1078. img = cv2.imread(item)
  1079. video.write(img)
  1080. video.release()
  1081. for filename in set(filenames):
  1082. os.remove(filename)
  1083. pass
  1084. def plot_dFF(dff_csv, fr):
  1085. dFF = pd.read_csv(dff_csv)
  1086. names = dFF.columns
  1087. print(names)
  1088. plot_lines([dFF[n] for n in names], names)
  1089. plt.savefig(dff_csv + ".png")
  1090. plot_hot(dFF[dFF.keys()[1:]], dff_csv + "_hot", fr, is_PB=(dff_csv.find("-PB-") > 0))
  1091. # plot_lines([auto_cor(dFF[n][200:]) for n in names], names, ylim=(-0.2, 0.5), xlim_r=0.505)
  1092. # plt.savefig(parent + "/dFF_cor.png")
  1093. def get_dFF_by_baseline_range(F, baseline_range, bg_idx=0):
  1094. names = F.columns
  1095. bg = F[names[bg_idx]].copy()
  1096. for g in names:
  1097. F[g] = F[g] - bg
  1098. baseline = np.mean(F[g][baseline_range[0]:baseline_range[1]])
  1099. F[g] = (F[g] - baseline) / baseline
  1100. return F
  1101. def get_dFF(F, bg_correction = True):
  1102. names = F.columns
  1103. bg = F[names[0]]
  1104. for g in names[1:]:
  1105. if np.mean(bg) < np.mean(F[g]) + 5:
  1106. if bg_correction:
  1107. f = F[g] - bg
  1108. else:
  1109. f = F[g] - np.mean(bg)
  1110. else:
  1111. f = F[g]
  1112. f = f.mask(f < 0, np.nan)
  1113. baseline = np.nanmean(f[f <= np.nanpercentile(f, 5)]) # NOTE: F as the mean of the lower 5% (MaimonG_Nat17)
  1114. if baseline <= 1:
  1115. baseline = 1
  1116. print(g, "baseline", baseline)
  1117. F[g] = (f - baseline) / baseline
  1118. return F
  1119. def get_zscore(F):
  1120. from scipy.stats import zscore
  1121. zs = zscore(F, axis=0)
  1122. return pd.DataFrame(zs, columns=F.columns)
  1123. def zscore1(xs):
  1124. m, s = np.nanmean(xs), np.nanstd(xs)
  1125. return (xs - m) / s
  1126. def cov1(xs, ys):
  1127. n = len(xs)
  1128. mx, my = np.mean(xs), np.mean(ys)
  1129. return (xs - mx).dot(ys - my) / (n - 1)
  1130. def cor1(xs, ys):
  1131. n = len(xs)
  1132. return zscore1(xs).dot(zscore1(ys)) / (n - 1)
  1133. def cor2(xs, ys):
  1134. return cov1(xs, ys) / np.std(xs) / np.std(ys)
  1135. def cir_cor(xs, ys): # circstat circ_corrcc
  1136. mx, my = circmean(xs), circmean(ys)
  1137. num = np.sin(xs - mx).dot(np.sin(ys - my))
  1138. den = np.sqrt(np.sum(np.sin(xs - mx) ** 2) * np.sum(np.sin(ys - my) ** 2))
  1139. return num / den
  1140. def auto_cor(xs):
  1141. xm = xs - xs.mean()
  1142. xn = np.sum(xm ** 2)
  1143. return np.correlate(xm, xm, "same") / xn
  1144. def cor(x, y):
  1145. # return np.dot(x, y)/np.count_nonzero(y>0.5)
  1146. return np.corrcoef(x, y)[0][1]
  1147. def cor_equal_len(x, y):
  1148. if len(x) > len(y):
  1149. return cor(down_sample(x, len(y)), y)
  1150. else:
  1151. return cor(x, down_sample(y, len(x)))
  1152. def slide_cor(x, y, win, rate):
  1153. n = min(len(x), len(y))
  1154. rx, ry = [], []
  1155. for i in range(0, n - win, int(rate)):
  1156. c = cor(unwrap_dir(x[i:i + win]), unwrap_dir(y[i:i + win]))
  1157. rx.append(i + win/2.0)
  1158. ry.append(c)
  1159. return rx, ry
  1160. def down_sample(x, n):
  1161. x = np.array(x, dtype=np.float)
  1162. index_arr = np.linspace(0, len(x) - 1, num=n, dtype=np.float)
  1163. index_floor = np.array(index_arr, dtype=np.int)
  1164. index_ceil = index_floor + 1
  1165. index_rem = index_arr - index_floor
  1166. val1 = x[index_floor]
  1167. val2 = x[index_ceil % len(x)]
  1168. interp = val1 * (1.0 - index_rem) + val2 * index_rem
  1169. assert (len(interp) == n)
  1170. return interp
  1171. def view_img_seq(imgs, shifts_rig=None):
  1172. global g_total_frame, g_frame, g_is_input_begin, g_input_int
  1173. g_total_frame, h, w = imgs.shape
  1174. g_frame = 0
  1175. g_is_input_begin = False
  1176. g_input_int = 0
  1177. def plot_one_frame(f):
  1178. global g_frame
  1179. g_ax.cla()
  1180. g_ax.imshow(imgs[f].astype(int), cmap=plt.cm.gray, norm=NoNorm())
  1181. # g_ax.set_xlabel("%02d:%02.2f" % (t_sec / 60, t_sec % 60))
  1182. g_ax.set_xlabel(str(f))
  1183. if shifts_rig is not None:
  1184. g_ax.set_title(str(shifts_rig[g_frame]))
  1185. g_ax.grid(True)
  1186. g_ax.set_xticks(np.linspace(0, imgs[f].shape[1], 13))
  1187. g_frame = f
  1188. def on_slider(val):
  1189. plot_one_frame(int(val))
  1190. def onkey(event):
  1191. print(event.key)
  1192. global g_frame, g_is_input_begin, g_input_int
  1193. if event.key == "left":
  1194. g_frame -= 1
  1195. elif event.key == "right":
  1196. g_frame += 1
  1197. elif event.key == "enter":
  1198. g_frame = g_input_int
  1199. g_input_int = 0
  1200. elif event.key in list([*"1234567890"]):
  1201. g_input_int = int(event.key) + g_input_int * 10
  1202. print("input: %d" % g_input_int)
  1203. return
  1204. else:
  1205. g_input_int = 0
  1206. return
  1207. if g_frame >= g_total_frame:
  1208. g_frame = g_total_frame - 1
  1209. if g_frame < 0:
  1210. g_frame = 0
  1211. g_slider.set_val(g_frame)
  1212. event.canvas.draw()
  1213. from matplotlib.widgets import Slider
  1214. fig, g_ax = plt.subplots(figsize=(w/25, h/25))
  1215. plt.subplots_adjust(top=0.95, bottom=0.1)
  1216. g_slider = Slider(plt.axes([0.1, 0.03, 0.8, 0.03]), "", valmin=0, valmax=g_total_frame - 1, valfmt="%d", valinit=0)
  1217. g_slider.on_changed(on_slider)
  1218. plot_one_frame(0)
  1219. fig.canvas.mpl_connect('key_press_event', onkey)
  1220. def load_exp_xml(xml):
  1221. if not os.path.exists(xml):
  1222. return None
  1223. def get_value(ss, key):
  1224. p = ss.find(key + "=")
  1225. return ss[p:].split("\"")[1] if p >= 0 else None
  1226. s = open(xml, "r").readlines()
  1227. ret = {}
  1228. for ss in s:
  1229. if ss.find("ThorZPiezo") >= 0:
  1230. ret["steps"] = int(get_value(ss, "steps"))
  1231. ret["stepSizeUM"] = float(get_value(ss, "stepSizeUM"))
  1232. ret["startPos"] = float(get_value(ss, "startPos"))
  1233. elif ss.find("<Streaming") >= 0:
  1234. ret["frames"] = int(get_value(ss, "frames"))
  1235. ret["zFastEnable"] = int(get_value(ss, "zFastEnable"))
  1236. ret["flybackFrames"] = int(get_value(ss, "flybackFrames"))
  1237. elif ss.find("<LSM") >= 0:
  1238. ret["name"] = get_value(ss, "name")
  1239. ret["pixelX"] = int(get_value(ss, "pixelX"))
  1240. ret["pixelY"] = int(get_value(ss, "pixelY"))
  1241. ret["pixelSizeUM"] = float(get_value(ss, "pixelSizeUM"))
  1242. ret["frameRate"] = float(get_value(ss, "frameRate")) #167.569
  1243. return ret
  1244. def load_ft_bar(bar_name):
  1245. if not os.path.exists(bar_name):
  1246. return None
  1247. import scipy.io as sio
  1248. bar_pos = sio.loadmat(bar_name)["bar_position"][0]
  1249. p = np.min(np.nonzero(np.isnan(bar_pos)))
  1250. return (bar_pos[:p] - 64) / 1648 * 2 * np.pi # 64~1712
  1251. def write_video(path, m, cvt=cv2.COLOR_GRAY2BGR, fps=30, need_time=True, sti_img_frame = None):
  1252. h, w = m[0].shape[:2]
  1253. output_video = cv2.VideoWriter(path, cv2.VideoWriter_fourcc(*"DIVX"), fps, (w, h))
  1254. for i, mi in enumerate(m):
  1255. #img_bgr = cv2.cvtColor(mi, cvt)
  1256. img_bgr = mi
  1257. if need_time:
  1258. cv2.putText(img_bgr, format_time(i/fps), (6, 20), cv2.FONT_HERSHEY_PLAIN, 1, (255, 255, 0), 1)
  1259. if sti_img_frame is not None:
  1260. for j in range(len(sti_img_frame)):
  1261. if i>sti_img_frame[j][0] and i < sti_img_frame[j][1]:
  1262. img = np.ones((20, 20,3), dtype=np.uint8) * 255
  1263. img_bgr[0:20, 0:20, :] = img
  1264. pass
  1265. output_video.write(img_bgr)
  1266. output_video.release()
  1267. def embed_video(v1, v2, v3, t2, t3, offset2=(0, 320), offset3=(160, 320)):
  1268. cap1 = cv2.VideoCapture(v1)
  1269. cap2 = cv2.VideoCapture(v2)
  1270. cap3 = cv2.VideoCapture(v3)
  1271. n1 = int(cap3.get(cv2.CAP_PROP_FRAME_COUNT))
  1272. cap2.set(cv2.CAP_PROP_POS_FRAMES, t2 * FICTRAC_RATE)
  1273. cap3.set(cv2.CAP_PROP_POS_FRAMES, t3 * FICTRAC_RATE)
  1274. w, h = int(cap1.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cap1.get(cv2.CAP_PROP_FRAME_HEIGHT))
  1275. output_video = cv2.VideoWriter(v2 + "_combine.avi", cv2.VideoWriter_fourcc(*"DIVX"), FICTRAC_RATE, (w, h))
  1276. for seq in range(0, n1):
  1277. ret, img1 = cap1.read()
  1278. if not ret:
  1279. break
  1280. ret, img2 = cap2.read()
  1281. if not ret:
  1282. break
  1283. ret, img3 = cap3.read()
  1284. img3 = resize_image(img3,320,320)
  1285. if not ret:
  1286. break
  1287. h, w, c = img2.shape
  1288. img1[offset2[0]:offset2[0]+h, offset2[1]:offset2[1]+w, :] = img2
  1289. h, w, c = img3.shape
  1290. img1[offset3[0]:offset3[0]+h, offset3[1]:offset3[1]+w, :] = img3
  1291. output_video.write(img1)
  1292. output_video.release()
  1293. def resize_image(image, width, height,COLOUR=[0,0,0]):
  1294. h, w, layers = image.shape
  1295. if h > height:
  1296. ratio = height/h
  1297. image = cv2.resize(image,(int(image.shape[1]*ratio),int(image.shape[0]*ratio)))
  1298. h, w, layers = image.shape
  1299. if w > width:
  1300. ratio = width/w
  1301. image = cv2.resize(image,(int(image.shape[1]*ratio),int(image.shape[0]*ratio)))
  1302. h, w, layers = image.shape
  1303. if h < height and w < width:
  1304. hless = height/h
  1305. wless = width/w
  1306. if(hless < wless):
  1307. image = cv2.resize(image, (int(image.shape[1] * hless), int(image.shape[0] * hless)))
  1308. else:
  1309. image = cv2.resize(image, (int(image.shape[1] * wless), int(image.shape[0] * wless)))
  1310. h, w, layers = image.shape
  1311. if h < height:
  1312. df = height - h
  1313. df /= 2
  1314. image = cv2.copyMakeBorder(image, int(df), int(df), 0, 0, cv2.BORDER_CONSTANT, value=COLOUR)
  1315. if w < width:
  1316. df = width - w
  1317. df /= 2
  1318. image = cv2.copyMakeBorder(image, 0, 0, int(df), int(df), cv2.BORDER_CONSTANT, value=COLOUR)
  1319. image = cv2.resize(image,(width,height),interpolation=cv2.INTER_AREA)
  1320. return image
  1321. def merge_z_slices(fname, n, method=None):
  1322. import tifffile
  1323. with tifffile.TiffFile(fname) as tffl:
  1324. input_arr = tffl.asarray() # 25200*128*128
  1325. # write_video(fname + ".avi", norm_img(input_arr))
  1326. # plt.figure()
  1327. # a = input_arr[0].flatten()
  1328. # plt.hist(a[a>0.01], bins=50)
  1329. # plt.savefig(fname + "_hist.png")
  1330. chs = []
  1331. tn = int(len(input_arr) / n)
  1332. #fname_ch_list = []
  1333. for i in range(n):
  1334. ch0 = input_arr[i:tn*n:n]
  1335. # avg_frame = np.mean(ch0, axis=0)
  1336. # w = 255.0 / np.max(avg_frame)
  1337. # print(w)
  1338. eq = ch0 #* w*w
  1339. # eq[eq > 0] = 255
  1340. chs.append(eq)
  1341. #tifffile.imwrite(fname + "_ch%d.tif" % i, eq)
  1342. #fname_ch_list.append(fname + "_ch%d.tif" % i)
  1343. # write_video(fname + "_ch%d.avi"%i, norm_img(eq))
  1344. cv2.imwrite(fname + "_avg%d.png" % i, norm_img(np.mean(eq, axis=0)))
  1345. volu_arr = input_arr.reshape((np.shape(ch0)[0],np.shape(ch0)[1],np.shape(ch0)[2],n))
  1346. tifffile.imwrite(fname + "_ch_all.tif", volu_arr)
  1347. if method == "max":
  1348. avg_tif = np.max(chs, axis=0)
  1349. else:
  1350. avg_tif = np.mean(chs, axis=0)
  1351. tifffile.imwrite(fname + "_avg.tif", avg_tif)
  1352. return fname + "_avg.tif", fname + "_ch_all.tif"
  1353. def merge_info(date_folder):
  1354. info_l = []
  1355. for f in glob(date_folder + "/*/info.txt"):
  1356. c = json.load(open(f, "r"))
  1357. print(f, c)
  1358. exp_name = os.path.basename(os.path.dirname(f))
  1359. t = exp_name.split("-")
  1360. pair = "-".join(t[1:-1])
  1361. trail = t[-1]
  1362. stim = t[-2]
  1363. part = t[-3]
  1364. fly = t[2]
  1365. info_l.append([pair, fly, trail, part, stim, c["cor_unwrap"], c["cor_cir"]])
  1366. df = pd.DataFrame.from_records(info_l, columns=["pair", "fly", "trail", "part", "stim", "cor_unwrap", "cor_cir"])
  1367. import seaborn as sns
  1368. for x in ["cor_unwrap", "cor_cir"]:
  1369. plt.figure()
  1370. sns.set_theme(style="darkgrid")
  1371. sns.catplot(x=x, y="stim", hue="fly", col="part", data=df)
  1372. plt.xlim(-1, 1)
  1373. plt.tight_layout()
  1374. plt.savefig(date_folder + "/" + x)
  1375. def smooth_angle(s, win):
  1376. hw = int(win/2)
  1377. s1 = np.concatenate([[s[0]] * hw, s, [s[-1]] * hw])
  1378. ret = []
  1379. for i in range(len(s)):
  1380. m = circmean(s1[i:i+win])
  1381. if m > np.pi:
  1382. m -= np.pi * 2
  1383. ret.append(m)
  1384. return ret
  1385. def proc_ft_pv(fname):
  1386. c = json.load(open(fname, "r"))
  1387. t_range, heading, pv, pvl, speed, vx, vy, vx, pv2, pvl2 = c
  1388. n = len(heading)
  1389. ts = np.linspace(t_range[0], t_range[1], n)
  1390. # plot_lines(c[:7], "heading pv pvl speed vx vy vz".split())
  1391. pvs = smooth_angle(pv, 10)
  1392. pvs2 = smooth_angle(pv2, 10)
  1393. fig, axs = plt.subplots(3, 1, figsize=(8, 4), sharex=True, dpi=300)
  1394. # plt.subplots_adjust(left=0.02, right=0.99)
  1395. axs[0].plot(ts, unwrap_dir(np.array(heading)), c="m", lw=0.5)
  1396. axs[0].plot(ts, unwrap_dir(pvs), "k--", lw=0.5)
  1397. axs[0].plot(ts, unwrap_dir(pv), c="k", lw=0.5)
  1398. # axs[0].plot(ts, unwrap_dir(pvs2), c="gray", lw=0.5)
  1399. print(cor(unwrap_dir(np.array(heading)), unwrap_dir(pvs)))
  1400. # axs[0].plot(ts, unwrap_dir(pv2), c="gray", lw=0.5)
  1401. # axs[1].plot(ts, -np.array(heading), c="m", lw=0.5)
  1402. axs[1].plot(ts, pv, c="k", lw=0.5)
  1403. # axs[1].plot(ts, pvs, c="g", lw=0.5)
  1404. axs[1].plot(ts, pv2, c="gray", lw=0.5)
  1405. axs[2].plot(ts, zscore1(speed), c="m", lw=0.5)
  1406. axs[2].plot(ts, zscore1(pvl), c="k", lw=0.5)
  1407. axs[2].plot(ts, zscore1(pvl2), c="gray", lw=0.5)
  1408. plt.tight_layout()
  1409. # plt.show()
  1410. plt.savefig(fname + ".png")
  1411. """
  1412. // frame_count
  1413. ss << _cnt << ", ";
  1414. // rel_vec_cam[3] | error
  1415. ss << _dr_cam[0] << ", " << _dr_cam[1] << ", " << _dr_cam[2] << ", " << _err << ", ";
  1416. // rel_vec_world[3]
  1417. ss << _dr_lab[0] << ", " << _dr_lab[1] << ", " << _dr_lab[2] << ", ";
  1418. // abs_vec_cam[3]
  1419. ss << _r_cam[0] << ", " << _r_cam[1] << ", " << _r_cam[2] << ", ";
  1420. // abs_vec_world[3]
  1421. ss << _r_lab[0] << ", " << _r_lab[1] << ", " << _r_lab[2] << ", ";
  1422. // integrated xpos | integrated ypos | integrated heading
  1423. ss << _posx << ", " << _posy << ", " << _heading << ", ";
  1424. // direction (radians) | speed (radians/frame)
  1425. ss << _step_dir << ", " << _step_mag << ", ";
  1426. // integrated x movement | integrated y movement (mouse output equivalent)
  1427. ss << _intx << ", " << _inty << ", ";
  1428. // timestamp | sequence number
  1429. ss << _ts << ", " << _seq << std::endl;
  1430. """
  1431. if __name__ == '__main__':
  1432. ft, pv = json.load(open(r"D:\exp_2p\data\EPG\210823\CX1001-7F-F6-PB-reverseCLOSED-7+\ft_pv.txt", "r"))
  1433. print(cor(ft, pv))
  1434. print(cor(unwrap_dir(ft), unwrap_dir(pv)))
  1435. print(cir_cor(ft, pv))

cia_utils.py at commit 9c55e7a, no license · at the source

Overview

Authors: Qiantao Lv1,2,3,4, Yunming Gao2,3,4, Jing Ning2,3,4, Yi Sun1,2,3,4
ORCID iDs: Yi Sun
  1. Zhejiang University, Hangzhou, Zhejiang 310058, China
  2. Westlake Laboratory of Life Sciences and Biomedicine, Hangzhou, Zhejiang 310024, China
  3. Key Laboratory of Growth Regulation and Translational Research of Zhejiang Province, School of Life Sciences, Westlake University, Hangzhou, Zhejiang 310030, China
  4. Institute of Basic Medical Sciences, Westlake Institute for Advanced Study, Hangzhou, Zhejiang 310024, China
Institutions: Westlake University (China); Zhejiang University (China)
Journal: STAR protocols, volume 7, issue 3, article 104659
Dates: published online 25 June 2026; in print June 2026
Type: Research article · Language: English
License: CC BY-NC-ND
Identifiers: DOI 10.1016/j.xpro.2026.104659 · PMID 42348421 · PMCID PMC13333336 · OpenAlex W7165931131
Open access: gold, a free copy (OpenAlex)
Status: code verified
Methods: Spectral & time-frequency, Connectivity, Statistics, fMRI & imaging, Single-unit activity, calcium imaging, Smoothing, state filtering, decompositions
Keywords: Microscopy, Model Organisms, Neuroscience
Topic: Neurobiology and Insect Physiology Research (Cellular and Molecular Neuroscience, Neuroscience), according to OpenAlex
Funding: Westlake University (WU2022C011); Westlake Laboratory of Life Sciences and Biomedicine (W101386022101); Westlake Education Foundation
Citations: not cited yet (Europe PMC); 15 references in the paper
Research resources: Python RRID:SCR_008394, Illustrator RRID:SCR_010279

Abstract

The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.

Repository

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

SunLabWestlake/cia

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 9c55e7ac2ebd9ea6b11ccb8a013fa9d76119f856, 4 June 2026
Languages: Python (7)
Size: 10 files, 7 scripts
Software Heritage: not archived
Found in: “Data and code availability”
Holds: README, environment (requirements.txt)
Not found: license file, CITATION.cff, tests, continuous integration, documentation
Tools: Matplotlib (5 files), NumPy (5 files), pandas (5 files), h5py (3 files), OpenCV (3 files), SciPy (3 files), CaImAn (2 files), imageio (1 file), Pillow (1 file), seaborn (1 file), tifffile (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
9 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:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 7 scripts, each with its path and the digest of its content;
  • 2 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

No dataset and no data link were found in the paper.

Code and data availability statement

The paper has a code and data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

Read it in the paper: doi.org/10.1016/j.xpro.2026.104659.

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, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 4 authors, 3 keywords, 3 funders, 15 references, 2 RRIDs.

Cite

This paper

Lv, Q., Gao, Y., Ning, J., & Sun, Y. (2026). Protocol for simultaneous in vivo two-photon imaging and locomotion quantification during olfactory stimulation and pharmacology in walking Drosophila. STAR protocols, 7(3), 104659. https://doi.org/10.1016/j.xpro.2026.104659

BibTeX

@article{lv2026protocol,
author = {Lv, Qiantao and Gao, Yunming and Ning, Jing and Sun, Yi},
title = {{Protocol for simultaneous in vivo two-photon imaging and locomotion quantification during olfactory stimulation and pharmacology in walking Drosophila}},
journal = {STAR protocols},
year = {2026},
month = jun,
volume = {7},
number = {3},
pages = {104659},
publisher = {Elsevier},
issn = {2666-1667},
doi = {10.1016/j.xpro.2026.104659},
url = {https://doi.org/10.1016/j.xpro.2026.104659},
pmid = {42348421},
pmcid = {PMC13333336}
}

RIS

TY - JOUR
AU - Lv, Qiantao
AU - Gao, Yunming
AU - Ning, Jing
AU - Sun, Yi
TI - Protocol for simultaneous in vivo two-photon imaging and locomotion quantification during olfactory stimulation and pharmacology in walking Drosophila
T2 - STAR protocols
J2 - STAR Protoc
PY - 2026
DA - 2026/06/25
VL - 7
IS - 3
SP - 104659
SN - 2666-1667
PB - Elsevier
DO - 10.1016/j.xpro.2026.104659
UR - https://doi.org/10.1016/j.xpro.2026.104659
LA - en
ER -

CSL-JSON

{
"id": "10.1016/j.xpro.2026.104659",
"type": "article-journal",
"title": "Protocol for simultaneous in vivo two-photon imaging and locomotion quantification during olfactory stimulation and pharmacology in walking Drosophila",
"container-title": "STAR protocols",
"author": [
{
"family": "Lv",
"given": "Qiantao"
},
{
"family": "Gao",
"given": "Yunming"
},
{
"family": "Ning",
"given": "Jing"
},
{
"family": "Sun",
"given": "Yi"
}
],
"container-title-short": "STAR Protoc",
"volume": "7",
"issue": "3",
"page": "104659",
"DOI": "10.1016/j.xpro.2026.104659",
"PMID": "42348421",
"PMCID": "PMC13333336",
"ISSN": "2666-1667",
"publisher": "Elsevier",
"URL": "https://doi.org/10.1016/j.xpro.2026.104659",
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
25
]
]
}
}

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.1038/s41467-026-72437-1 [code]
High-speed whole-brain imaging in Drosophila.
Journal: Nature communications
In common: OpenCV, h5py, Pillow, 4 other tools, drosophila, 6 references
[2] 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: CaImAn, tifffile, OpenCV, 6 other tools, optical imaging (calcium, voltage, 2-photon), 2 references
[3] doi:10.1126/sciadv.adv3770 [code]
Evolution of a central dopamine circuit underlies adaptation of a light-evoked sensorimotor response in the blind cavefish.
Journal: Science advances
In common: CaImAn, tifffile, OpenCV, 7 other tools, 1 reference
[4] doi: [code]
Real-time closed-loop feedback system for mouse mesoscale cortical signal and movement control
Journal: eLife
In common: imageio, tifffile, OpenCV, 7 other tools, optical imaging (calcium, voltage, 2-photon)
[5] doi:10.1038/s41467-026-72709-w [code]
An epifluorescence microscope design for naturalistic behavior and cellular activity in freely moving Caenorhabditis elegans.
Journal: Nature communications
In common: imageio, tifffile, OpenCV, 7 other tools, histology / microscopy
[6] 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), 2 references
[7] doi:10.1364/boe.605322 [code]
Generalized plaque digitization framework for multi-dimensional mesoscopic images.
Journal: Biomedical optics express
In common: imageio, tifffile, OpenCV, 7 other tools
[8] doi:10.7554/elife.109717 [code]
Retrosplenial cortex enables context-dependent goal-directed sensorimotor transformation.
Journal: eLife
In common: imageio, tifffile, OpenCV, 7 other tools
[9] doi:10.1016/j.xcrm.2026.102766 [code]
A longitudinal single-cell and spatial multiomic atlas of pediatric high-grade glioma.
Journal: Cell reports. Medicine
In common: imageio, tifffile, OpenCV, 7 other tools
[10] doi:10.1038/s41598-026-57519-w [code]
Automated segmentation of neurons and spinal cord structures in immunofluorescence images using SpineDL.
Journal: Scientific reports
In common: imageio, tifffile, OpenCV, 6 other tools, histology / microscopy

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.