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
相关产品推荐
相关产品推荐

