寻找峰值 MATLAB

3
我有一个向量,其中包含图像一行中像素的灰度级别。 vec=IM(:,65); 我展示了我想要检测的数组部分。这些部分将是我的对象像素。
如何检测这些对象像素?
向量的绘图: enter image description here 该向量在此处: vec

你尝试过findpeaks方法吗?它有过滤参数,我建议先尝试使用'MinPeakprominence'。 - Daniel
同意丹尼尔的观点。在数据的负数上使用 findpeaks 函数。 - rayryeng
如果没有人回答,我会很快写一个答案。 - rayryeng
1
你为什么改变了问题? - Mendi Barel
@MendiBarel 我把它改回来了。我讨厌人们这样做。我们的答案不再有意义,而且他们已经接受了一个答案。 - rayryeng
2个回答

6

您可以使用信号处理工具箱中的findpeaks轻松解决此问题。具体来说,对于您的数据,我需要以如下方式调用它:

[pks, locs] = findpeaks(max(vec)-vec, 'minpeakdistance', 160, 'minpeakheight', 22);
findpeaks只能找到正峰(局部最大值)。因此,我们需要反转它,使所有的局部极小值变成局部极大值。我通过取向量的最大值并将其减去向量来实现这一点。由于有很多局部峰值,minpeakdistance字段允许您查找至少在每个峰值之间分隔这么多的峰值。我将其调整为160。此外,最小峰值高度查找大于某个数的峰值,我将其调整为22。 pks找到实际峰值,而locs给出信号中峰值的位置。我们需要使用locs来找到实际的峰值数据,因为我们是在信号的镜像反射版本上执行的操作。因此,要获取实际的峰值数据,请执行以下操作:
pks_final = vecs(loc);

作为示范,让我们将这个信号以及由findpeaks定位的峰值绘制出来:
plot(1:numel(vec), vec, locs, vec(locs), 'r.');

原始数据以蓝色绘制,而检测到的峰值以红色绘制。这是我得到的结果:

enter image description here


祝你好运!


@rayryend 很好的想法。为此点赞。但是有一个问题:为什么不使用findpeaks(-vec,'minpeakdistance',160,'minpeakheight',22)?为什么需要max(vec)-vec? - lakshmen
1
@lakesh - 执行-vec会将数据的所有值取反,而minpeakheight仅适用于正数。如果数据以零为中心,则-vec将起作用,但请注意,所有点都在0上方。如果您尝试执行您的建议,您将收到一个警告,指明我所说的内容,并且您将找不到峰值。 - rayryeng
@lakesh - 很高兴能帮忙。如果你有疑问,请继续提问! - rayryeng

3

寻找本地峰值的方法有多种,这里我使用与本地平均值的偏差来寻找,然后分离区域,并扫描每个区域的最小值。

clear
close all

load a

std_a=std( a(a~=0) );

SMOOTH_SIZE=131;% depend on data size
THRESHOLD=0.7*std_a;

smooth_a = conv(a,ones(SMOOTH_SIZE,1)/SMOOTH_SIZE,'same'); %ma filter

deviation_a=a-smooth_a; 
negdev_a=deviation_a.*(deviation_a<-THRESHOLD); %deviation in negative region (minimum)

negdev_a_left=[negdev_a(2:end) 0]; % helper to find starting index point
negdev_a_right=[0 negdev_a(1:end-1)]; % helper to find end index point

negdev_a(1)=0; negdev_a(end)=0; % make sure that we have zero point
indfrom=find(negdev_a==0 & negdev_a_left~=0); %start index per region
indto=find(negdev_a==0 & negdev_a_right~=0); %start index per region

if(length(indfrom)~=length(indto)), error('error in regions');end

peak_indexes=zeros(1,length(indfrom)); %number of regions
peak_counter = 0;

for i=1:length(indfrom)
  [center_min, min_idx]=min( a( indfrom(i):indto(i) ) );
  real_min_idx=indfrom(i)-1+min_idx; % convert back to original array index
  if( real_min_idx==indfrom(i) || real_min_idx==indto(i) ), continue; end
  left_max=max(a( indfrom(i):real_min_idx-1)); %helper to check for real minimum
  right_max=max(a( real_min_idx+1:indto(i)));  %helper to check for real minimum

  if(center_min<left_max && center_min<right_max) % check if this is real minimum
       peak_counter=peak_counter+1;
       peak_indexes(peak_counter)=real_min_idx;
  end
     % if you need subpixel accuracy you can do some weighted average in the min region
end
peak_indexes=peak_indexes(1:peak_counter); %narrow to found indexes

figure; plot(a); hold on; plot(smooth_a, 'k'); hold off;
figure; plot(deviation_a);hold on;  plot(negdev_a,'r.');hold off;
figure; plot(a);hold on; plot(peak_indexes, a(peak_indexes),'rO');hold off; %result

结果 希望这能有所帮助, Mendi


你能展示一下使用你的算法在 OP 的数据上得到的结果吗?它能够找到 OP 示例中给出的那些峰值并且仅限于这些峰值吗?从你的代码来看,它可以找到所有的局部峰值,但是不能够分离 OP 指定的目标峰值。 - rayryeng
非常好!做得好。然而,我发现这有点侵入性,因为你必须下载一个不是 MATLAB 原生部分的额外文件。 - rayryeng
好的,我已经移除了外部文件并添加了移动平均滤波器的代码。 - Mendi Barel
2
一个用于移动平均的for循环,我快死了...不要这样,请使用类似这样的东西:smooth_a = conv(a,ones(SMOOTH_SIZE,1)/SMOOTH_SIZE,'same');,更好更清晰。 - Bentoy13
它对我来说效果相当不错,谢谢。有一个问题,您是如何决定在“THRESHOLD=0.7*std_a;”这一行中使用数字0.7的? - ffttyy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接