A transparent wheel-based platform for locomotion-on-demand and multi-view body and facial kinematics in head-fixed mice.
The 19 matches · 1 of them tie a paragraph to a whole file, not to given lines: a weak match, whose lines are not tinted
- [1] § Methods › Data analysis › Video preprocessing ↔ Python/reduce_video_dimensions.py, lines 5–49 · score 0.81 · INTER_AREA, OpenCV, Resized videos, dimension, downscaling, quality
- [2] § Methods › Data analysis › Video preprocessing ↔ Python/notebooks/optical_flow.ipynb, lines 111–198 · score 0.79 · INTER_AREA, Resized videos, pupil video, optical flow, OpenCV, suffix
- [3] § Methods › Data analysis › Optical flow and motion-energy extraction ↔ Python/optical_flow.py, lines 125–202 · score 0.79 · avg_u, avg_v, optical flow, motion energy, OpenCV, timestamp
- [4] § Methods › Data analysis › Determination of eye/pupil area ↔ Matlab/load_eye_pupil_signal.m, the whole file · a weak match · score 0.77 · eye pupil roi, eye pupil signal, pupil area, largest, hole, calibration
- [5] § Methods › Data analysis › Optical flow and motion-energy extraction ↔ Python/src/utils/motion_analysis.py, lines 181–247 · score 0.75 · avg_u, avg_v, motion energy, OpenCV, timestamp, pose
- [6] § Results › Air-induced locomotion produces reliable paw kinematics on a transparent wheel ↔ Matlab/fig_dlc.m, lines 100–183 · score 0.71 · hind left, hind right, front left, front right, tail, nose
- [7] § Results › Air-induced locomotion produces reliable paw kinematics on a transparent wheel ↔ Matlab/fig_dlc_n3.m, lines 17–130 · score 0.70 · hind left, hind right, front left, front right, tail, nose
- [8] § Methods › Data analysis › LED intensity extraction for LED-based synchronization of video streams ↔ Matlab/fig_montage_n3.m, lines 257–387 · score 0.61 · nearest DAQ sample, video frame, divergence, windows, alignment, events
- [9] § Results › LED-based synchronization enables alignment of air stimulus across video streams ↔ Matlab/fig_montage_n3.m, lines 257–387 · score 0.60 · nearest DAQ sample, video frame, anchoring, divergence, windows, alignment
- [10] § Results › LED-based synchronization enables alignment of air stimulus across video streams ↔ Matlab/fig_montage.m, lines 135–172 · score 0.58 · paws camera, air state, video stream, montages, synchronization, alignment
- [11] § Results › Facial and eye dynamics during air-induced locomotion ↔ Matlab/fig_motion_energy_n3.m, lines 13–127 · score 0.56 · Optical flow, motion energy, LED signal, air onset, SEM, cm
- [12] § Methods › Data analysis › ROI selection and metadata ↔ Python/spyder_extract_led_signal.py, lines 13–86 · score 0.54 · interactive ROI, OpenCV, selection, frame, video
- [13] § Methods › Data analysis › Determination of eye/pupil area ↔ Matlab/fig_pupil.m, lines 17–94 · score 0.54 · pupil area, largest, hole, calibration, segmented, rectangular
- [14] § Results › Air-induced locomotion produces reliable paw kinematics on a transparent wheel ↔ Matlab/fig_motion_energy.m, lines 12–104 · score 0.54 · left axis, right axis, motion energy, box, offset, LED
- [15] § Results › Facial and eye dynamics during air-induced locomotion ↔ Matlab/fig_motion_energy.m, lines 12–104 · score 0.53 · left axis, right axis, motion energy, box, offset, LED
- [16] § Methods › Data analysis › ROI selection and metadata ↔ Python/fig_show_roi.py, lines 6–66 · score 0.53 · roi.json, OpenCV, selection, rectangular, pupil, frame
- [17] § Results › Air-induced locomotion produces reliable paw kinematics on a transparent wheel ↔ Matlab/fig_dlc.m, lines 59–98 · score 0.51 · Tracking reliability, Bar graphs, CDFs, cumulative, likelihood, frame
- [18] § Results › Air-induced locomotion produces reliable paw kinematics on a transparent wheel ↔ Matlab/fig_motion_energy_n3.m, lines 13–127 · score 0.51 · optical flow speeds, motion energy, air onset, SEM, paw, animals
- [19] § Results › Air-induced locomotion produces reliable paw kinematics on a transparent wheel ↔ Matlab/fig_dlc_F.m, lines 76–100 · score 0.50 · Tracking reliability, Bar graphs, CDFs, cumulative, likelihood, frame
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
MATLAB · 673 lines · 21 KB · no license · 2 matches
- function fig_dlc
- % vp = evalin('base','vp');
- %
- % vp_l = evalin('base','vp_labeled');
- % vf = evalin('base','vf');
- % v = evalin('base','v');
- mD = evalin('base','mData'); colors = mD.colors; sigColor = mD.sigColor; axes_font_size = mD.axes_font_size;
- mData = mD;
- animal = evalin('base','animals(1)');
- % animal = evalin('base','fanimal');
- % physical_dist_cm = 5.0;
- % pixel_dist_px = 200; % Measure this from a still frame using 'imdistline'
- px_to_cm = 0.0114;
- n = 0;
- %%
- %% ---------- Load and Clean DLC Data ----------
- % Using the path structure you provided
- file_path = fullfile(animal(1).pdir,'video_20251216_165824DLC_resnet50_gcamp16declimbDec18shuffle1_185000_filtered.csv');
- % Set up import options to handle the triple-header (scorer, bodyparts, coords)
- opts = detectImportOptions(file_path);
- % Start reading from row 4 where the numeric data begins
- opts.DataLines = [4, Inf];
- opts.VariableNamingRule = 'preserve';
- tbl = readtable(file_path, opts);
- % Mapping likelihood columns based on your CSV image:
- % Column D=4 (Front Right), G=7 (Front Left), J=10 (Hind Right),
- % M=13 (Hind Left), P=16 (Tail Base), S=19 (Nose)
- lik_indices = [4, 7, 10, 13, 16, 19];
- bodyparts = {'Front Right', 'Front Left', 'Hind Right', 'Hind Left', 'Tail Base', 'Nose'};
- % Extract likelihood matrix [40748 frames x 6 bodyparts]
- likelihoods = table2array(tbl(:, lik_indices));
- %% ---------- Calculate Quality Metrics ----------
- p_thresh = 0.9;
- % Percentage of frames where p > 0.9 for each body part
- percent_high_conf = sum(likelihoods > p_thresh, 1) / size(likelihoods, 1) * 100;
- %% ---------- Updated Bodypart Colors (RGB) ----------
- % These values are sampled directly from the markers in your images:
- % Front Right (Blue/Indigo), Front Left (Sky Blue), Hind Right (Teal),
- % Hind Left (Bright Green), Tail Base (Orange), Nose (Red)
- custom_colors = [
- 0.20, 0.00, 1.00; % Front Right (Dark Blue/Indigo)
- 0.00, 0.60, 1.00; % Front Left (Sky Blue)
- 0.00, 0.80, 0.75; % Hind Right (Teal)
- 0.60, 1.00, 0.40; % Hind Left (Bright Green)
- 1.00, 0.60, 0.00; % Tail Base (Orange)
- 1.00, 0.00, 0.00 % Nose (Red)
- ];
- %% ---------- PANEL C: DLC Quality Analysis ----------
- magfac = mD.magfac;
- ff = makeFigureRowsCols(110, [3 5 4 1.5], 'RowsCols', [1 2], ...
- 'spaceRowsCols', [0.05 0.1], 'rightUpShifts', [0.1 0.1]);
- % --- C1: Bar Graph (% Frames > 0.9) ---
- subplot(1,2,1);
- hold on;
- for i = 1:6
- % Plot each bar individually to apply specific color
- bar(i, percent_high_conf(i), 'FaceColor', custom_colors(i,:), 'EdgeColor', 'none');
- end
- set(gca, 'XTick', 1:6, 'XTickLabel', bodyparts, 'XTickLabelRotation', 45);
- ylabel('% Frames (p > 0.9)');
- ht = title('DLC Tracking Reliability'); set(ht,'FontWeight','Normal');
- ylim([0 105]);
- box off;
- format_axes(gca);
- % --- C2: Likelihood CDF (Multi-line) ---
- subplot(1,2,2);
- hold on;
- for i = 1:6
- [f, x] = ecdf(likelihoods(:, i));
- plot(x, f, 'LineWidth', 2, 'Color', custom_colors(i,:));
- end
- % Reference line at 0.9 threshold
- line([p_thresh p_thresh], [0 1], 'Color', [0.5 0.5 0.5], 'LineStyle', '--', 'LineWidth', 1);
- xlabel('Likelihood');
- ylabel('Cumulative Probability');
- % legend(bodyparts, 'Location', 'northwest', 'FontSize', 7, 'Box', 'off');
- ht = title('Likelihood CDF'); set(ht,'FontWeight','Normal');
- grid on;
- box off;
- format_axes(gca);
- % Save to your designated PDF folder
- save_pdf(ff.hf, mD.pdf_folder, 'DLC_Quality_Analysis_Colored.pdf', 600);
- %% ---------- Setup & Data Loading ----------
- file_path = fullfile(animal(1).pdir, 'video_20251216_165824DLC_resnet50_gcamp16declimbDec18shuffle1_185000_filtered.csv');
- opts = detectImportOptions(file_path);
- opts.DataLines = [4, Inf];
- opts.VariableNamingRule = 'preserve';
- tbl = readtable(file_path, opts);
- % Define indices for X and Y based on your CSV structure
- % x: Col 2, 5, 8, 11, 14, 17 | y: Col 3, 6, 9, 12, 15, 18
- x_idx = [2, 5, 8, 11, 14, 17];
- y_idx = [3, 6, 9, 12, 15, 18];
- likelihood_idx = [4, 7, 10, 13, 16, 19];
- bodyparts = {'Front Right', 'Front Left', 'Hind Right', 'Hind Left', 'Tail Base', 'Nose'};
- % ---------- 1. Re-establish Time Base ----------
- N_sig = 40746;
- % If you have your sampling frequency (e.g., 30 fps or 60 fps)
- % If unknown, we can infer it if 'b.fs' exists from your earlier code
- if exist('b','var') && isfield(b,'fs')
- fs = b.fs;
- else
- fs = 60; % Defaulting to 30 fps; change this to your actual video FPS
- end
- fs = animal(1).video.specs.paws.fps;
- % Create time vector in seconds
- time_sec = (0:N_sig-1)' / fs;
- tmin = time_sec / 60; % Convert to minutes for plotting
- % Extract and truncate to match your signal time base (40745 samples)
- N_target = length(time_sec);
- X_coords = table2array(tbl(1:N_target, x_idx));
- Y_coords = table2array(tbl(1:N_target, y_idx));
- L = table2array(tbl(1:N_target, likelihood_idx));
- % Use the custom colors from the mouse markers
- custom_colors = [
- 0.20, 0.00, 1.00; % Front Right (Blue/Indigo)
- 0.00, 0.60, 1.00; % Front Left (Sky Blue)
- 0.00, 0.80, 0.75; % Hind Right (Teal)
- 0.60, 1.00, 0.40; % Hind Left (Bright Green)
- 1.00, 0.60, 0.00; % Tail Base (Orange)
- 1.00, 0.00, 0.00 % Nose (Red)
- ];
- % ---------- Likelihood filtering ----------
- lik_thresh = 0.9;
- low_conf = L < lik_thresh;
- X_coords(low_conf) = NaN;
- Y_coords(low_conf) = NaN;
- % ---------- Interpolate missing values ----------
- X_coords = fillmissing(X_coords,'linear');
- Y_coords = fillmissing(Y_coords,'linear');
- % ---------- No Smoothing ----------
- X_coordsS = X_coords;
- Y_coordsS = Y_coords;
- % ---------- Smooth coordinates ----------
- X_coordsS = movmedian(X_coords,5);
- Y_coordsS = movmedian(Y_coords,5);
- % X_coordsS = movmean(X_coordsS,5);
- % Y_coordsS = movmean(Y_coordsS,5);
- % Convert coordinates to cm
- X_cm = X_coordsS * px_to_cm;
- Y_cm = Y_coordsS * px_to_cm;
- fs = 60;
- % Compute velocity
- Vx = [zeros(1,6); diff(X_cm) * fs];
- Vy = [zeros(1,6); diff(Y_cm) * fs];
- % remove impossible spikes
- max_speed = 100; % cm/s
- Vx(abs(Vx) > max_speed) = NaN;
- Vy(abs(Vy) > max_speed) = NaN;
- %%
- T = animal(1).b.led_sig.paws;
- % t_paws = T.t_led/60;
- air_paws = double(T.is_on);
- %% ---------- Plotting: Trajectories vs Time ----------
- magfac = mD.magfac;
- tmin = time_sec / 60; % Time in minutes for the x-axis
- ff = makeFigureRowsCols(111, [3 5 6.9 1.5], 'RowsCols', [1 2], ...
- 'spaceRowsCols', [0.1 5], 'rightUpShifts', [0.1 0.2],'widthHeightAdjustment',[0 -300]);
- MY = 1920; ysp = 0.15285; mY = -2.5; titletxt = ''; ylabeltxt = {'PDF'}; % for all cells (vals) MY = 80
- stp = 0.35*magfac; widths = [3.12 3.12 2.85 1]*magfac; gap = 0.25*magfac;
- adjust_axes(ff,[mY MY],stp,widths,gap,{''});
- % axes_title_shifts_line = [0 0.55 0 0]; axes_title_shifts_text = [0.02 0.1 0 0]; xs_gaps = [1 2];
- % --- Subplot 1: X-Coordinates vs Time ---
- axes(ff.h_axes(1,1))
- hold on;
- for i = 1:6
- miny(i) = min(X_coords(:,i)); maxy(i) = max(X_coords(:,i));
- plot(tmin, X_coords(:,i), 'Color', custom_colors(i,:), 'LineWidth', 0.25);
- end
- % ylim([200 1400])
- ylims = ylim;
- plot(t_paws,air_paws*ylims(2),'k','LineWidth',0.25)
- ylabel('X Pixel Value');xlabel('Time (min)');
- % title('Horizontal Position (X) over Time');
- % legend(bodyparts, 'Location', 'eastoutside', 'FontSize', 7, 'Box', 'off');
- % box off; format_axes(gca);
- xlim([0 3]);
- onsets = find_rising_edge(air_paws,0.5,-1);
- offsets = find_falling_edge(air_paws,-0.5,1);
- ylims = ylim;
- [TLx TLy] = ds2nfu(time_sec(onsets(1)),ylims(2)-0);
- % axes(ff.h_axes(1,1));ylims = ylim;
- [BLx BLy] = ds2nfu(time_sec(onsets(1)),ylims(1));
- aH = (TLy - BLy);
- len = sum(find(time_sec(onsets)<3,1,'last'));
- for ii = 1:len%gth(onsets)
- [BRx BRy] = ds2nfu(time_sec(offsets(ii)),ylims(1));
- [BLx BLy] = ds2nfu(time_sec(onsets(ii)),ylims(1));
- aW = (BRx-BLx);
- annotation('rectangle',[BLx BLy aW aH],'facealpha',0.2,'linestyle','none','facecolor','k');
- end
- box off
- format_axes(gca)
- % --- Subplot 2: Y-Coordinates vs Time ---
- axes(ff.h_axes(1,2))
- hold on;
- for i = 1:6
- plot(tmin, Y_coords(:,i), 'Color', custom_colors(i,:), 'LineWidth', 0.25);
- end
- % ylim([10 1000])
- ylims = ylim;
- plot(t_paws,air_paws*ylims(2),'k','LineWidth',0.25)
- ylabel('Y Pixel Value');
- xlabel('Time (min)');
- % title('Vertical Position (Y) over Time');
- % box off; format_axes(gca);
- xlim([0 3]);
- onsets = find_rising_edge(air_paws,0.5,-1);
- offsets = find_falling_edge(air_paws,-0.5,1);
- ylims = ylim;
- [TLx TLy] = ds2nfu(time_sec(onsets(1)),ylims(2)-0);
- % axes(ff.h_axes(1,1));ylims = ylim;
- [BLx BLy] = ds2nfu(time_sec(onsets(1)),ylims(1));
- aH = (TLy - BLy);
- len = sum(find(time_sec(onsets)<3,1,'last'));
- for ii = 1:len%gth(onsets)
- [BRx BRy] = ds2nfu(time_sec(offsets(ii)),ylims(1));
- [BLx BLy] = ds2nfu(time_sec(onsets(ii)),ylims(1));
- aW = (BRx-BLx);
- annotation('rectangle',[BLx BLy aW aH],'facealpha',0.2,'linestyle','none','facecolor','k');
- end
- box off
- format_axes(gca)
- % Save the trajectory plot
- save_pdf(ff.hf, mD.pdf_folder, 'DLC_Trajectories.pdf', 600);
- %% ---------- Plotting: Inst Vel vs Time ----------
- magfac = mD.magfac;
- tmin = time_sec / 60; % Time in minutes for the x-axis
- ff = makeFigureRowsCols(111, [3 5 6.9 1.5], 'RowsCols', [1 2], ...
- 'spaceRowsCols', [0.1 0.061], 'rightUpShifts', [0.051 0.2],'widthHeightAdjustment',[-60 -300]);
- MY = 1920; ysp = 0.15285; mY = -2.5; titletxt = ''; ylabeltxt = {'PDF'}; % for all cells (vals) MY = 80
- stp = 0.1*magfac; widths = [3.12 3.12 2.85 1]*magfac; gap = 0.25*magfac;
- % adjust_axes(ff,[mY MY],stp,widths,gap,{''});
- % axes_title_shifts_line = [0 0.55 0 0]; axes_title_shifts_text = [0.02 0.1 0 0]; xs_gaps = [1 2];
- % --- Subplot 1: X-Coordinates vs Time ---
- axes(ff.h_axes(1,1))
- hold on;
- for i = 1:6
- miny(i) = min(Vx(:,i)); maxy(i) = max(Vx(:,i));
- plot(tmin, Vx(:,i), 'Color', custom_colors(i,:), 'LineWidth', 0.25);
- end
- ylim([min(miny) max(maxy)])
- ylims = ylim;
- % plot(t_paws,air_paws*ylims(2),'k','LineWidth',0.25)
- ylabel('Vx (cm/s)');xlabel('Time (min)');
- % title('Horizontal Position (X) over Time');
- % legend(bodyparts, 'Location', 'eastoutside', 'FontSize', 7, 'Box', 'off');
- % box off; format_axes(gca);
- xlim([0 3]);
- onsets = find_rising_edge(air_paws,0.5,-1);
- offsets = find_falling_edge(air_paws,-0.5,1);
- ylims = ylim;
- [TLx TLy] = ds2nfu(tmin(onsets(1)),ylims(2)-0);
- % axes(ff.h_axes(1,1));ylims = ylim;
- [BLx BLy] = ds2nfu(tmin(onsets(1)),ylims(1));
- aH = (TLy - BLy);
- len = sum(find(tmin(onsets)<3,1,'last'));
- for ii = 1:len%gth(onsets)
- [BRx BRy] = ds2nfu(tmin(offsets(ii)),ylims(1));
- [BLx BLy] = ds2nfu(tmin(onsets(ii)),ylims(1));
- aW = (BRx-BLx);
- annotation('rectangle',[BLx BLy aW aH],'facealpha',0.2,'linestyle','none','facecolor','k');
- end
- box off
- format_axes(gca)
- % --- Subplot 2: Y-Coordinates vs Time ---
- axes(ff.h_axes(1,2))
- hold on;
- for i = 1:6
- minyy(i) = min(Vy(:,i)); maxyy(i) = max(Vy(:,i));
- plot(tmin, Vy(:,i), 'Color', custom_colors(i,:), 'LineWidth', 0.25);
- end
- ylim([min(minyy) max(maxyy)])
- ylims = ylim;
- % plot(t_paws,air_paws*ylims(2),'k','LineWidth',0.25)
- ylabel('Vy (cm/s)');
- xlabel('Time (min)');
- % title('Vertical Position (Y) over Time');
- % box off; format_axes(gca);
- xlim([0 3]);
- onsets = find_rising_edge(air_paws,0.5,-1);
- offsets = find_falling_edge(air_paws,-0.5,1);
- ylims = ylim;
- [TLx TLy] = ds2nfu(tmin(onsets(1)),ylims(2)-0);
- % axes(ff.h_axes(1,1));ylims = ylim;
- [BLx BLy] = ds2nfu(tmin(onsets(1)),ylims(1));
- aH = (TLy - BLy);
- len = sum(find(tmin(onsets)<3,1,'last'));
- for ii = 1:len%gth(onsets)
- [BRx BRy] = ds2nfu(tmin(offsets(ii)),ylims(1));
- [BLx BLy] = ds2nfu(tmin(onsets(ii)),ylims(1));
- aW = (BRx-BLx);
- annotation('rectangle',[BLx BLy aW aH],'facealpha',0.2,'linestyle','none','facecolor','k');
- end
- box off
- format_axes(gca);
- % axes(ff.h_axes(1,1))
- % ylim([min([miny minyy]) max([maxy maxyy])])
- % axes(ff.h_axes(1,2))
- % ylim([min([miny minyy]) max([maxy maxyy])])
- % Save the trajectory plot
- save_pdf(ff.hf, mD.pdf_folder, 'DLC_Trajectories.pdf', 600);
- %% ---------- 1. Calculate Speeds for All Body Parts ----------
- % Ensure fs is defined (sampling rate)
- if ~exist('fs','var'), fs = 60; end
- dt = 1/fs;
- % Initialize speed matrix [Frames x 6 bodyparts]
- DLC_speeds = nan(size(X_coords));
- for i = 1:6
- % Calculate velocity (difference between frames)
- dx = diff(X_coords(:,i)) * fs; % Change in pixels per second
- dy = diff(Y_coords(:,i)) * fs;
- % Collapse X and Y into Speed (Magnitude of Velocity Vector)
- % We pad with a NaN at the start to maintain vector length
- DLC_speeds(2:end, i) = sqrt(dx.^2 + dy.^2);
- end
- % physical_dist_cm = 5.0;
- % pixel_dist_px = 200; % Measure this from a still frame using 'imdistline'
- px_to_cm = 0.0114;
- % Update the speeds by multiplying pixels by the calibration factor
- DLC_speeds_cm = DLC_speeds * px_to_cm;
- %% ---------- 2. Plot: Speeds vs Time (Panel F Style) ----------
- magfac = mD.magfac;
- tmin = time_sec / 60;
- magfac = mD.magfac;
- ff = makeFigureRowsCols(107,[3 5 6.5 1.5],'RowsCols',[1 1],'spaceRowsCols',[0.01 -0.02],'rightUpShifts',[0.2 0.22],'widthHeightAdjustment',[10 -250]);
- MY = 100; ysp = 0.15285; mY = -2.5; titletxt = ''; ylabeltxt = {'PDF'}; % for all cells (vals) MY = 80
- stp = 0.35*magfac; widths = [6.4 1 2.85 1]*magfac; gap = 0.115*magfac; adjust_axes(ff,[mY MY],stp,widths,gap,{''});
- axes_title_shifts_line = [0 0.55 0 0]; axes_title_shifts_text = [0.02 0.1 0 0]; xs_gaps = [1 2];
- % --- Subplot 1: Individual Speed Traces ---
- axes(ff.h_axes(1,1))
- hold on;
- for i = 1:6
- plot(tmin, DLC_speeds_cm(:,i), 'Color', custom_colors(i,:), 'LineWidth', 0.8);
- end
- ylabel('Speed (pixels/s)');
- title('DLC Speed: All Body Parts');
- % legend(bodyparts, 'Location', 'eastoutside', 'FontSize', 7, 'Box', 'off');
- box off; format_axes(gca);
- ylim([0 200]);xlim([0 3]);
- ylims = ylim;
- plot(t_paws,air_paws*ylims(2),'k')
- onsets = find_rising_edge(air_paws,0.5,-1);
- offsets = find_falling_edge(air_paws,-0.5,1);
- xlims = xlim;
- [TLx TLy] = ds2nfu(time_sec(onsets(1)),ylims(2)-0);
- % axes(ff.h_axes(1,1));ylims = ylim;
- [BLx BLy] = ds2nfu(time_sec(onsets(1)),ylims(1));
- aH = (TLy - BLy);
- len = sum(find(time_sec(onsets)<xlims(2),1,'last'));
- for ii = 1:len%gth(onsets)
- [BRx BRy] = ds2nfu(time_sec(offsets(ii)),ylims(1));
- [BLx BLy] = ds2nfu(time_sec(onsets(ii)),ylims(1));
- aW = (BRx-BLx);
- annotation('rectangle',[BLx BLy aW aH],'facealpha',0.2,'linestyle','none','facecolor','k');
- end
- box off
- format_axes(gca)
- save_pdf(ff.hf, mD.pdf_folder, 'DLC_Speeds_Combined.pdf', 600);
- %% ---------- 3. Statistical Comparison (Air-ON vs Air-OFF) ----------
- % Align with air epochs
- is_on = air_paws(1:length(avg_speed)) > 0;
- speed_on = avg_speed(is_on & isfinite(avg_speed));
- speed_off = avg_speed(~is_on & isfinite(avg_speed));
- if ~isempty(speed_on) && ~isempty(speed_off)
- [~, p_speed] = ttest2(speed_on, speed_off);
- fprintf('Global DLC Speed: Air-ON vs OFF, p = %.2e\n', p_speed);
- end
- %% ===================== DLC SPEED + AIR ON/OFF =====================
- % Inputs assumed from your script:
- % X_coords (N x 6), Y_coords (N x 6)
- % time_sec (N x 1) in seconds (DLC time base)
- % T = animal(1).b.led_sig.paws; T.time, T.is_on (LED time base)
- % bodyparts (1x6 cell), custom_colors (6x3)
- % ---- 0) Basic checks
- N = size(X_coords,1);
- assert(numel(time_sec)==N, 'time_sec length must match X_coords rows.');
- % ---- 1) Resample/align air signal to DLC time base
- % LED time is in seconds already (but you divided by 60 earlier for t_paws)
- % t_led = double(T.time(:)); % seconds
- t_led = double(T.t_led(:));
- air_led = double(strcmpi(string(T.is_on), "True") | double(T.is_on)); % robust
- % If T.is_on is already logical numeric, above still works.
- % Make sure time vectors are monotonic
- [~, idxSort] = sort(t_led);
- t_led = t_led(idxSort);
- air_led = air_led(idxSort);
- % Resample LED air to DLC time points
- air_dlc = interp1(t_led, air_led, time_sec, 'previous', 0);
- air_dlc(isnan(air_dlc)) = 0;
- air_dlc = air_dlc > 0.5;
- % ---- 2) Compute per-bodypart speed in pixels/s
- fs_dlc = 1/median(diff(time_sec)); % inferred FPS from DLC time base
- dx = [zeros(1,6); diff(X_coords,1,1)];
- dy = [zeros(1,6); diff(Y_coords,1,1)];
- speed_pix_s = sqrt(dx.^2 + dy.^2) * fs_dlc * 0.0114 ; % N x 6
- % Optional smoothing (helps readability)
- speed_pix_s_sm = movmedian(speed_pix_s, 5, 1);
- % ---- 3) Find air epochs on DLC time base
- air = air_dlc(:);
- on_idx = find(diff([0; air])== 1); % rising edges
- off_idx = find(diff([air; 0])==-1); % falling edges
- nTr = min(numel(on_idx), numel(off_idx));
- on_idx = on_idx(1:nTr);
- off_idx = off_idx(1:nTr);
- % sanity: ensure each onset precedes offset
- good = off_idx > on_idx;
- on_idx = on_idx(good);
- off_idx = off_idx(good);
- nTr = numel(on_idx);
- % ---- 4) Trial-wise paired comparison: Air-ON vs preceding Air-OFF window
- % Define a "pre" window immediately before each onset (e.g., 1.0 s)
- preWin_s = 1.0;
- preSamp = max(1, round(preWin_s * fs_dlc));
- mean_on = nan(nTr,6);
- mean_off = nan(nTr,6);
- for k = 1:nTr
- idx_on = on_idx(k):off_idx(k);
- idx_off = max(1, on_idx(k)-preSamp):on_idx(k)-1;
- if isempty(idx_off) || numel(idx_on) < 3
- continue
- end
- mean_on(k,:) = mean(speed_pix_s_sm(idx_on,:), 1, 'omitnan');
- mean_off(k,:) = mean(speed_pix_s_sm(idx_off,:), 1, 'omitnan');
- end
- % Drop trials with NaNs
- validTr = all(isfinite(mean_on),2) & all(isfinite(mean_off),2);
- mean_on = mean_on(validTr,:);
- mean_off = mean_off(validTr,:);
- nValid = size(mean_on,1);
- % Paired t-test per bodypart
- p_t = nan(1,6);
- tstat = nan(1,6);
- for i = 1:6
- [~, p_t(i), ~, stats] = ttest(mean_on(:,i), mean_off(:,i)); % paired
- tstat(i) = stats.tstat;
- end
- % ---- 5) Plot 1: Speed time-series with shaded air epochs
- figure(501); clf
- tmin = time_sec/60;
- for i = 1:6
- subplot(3,2,i); hold on
- % Shaded air windows
- yl = [0, max(speed_pix_s_sm(:,i), [], 'omitnan')*1.05 + eps];
- ylim(yl)
- for k = 1:nTr
- x1 = time_sec(on_idx(k))/60;
- x2 = time_sec(off_idx(k))/60;
- patch([x1 x2 x2 x1], [yl(1) yl(1) yl(2) yl(2)], ...
- 'k', 'FaceAlpha', 0.12, 'EdgeColor', 'none');
- end
- plot(tmin, speed_pix_s_sm(:,i), 'Color', custom_colors(i,:), 'LineWidth', 0.5);
- xlim([0 3]) % match your 3-min window
- xlabel('Time (min)')
- ylabel('Speed (px/s)')
- title(sprintf('%s', bodyparts{i}))
- box off
- end
- sgtitle('DLC point speed over time (shaded = Air ON)')
- % ---- 6) Plot 2: Onset-aligned speed (mean ± SD)
- win_pre = 2; % seconds before onset
- win_post = 5; % seconds after onset
- Lpre = round(win_pre * fs_dlc);
- Lpost = round(win_post * fs_dlc);
- t_rel = (-Lpre:Lpost)'/fs_dlc;
- % Build onset-aligned matrices: (time x trials x bodyparts)
- M = numel(t_rel);
- speed_onset = nan(M, nTr, 6);
- for k = 1:nTr
- c = on_idx(k);
- idx = (c-Lpre):(c+Lpost);
- if idx(1) < 1 || idx(end) > N
- continue
- end
- speed_onset(:,k,:) = speed_pix_s_sm(idx,:);
- end
- figure(502); clf
- for i = 1:6
- subplot(3,2,i); hold on
- X = squeeze(speed_onset(:,:,i)); % M x nTr
- mu = mean(X, 2, 'omitnan');
- sd = std(X, 0, 2, 'omitnan');
- plot(t_rel, mu, 'Color', custom_colors(i,:), 'LineWidth', 1.5);
- plot(t_rel, mu+sd, '--', 'Color', custom_colors(i,:), 'LineWidth', 0.75);
- plot(t_rel, mu-sd, '--', 'Color', custom_colors(i,:), 'LineWidth', 0.75);
- xline(0, 'k-');
- xlabel('Time from air onset (s)')
- ylabel('Speed (px/s)')
- title(bodyparts{i})
- box off
- end
- sgtitle('Onset-aligned DLC speed (mean ± SD)')
- % ---- 7) Plot 3: Trial-wise Air-ON vs Air-OFF (paired) bar + dots
- % figure(503); clf
- % tiledlayout(1,6,'Padding','compact','TileSpacing','tight');
- %%
- magfac = mD.magfac;
- ff = makeFigureRowsCols(107,[3 5 6.75 1.5],'RowsCols',[1 6],'spaceRowsCols',[0.01 0.04],'rightUpShifts',[0.051 0.2],...
- 'widthHeightAdjustment',[-45 -500]);
- for i = 1:6
- % subplot(1,6,i);
- axes(ff.h_axes(1,i));% nexttile
- hold on
- mOn = mean(mean_on(:,i), 'omitnan');
- mOff = mean(mean_off(:,i), 'omitnan');
- allmOn(i) = mOn;
- allmOff(i) = mOff;
- seOn = std(mean_on(:,i), 'omitnan');%/sqrt(nValid);
- seOff = std(mean_off(:,i), 'omitnan');%/sqrt(nValid);
- allseOn(i) = seOn;
- allseOff(i) = seOff;
- hb = bar([1 2], [mOff mOn]); % OFF then ON
- errorbar([1 2], [mOff mOn], [seOff seOn], 'k.', 'LineWidth', 1);
- set(hb,'FaceColor',custom_colors(i,:))
- % paired dots
- for k = 1:nValid
- plot([1 2], [mean_off(k,i) mean_on(k,i)], '-', 'Color', [0 0 0 0.15]);
- end
- set(gca,'XTick',[1 2],'XTickLabel',{'Air-OFF','Air-ON'});xtickangle(30)
- if i == 1
- ylabel('Mean speed (cm/s)')
- end
- % title(sprintf('%s | p=%.3g', bodyparts{i}, p_t(i)))
- ht = title(sprintf('%s | p<0.001', bodyparts{i})); set(ht,'FontWeight','Normal')
- box off
- format_axes(gca)
- end
- ht = sgtitle(sprintf('Paired Air-ON vs pre-Air-ON (Representative Animal, n = 34 trials)', nValid));set(ht,'FontSize',8,'FontWeight','Normal')
- save_pdf(gcf, mD.pdf_folder, 'DLC_bar_air_on_vs_off.pdf', 600);
- %% ---------- 1. Parameter Setup ----------
- win_pre = 5; % seconds
- win_post = 5; % seconds
- fs = 60; % Sampling rate (frames per second)
- Npre = round(win_pre * fs);
- Npost = round(win_post * fs);
- signal = DLC_speeds_cm(:,6); % Your data vector
- % Initialize arrays for mean values
- pre_means = [];
- post_means = [];
- % ---------- 2. Extraction Loop ----------
- for i = 1:length(onsets)
- idx = onsets(i);
- % Ensure the window is within the bounds of the signal
- if idx > Npre && idx + Npost <= length(signal)
- % Extract the pre-event segment (5s before onset)
- pre_segment = signal(idx - Npre : idx - 1);
- % Extract the post-event segment (5s starting at onset)
- post_segment = signal(idx : idx + Npost);
- % Calculate and store the mean for this specific trial
- pre_means(end+1) = mean(pre_segment, 'omitnan');
- post_means(end+1) = mean(post_segment, 'omitnan');
- end
- end
- % Display results for verification
- fprintf('Extracted means for %d valid trials.\n', length(pre_means));
fig_dlc.m at commit 5b9d1c9, no license · at the source
Overview
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.
Repositories
Its files are read in the Code ↔ Paper reader above, with 19 matches between paragraphs and lines of code.
OSF k9j3b
Availability: 1 check, the latest on 28 September 2026: the link answers (HTTP 200)
- 28 September 2026: the link answers (HTTP 200)
neuromomentumlab/AIR_Wheel_Methods
5b9d1c94237197f3c21d7a9dd9459c1f5e15f62f, 1 May 2026Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
64 files
- Matlab/
add_to_path_unlv.m , MATLAB, 37 lines - Matlab/
air_durations.m , MATLAB, 24 lines - Matlab/
air_signal_reader.m , MATLAB, 46 lines - Matlab/
apply_binary_segmentatio , MATLAB, 21 linesn.m - Matlab/
binarize_led_with_backgr , MATLAB, 76 linesound.m - Matlab/
calibrate_video_interact , MATLAB, 58 linesive.m - Matlab/
clean_binary_event_pairs , MATLAB, 69 lines.m - Matlab/
clean_led_by_daq_events. , MATLAB, 108 linesm - Matlab/
data_loading.m , MATLAB, 80 lines - Matlab/
extract_led_from_roi.m , MATLAB, 98 lines - Matlab/
extract_led_from_roi_M.m , MATLAB, 189 lines - Matlab/
fig_corr_map.m , MATLAB, 169 lines - Matlab/
fig_dlc.m , MATLAB, 673 lines, 2 matches - Matlab/
fig_dlc_F.m , MATLAB, 502 lines, 1 match - Matlab/
fig_dlc_n3.m , MATLAB, 232 lines, 1 match - Matlab/
fig_montage.m , MATLAB, 507 lines, 1 match - Matlab/
fig_montage_M.m , MATLAB, 455 lines - Matlab/
fig_montage_n3.m , MATLAB, 387 lines, 2 matches - Matlab/
fig_motion_energy.m , MATLAB, 420 lines, 2 matches - Matlab/
fig_motion_energy_n3.m , MATLAB, 704 lines, 2 matches - Matlab/
fig_pupil.m , MATLAB, 219 lines, 1 match - Matlab/
fig_pupiln3.m , MATLAB, 166 lines - Matlab/
fig_speed_analysis.m , MATLAB, 899 lines - Matlab/
fig_speed_analysis_n3.m , MATLAB, 900 lines - Matlab/
fig_speed_analysis_n4.m , MATLAB, 644 lines - Matlab/
find_onsets_offsets.m , MATLAB, 53 lines - Matlab/
get_edges.m , MATLAB, 12 lines - Matlab/
get_exp_info.m , MATLAB, 60 lines - Matlab/
jitter.m , MATLAB, 54 lines - Matlab/
load_dlc_labeled_filenam , MATLAB, 98 lineses.m - Matlab/
load_eye_pupil_roi.m , MATLAB, 52 lines - Matlab/
load_eye_pupil_signal.m , MATLAB, 124 lines, 1 match - Matlab/
load_led_signal.m , MATLAB, 310 lines - Matlab/
load_led_signal_s.m , MATLAB, 49 lines - Matlab/
make_alignment_movie.m , MATLAB, 180 lines - Matlab/
make_alignment_movie_1.m , MATLAB, 223 lines - Matlab/
match_edges.m , MATLAB, 22 lines - Matlab/
process_behavior_signals , MATLAB, 180 lines.m - Matlab/
process_h264.m , MATLAB, 112 lines - Matlab/
process_h264_1.m , MATLAB, 127 lines - Matlab/
sanitize_air_sig.m , MATLAB, 26 lines - Matlab/
speed_distributions_of_a , MATLAB, 44 linesnimals.m - Matlab/
validate_led_signal.m , MATLAB, 187 lines - Python/
build_air_wheel_data.py , Python, 117 lines - Python/
data_loader.py , Python, 143 lines - Python/
export_session_data_to_m , Python, 15 linesatlab.py - Python/
fig_show_roi.py , Python, 88 lines, 1 match - Python/
main_func.py , Python, 7 lines - Python/
main_processor.ipynb , Jupyter, 2 lines - Python/
notebooks/ , Jupyter, 344 linesLED_sig_extraction.ipynb - Python/
notebooks/ , Jupyter, 137 linesextract_roi.ipynb - Python/
notebooks/ , Jupyter, 215 lines, 1 matchoptical_flow.ipynb - Python/
optical_flow.py , Python, 249 lines, 1 match - Python/
plot_OF_ME.py , Python, 76 lines - Python/
process_h264.py , Python, 97 lines - Python/
reduce_video_dimensions. , Python, 51 lines, 1 matchpy - Python/
run_in_spyder_led_sig.py , Python, 27 lines - Python/
spyder_extract_led_signa , Python, 123 lines, 1 matchl.py - Python/
src/ , Python, 1 lineutils/ __init__.py - Python/
src/ , Python, 135 linesutils/ extract_led_signal.py - Python/
src/ , Python, 42 linesutils/ frame_funcs.py - Python/
src/ , Python, 276 lines, 1 matchutils/ motion_analysis.py - data_loading.m, MATLAB, 125 lines
- temp.m, MATLAB, 128 lines
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;
- 64 scripts, each with its path and the digest of its content;
- 19 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:
- it points to the authors' code: neuromomentumlab/
AIR_Wheel_Methods , OSF k9j3b
Read it in the paper: doi.org/10.1038/s41598-026-52322-z.
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, 28 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 3 authors, 11 keywords, 9 MeSH terms, 3 funders, 32 references.
Cite
This paper
Paranjape, P. S., Ghohaki, T. M., & Inayat, S. (2026). A transparent wheel-based platform for locomotion-on-demand and multi-view body and facial kinematics in head-fixed mice. Scientific reports, 16(1), 26691. https://
BibTeX
@article{paranjape2026tr
author = {Paranjape, Pratik S. and Ghohaki, Tahoura Mohammadi and Inayat, Samsoon},
title = {{A transparent wheel-based platform for locomotion-on-demand and multi-view body and facial kinematics in head-fixed mice}},
journal = {Scientific reports},
year = {2026},
month = may,
volume = {16},
number = {1},
pages = {26691},
publisher = {Nature Publishing Group},
issn = {2045-2322},
doi = {10.1038/
url = {https://
pmid = {42168450},
pmcid = {PMC13507272}
}
RIS
TY - JOUR
AU - Paranjape, Pratik S.
AU - Ghohaki, Tahoura Mohammadi
AU - Inayat, Samsoon
TI - A transparent wheel-based platform for locomotion-on-demand and multi-view body and facial kinematics in head-fixed mice
T2 - Scientific reports
J2 - Sci Rep
PY - 2026
DA - 2026/
VL - 16
IS - 1
SP - 26691
SN - 2045-2322
PB - Nature Publishing Group
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"type": "article-journal",
"title": "A transparent wheel-based platform for locomotion-on-demand and multi-view body and facial kinematics in head-fixed mice",
"container-title": "Scientific reports",
"author": [
{
"family": "Paranjape",
"given": "Pratik S."
},
{
"family": "Ghohaki",
"given": "Tahoura Mohammadi"
},
{
"family": "Inayat",
"given": "Samsoon"
}
],
"container-title-short":
"volume": "16",
"issue": "1",
"page": "26691",
"DOI": "10.1038/
"PMID": "42168450",
"PMCID": "PMC13507272",
"ISSN": "2045-2322",
"publisher": "Nature Publishing Group",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
5,
21
]
]
}
}
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.7554/elife.109717 [code]
- Retrosplenial cortex enables context-dependent goal-directed sensorimotor transformation.Journal: eLifeIn common: OpenCV, Statistics and Machine Learning Toolbox, pandas, 3 other tools, mouse, 5 references
- [2] doi:10.1038/s41467-026-75347-4 [code]
- Sleep reveals dynamics integrating and segregating movement and stimulus representations in V1.Journal: Nature communicationsIn common: Image Processing Toolbox, Signal Processing Toolbox, Statistics and Machine Learning Toolbox, 4 other tools, mouse, 3 references
- [3] doi:10.1038/s41467-026-71725-0 [code]
- Interactions across hemispheres in prefrontal cortex reflect global cognitive processing.Journal: Nature communicationsIn common: Image Processing Toolbox, Signal Processing Toolbox, Statistics and Machine Learning Toolbox, 4 other tools, 3 references
- [4] doi:10.1038/s41467-026-76581-6 [code]
- Thalamocortical bursts encode reward contingencies and drive associative learning.Journal: Nature communicationsIn common: OpenCV, Image Processing Toolbox, Signal Processing Toolbox, 5 other tools, mouse, 1 reference
- [5] doi:10.1038/s41593-026-02232-0 [code]
- Entorhinal cortex represents task-relevant remote locations independently of CA1.Journal: Nature neuroscienceIn common: OpenCV, Image Processing Toolbox, Signal Processing Toolbox, 5 other tools, mouse, 1 reference
- [6] doi:10.1038/s41467-026-73476-4 [code]
- Developmental molecular signatures define de novo cortico-brainstem circuit for skilled forelimb movement.Journal: Nature communicationsIn common: OpenCV, Image Processing Toolbox, Signal Processing Toolbox, 3 other tools, mouse, 2 references
- [7] doi:10.1016/j.neuron.2026.03.034 [code]
- Dentate gyrus interneurons modulate winner-take-all network dynamics in freely behaving mice.Journal: NeuronIn common: shadedErrorBar, Image Processing Toolbox, Signal Processing Toolbox, 5 other tools, mouse
- [8] doi:10.1038/s41593-026-02255-7 [code]
- Neural circuits encode prior knowledge of temporal statistics.Journal: Nature neuroscienceIn common: shadedErrorBar, Image Processing Toolbox, Signal Processing Toolbox, 5 other tools, mouse
- [9] doi:10.7554/elife.108663 [code]
- The Crunchometer, a low-cost, open-source acoustic analysis of feeding microstructure.Journal: eLifeIn common: OpenCV, Image Processing Toolbox, Signal Processing Toolbox, 5 other tools, methods / tools, mouse
- [10] doi:10.1126/sciadv.adv5652 [code]
- The anterior cingulate cortex modulates pupil-linked arousal.Journal: Science advancesIn common: OpenCV, pandas, SciPy, 2 other tools, 4 references
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.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 2 repositories of the authors' code, each at its verified commit and with its license, 64 scripts, and 19 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:054e77469580670c…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
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.
