You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MATLAB中基于第二X轴提取ECG波形峰值的技术问题

问题描述

我目前正在尝试基于ECG时间和3个标记点叠加DICOM超声心动图与CT图像。ECG线条包含在超声心动图中,我已通过颜色提取该轨迹并绘制为图表。由于图表基于colorMask,y值为ecgTrace,x值为timeVector,我希望X轴显示DICOM图像的总时长,以便为每个ECG波形获取正确帧。我已隐藏原X轴并替换为总时长,但无法基于第二X轴获取峰值。

ECG轨迹提取与初始绘图代码

colorMask = abs(double(echoImageRGB(:,:,1)) - clickedColor(1)) <= tolerance & ...
            abs(double(echoImageRGB(:,:,2)) - clickedColor(2)) <= tolerance & ...
            abs(double(echoImageRGB(:,:,3)) - clickedColor(3)) <= tolerance;

% 填充掩码中的孔洞
colorMaskFilled = imfill(colorMask, 'holes');

% 从掩码中提取ECG轨迹:取每列最下方的白色像素作为信号点
% 预分配ECG轨迹数组
ecgTrace = nan(1, size(colorMaskFilled, 2));

% 遍历每列提取信号
for col = 1:size(colorMaskFilled, 2)
    % 找到当前列中掩码为白色的行
    rows = find(colorMaskFilled(:, col));
    
    % 如果存在有效像素,取最下方的行作为信号值
    if ~isempty(rows)
        ecgTrace(col) = max(rows);
    end
end

% 反转ECG轨迹以匹配正确的显示方向
ecgTrace = size(colorMaskFilled, 1) - ecgTrace;

timeVector = linspace(0, totalDuration, length(ecgTrace));
% 归一化ECG轨迹(可选)
ecgTrace = ecgTrace - mean(ecgTrace, 'omitnan');

figure;
ax1 = axes; % 主坐标轴
plot(ax1, timeVector, ecgTrace, 'b', 'LineWidth', 1.5); % 在主轴绘制ECG
ax1.XColor = 'none'; % 隐藏主轴X轴
ax1.YColor = 'k';    % 保留Y轴可见

% 创建第二坐标轴
ax2 = axes('Position', ax1.Position, 'Color', 'none', 'YColor', 'k');
linkaxes([ax1, ax2], 'y'); % 同步两个轴的Y轴缩放

% 设置第二轴为可见的X轴
set(ax2, 'XLim', [0-startTime*2, max(totalDuration)], 'XColor', 'r'); % 设置范围和颜色
xlabel(ax2, '总时长(分钟)');

% 调整第二轴位置
ax2.XAxisLocation = 'bottom';  % 将X轴放在底部
ax2.YAxisLocation = 'left';    % Y轴放在左侧

% 完全隐藏主坐标轴
ax1.Visible = 'off';

% 优化显示效果
ax2.Box = 'off';  % 关闭边框
ax2.Color = 'none';  % 背景透明

% 设置图窗背景为白色
set(gcf, 'Color', 'w');

尝试的峰值检测代码(效果不佳)

figure;
ax1 = axes; % 创建不可见的主坐标轴
plot(ax1, timeVector, ecgTrace, 'b', 'LineWidth', 1.5); % 在主轴绘制ECG
ax1.Visible = 'off'; % 完全隐藏主轴

% 设置第二轴为交互用可见轴
ax2 = axes('Position', ax1.Position, 'XAxisLocation', 'bottom', 'Color', 'none', 'YColor', 'k');
linkaxes([ax1, ax2], 'y'); % 同步Y轴
ax2.XLim = [0, max(timeVector)/60]; % 设置X轴范围为分钟级总时长
xlabel(ax2, '总时长(分钟)');
ylabel(ax2, '幅值');
ax2.XColor = 'r'; % 标记第二轴颜色

% 检测ECG峰值
[peakAmplitudes, peakIndices] = findpeaks(ecgTrace, 'MinPeakHeight', 10);  % 可调整MinPeakHeight参数

% 将峰值索引转换为分钟级时间
peakTimesSeconds = timeVector(peakIndices);  % 获取峰值对应的秒级时间
peakTimesMinutes = peakTimesSeconds / 60;    % 转换为分钟

% 输出峰值信息
disp('第二轴上的峰值时间(总时长,分钟):');
disp(peakTimesMinutes);
disp('峰值幅值:');
disp(peakAmplitudes);

% 在第二轴绘制峰值标记
hold on;
plot(ax2, peakTimesMinutes, peakAmplitudes, 'rp', 'MarkerSize', 10, 'MarkerFaceColor', 'red');
title('ECG峰值在总时长轴上的可视化');

修正方案

问题核心在于原始代码未过滤ecgTrace中的NaN值,且第二轴X范围与时间序列的映射存在偏差,导致峰值坐标错位。以下是修正后的完整代码:

% --- 保留原始ECG轨迹提取代码(略)---

figure;
ax1 = axes; 
plot(ax1, timeVector, ecgTrace, 'b', 'LineWidth', 1.5); 
ax1.Visible = 'off'; 

% 创建第二轴并同步Y轴
ax2 = axes('Position', ax1.Position, 'Color', 'none', 'YColor', 'k');
linkaxes([ax1, ax2], 'y');

% 基于timeVector的分钟级范围设置第二轴X限制,确保映射准确
ax2.XLim = [min(timeVector)/60, max(timeVector)/60];
xlabel(ax2, '总时长(分钟)');
ylabel(ax2, '幅值');
ax2.XColor = 'r';

% 清洗ECG数据:过滤NaN值,避免findpeaks处理无效数据
validIdx = ~isnan(ecgTrace);
cleanEcg = ecgTrace(validIdx);
cleanTime = timeVector(validIdx);

% 检测峰值:根据实际数据调整MinPeakHeight参数
[peakAmplitudes, peakCleanIdx] = findpeaks(cleanEcg, 'MinPeakHeight', 10);

% 转换峰值时间为分钟级
peakTimesMinutes = cleanTime(peakCleanIdx) / 60;

% 在第二轴绘制峰值
hold(ax2, 'on');
plot(ax2, peakTimesMinutes, peakAmplitudes, 'rp', 'MarkerSize', 10, 'MarkerFaceColor', 'red');
title('ECG峰值在总时长轴上的可视化');

% 优化X轴刻度,提升可读性
ax2.XTick = linspace(min(peakTimesMinutes), max(peakTimesMinutes), 5);

关键修正点

  • 过滤ecgTrace中的NaN值,确保findpeaks仅处理有效信号
  • 让第二轴X范围直接匹配timeVector转换后的分钟范围,避免手动设置偏移
  • 基于清洗后的时间序列转换峰值坐标,保证与第二轴完全对齐

内容的提问来源于stack exchange,提问作者AMHM

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 17:05:18