-
Notifications
You must be signed in to change notification settings - Fork 11
Description
Dear Axion Developers,
I hope you're doing well. I'm currently processing contractility data using MATLAB and comparing it to the plots generated directly by AxIS Navigator. I noticed that in AxIS, the signals typically start and end at a baseline of zero, which makes the traces look smooth and well-aligned for visual inspection.
However, when plotting the same data in MATLAB using the AxisFile API, the signals often start and end at different baseline levels, which causes some drift or offset—especially visible in zoomed plots.
I'm wondering:
1. Is this baseline alignment (zero at start and end) something AxIS automatically applies for visualization?
2. If so, is there a specific method you recommend to reproduce this kind of correction in MATLAB for visual consistency?
I’ve tried applying a linear baseline correction between the median of the first and last 0.5 seconds of the signal, and it seems to work visually. Just wanted to confirm if that aligns with what AxIS does internally or if I'm doing something wrong with my MATLAB code.
Thanks so much for your help!
Best,
Iñigo Arrillaga
Biomedical Engineering – Tecnun – UNAV
Here is the code: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Proyecto Fin de Grado - Ingeniería Biomédica %%%%
%%% Universidad de Navarra - Tecnun %%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Instructions:
% ------------
% In our case, it is a MEAS plate containing 24 wells (4
% rows: A, B, C, and D x 6 columns: 1-6). Each well contains
% 16 electrodes (4 x 4), for a total of 384 electrodes.
% Total recording duration: 246.98 seconds
%
% The objective is to compare contractility signals (%ΔZ)
% obtained with the % Maestro Edge system (Axion BioSystems),
% both before and after filtering automatically applied using
% the Artifact Eliminator module in AxIS Navigator.
%
% Parameters such as DC and voltage development time will be
% identified. They will then be compared by day and experimental
% conditions.
%
% Author: Iñigo Arrillaga García
% Date: 25/05/2025
%
clear; close all; clc;
warning('off', 'all');
%% 0 - Initialization.
% '20250313_pacedcontractility_4dpd_placageltrex(000).raw'
% '20250313_pacedcontractility_4dpd_placageltrex(000)(001)_ArtifactEliminator.raw'
% Raw files names input:
filename_raw = input('Enter the name of the original, unfiltered file: ');
filename_filt = input('Enter the name of the file filtered by Artifact Eliminator: ');
% File verification:
if ~isfile(filename_raw) || ~isfile(filename_filt)
error('One of the .raw files is not in the current folder.');
end
% Load of Axion structures:
FileData_raw = AxisFile(filename_raw);
FileData_filt = AxisFile(filename_filt);
% Signal segment input to be analyzed (in seconds):
%intervalo = input('\nEnter the time interval to analyze (e.g. [0 24]): ');
intervalo = [0 inf];
% Initialization of original and filtered structures:
AllData = struct();
FilteredData = struct();
%% 1 - Data loading per well and electrode.
fprintf('\nLoading original and Artifact Eliminator-filtered signals:\n');
for row = 1:4
for col = 1:6
wellName = sprintf('%c%d', 'A' + (row - 1), col);
fprintf(' - Processing %s\n', wellName);
orig = cell(4,4);
filt = cell(4,4);
for er = 1:4
for ec = 1:4
electrodeName = sprintf('%d%d', ec, er);
try
% Original Signal:
data_raw = FileData_raw.RawContractilityData.LoadData(wellName, electrodeName, intervalo);
wf_raw = data_raw{row, col, ec, er};
[t_raw, z_raw] = wf_raw.GetTimeContractilityVector();
% Filtered Signal:
data_filt = FileData_filt.RawContractilityData.LoadData(wellName, electrodeName, intervalo);
wf_filt = data_filt{row, col, ec, er};
[~, z_filt] = wf_filt.GetTimeContractilityVector();
catch
t_raw = []; z_raw = []; z_filt = [];
end
orig{er, ec} = struct('t', t_raw, 'z', z_raw, 'name', electrodeName);
filt{er, ec} = struct('t', t_raw, 'z', z_filt, 'name', electrodeName);
end
end
AllData.(wellName) = orig;
FilteredData.(wellName) = filt;
end
end
%% 2 - Separate Display of Original & Filtered Signals per well.
fprintf('\nSignals per well Plot:\n');
wellRow = input('Introduce well row (1=A, 2=B, ...): ');
wellCol = input('Introduce well column (1–6): ');
wellName = sprintf('%c%d', 'A' + (wellRow - 1), wellCol);
pozo_orig = AllData.(wellName);
pozo_filt = FilteredData.(wellName);
% Size and location of centered windows
screenSize = get(0, 'ScreenSize');
figWidth = 1000;
figHeight = 700;
xOffset = 60;
yCenter = screenSize(4)/2 - figHeight/2;
centrar = input('Do you want to visually center the signals at start and end (like AxIS)? (1 = yes, 0 = no): ');
%% Consideration 1.1 – INITIAL Zoom – ORIGINAL Signal (0–1 s)
fprintf('\nInitial Zoom – Original signals (0–1 s)\n');
pozo = AllData.(wellName);
figure;
sgtitle(sprintf('Well %s – Original Signals Zoom(0–1 s)', wellName), 'FontWeight', 'bold');
for er = 1:4
for ec = 1:4
idx = (er - 1) * 4 + ec;
subplot(4,4,idx);
s = pozo{er, ec};
if ~isempty(s.t)
mask = s.t >= 0 & s.t <= 1;
t_zoom = s.t(mask);
z_zoom = s.z(mask);
if centrar
mask_start = s.t >= 0 & s.t <= 0.5;
mask_end = s.t >= s.t(end)-0.5 & s.t <= s.t(end);
if sum(mask_start) >= 3 && sum(mask_end) >= 3
base_ini = median(s.z(mask_start));
base_fin = median(s.z(mask_end));
correction = linspace(base_ini, base_fin, numel(s.z))';
zc_full = s.z - correction;
z_zoom = zc_full(mask);
end
end
plot(t_zoom, z_zoom, 'b');
title(sprintf('Electrode %d%d', ec, er));
xlim([0 1]);
else
title(sprintf('Electrode %d%d (empty)', ec, er));
end
xlabel('t (s)'); ylabel('%ΔZ'); grid on;
end
end
%% Consideration 1.2 – INITIAL Zoom – FILTERED Signal (0–1 s)
fprintf('\nInitial Zoom – Filtered signals (0–1 s)\n');
pozo_f = FilteredData.(wellName);
figure;
sgtitle(sprintf('Well %s – Filtered Signals Zoom(0–1 s)', wellName), 'FontWeight', 'bold');
for er = 1:4
for ec = 1:4
idx = (er - 1) * 4 + ec;
subplot(4,4,idx);
s = pozo_f{er, ec};
if ~isempty(s.t)
mask = s.t >= 0 & s.t <= 1;
t_zoom = s.t(mask);
z_zoom = s.z(mask);
if centrar
mask_start = s.t >= 0 & s.t <= 0.5;
mask_end = s.t >= s.t(end)-0.5 & s.t <= s.t(end);
if sum(mask_start) >= 3 && sum(mask_end) >= 3
base_ini = median(s.z(mask_start));
base_fin = median(s.z(mask_end));
correction = linspace(base_ini, base_fin, numel(s.z))';
zc_full = s.z - correction;
z_zoom = zc_full(mask);
end
end
plot(t_zoom, z_zoom, 'r');
title(sprintf('Electrode %d%d', ec, er));
xlim([0 1]);
else
title(sprintf('Electrode %d%d (empty)', ec, er));
end
xlabel('t (s)'); ylabel('%ΔZ'); grid on;
end
end
%% Consideration 1.3 – FINAL Zoom – ORIGINAL Signal (245.98–246.98 s)
fprintf('\nFinal Zoom – Original Signal (245.98–246.98 s)\n');
pozo = AllData.(wellName);
figure;
sgtitle(sprintf('Well %s – Original (245.98–246.98 s)', wellName), 'FontWeight', 'bold');
for er = 1:4
for ec = 1:4
idx = (er - 1) * 4 + ec;
subplot(4,4,idx);
s = pozo{er, ec};
if ~isempty(s.t)
mask = s.t >= 245.98 & s.t <= 246.98;
t_zoom = s.t(mask);
z_zoom = s.z(mask);
if centrar
mask_start = s.t >= 0 & s.t <= 0.5;
mask_end = s.t >= s.t(end)-0.5 & s.t <= s.t(end);
if sum(mask_start) >= 3 && sum(mask_end) >= 3
base_ini = median(s.z(mask_start));
base_fin = median(s.z(mask_end));
correction = linspace(base_ini, base_fin, numel(s.z))';
zc_full = s.z - correction;
z_zoom = zc_full(mask);
end
end
plot(t_zoom, z_zoom, 'b');
title(sprintf('Electrode %d%d', ec, er));
xlim([245.98 246.98]);
else
title(sprintf('Electrode %d%d (empty)', ec, er));
end
xlabel('t (s)'); ylabel('%ΔZ'); grid on;
end
end
%% Consideration 1.4 – FINAL Zoom – FILTERED Signal (245.98–246.98 s)
fprintf('\nFinal Zoom – Filtered Signal (245.98–246.98 s)\n');
pozo_f = FilteredData.(wellName);
figure;
sgtitle(sprintf('Well %s – Filtered (245.98–246.98 s)', wellName), 'FontWeight', 'bold');
for er = 1:4
for ec = 1:4
idx = (er - 1) * 4 + ec;
subplot(4,4,idx);
s = pozo_f{er, ec};
if ~isempty(s.t)
mask = s.t >= 245.98 & s.t <= 246.98;
t_zoom = s.t(mask);
z_zoom = s.z(mask);
if centrar
mask_start = s.t >= 0 & s.t <= 0.5;
mask_end = s.t >= s.t(end)-0.5 & s.t <= s.t(end);
if sum(mask_start) >= 3 && sum(mask_end) >= 3
base_ini = median(s.z(mask_start));
base_fin = median(s.z(mask_end));
correction = linspace(base_ini, base_fin, numel(s.z))';
zc_full = s.z - correction;
z_zoom = zc_full(mask);
end
end
plot(t_zoom, z_zoom, 'r');
title(sprintf('Electrode %d%d', ec, er));
xlim([245.98 246.98]);
else
title(sprintf('Electrode %d%d (empty)', ec, er));
end
xlabel('t (s)'); ylabel('%ΔZ'); grid on;
end
end