moving median with variable window
古いコメントを表示
Is there any way how to effectively generalize movmedian function to work with variable window length or local variable k-point median values, where k is vector with the same length as length of input vector (lenght(x) = lenght(k))?
Example:
x = 1:6
k = 2,3,3,5,3,2
M = movmedian_vk(x,k)
M = 1, 2, 3, 4, 5, 5.5
My naive solution looks like:
function M = movmedian_vk(x,k)
if length(k) ~= length(x)
error('Incomaptible input data')
end
M = zeros(size(x));
[uk,~,ck] = unique(k);
for i = 1:length(uk)
M_i = movmedian(x,uk(i));
I_i = (ck == i);
M(I_i) = M_i(I_i);
end
end
2 件のコメント
Image Analyst
2024 年 9 月 23 日
Can you explain the use case? Why do you want to do this?
回答 (3 件)
Bruno Luong
2024 年 9 月 23 日
編集済み: Bruno Luong
2024 年 9 月 23 日
One way (for k not very large)
x = 1:6
k = [2,3,4,5,3,2]; % Note: I change k(3) to 4
winmedian(x,k)
function mx = winmedian(x,k)
x = reshape(x, 1, []);
k = reshape(k, 1, []);
K = max(k);
p = floor(K/2);
q = K-p;
qm1 = q-1;
r = [x(q:end), nan(1,qm1)];
c = [nan(1,p), x(1:q)];
X = hankel(c,r);
i = (-p:qm1).';
kb = floor(k/2);
kf = k-1-kb;
mask = i < -kb | i > kf;
X(mask) = NaN;
mx = median(X,1,'omitnan');
end
7 件のコメント
John D'Errico
2024 年 9 月 23 日
This was essentially the solution I would have proposed. Is it an improvement? That may depend on how much variety there is in k. Also, for larger values of k, and long vectors, the arrays generated could be large. So unless the simple looped code is a problem, it might not be worth doing. But as I said, @Bruno Luong wrote exactly what I was going to write.
So for longer input signals, even in a case of a few unique k values, is your code 900x slower than my naive code.
But will that always be the case? And how large can the window widths k(i) get? Bruno stipulated that his solution was for the case when the window widths were not too large, and mine is the same.
When there are a small number of unique k(i), yes, yours is best. However, more generally, Bruno's is faster:
k = randi(30,1,1e5);
x = rand(1,1e5);
timeit(@() winmedianMichal(x,k))
timeit(@() winmedianBruno(x,k))
function M = winmedianMichal(x,k)
if length(k) ~= length(x)
error('Incomaptible input data')
end
M = zeros(size(x));
[uk,~,ck] = unique(k);
for i = 1:length(uk)
M_i = movmedian(x,uk(i));
I_i = (ck == i);
M(I_i) = M_i(I_i);
end
end
function mx = winmedianBruno(x,k)
x = reshape(x, 1, []);
k = reshape(k, 1, []);
K = max(k);
p = floor(K/2);
q = K-p;
qm1 = q-1;
r = [x(q:end), nan(1,qm1)];
c = [nan(1,p), x(1:q)];
X = hankel(c,r);
i = (-p:qm1).';
kb = floor(k/2);
kf = k-1-kb;
mask = i < -kb | i > kf;
X(mask) = NaN;
mx = median(X,1,'omitnan');
end
Michal
2024 年 9 月 24 日
x = rand(1,6)
k = [2,3,3,5,3,2];
n=numel(x);
J=repelem(1:n,k);
I0=1:numel(J);
splitMean=@(vals,G) (accumarray(G(:),vals(:))./accumarray(G(:),ones(numel(vals),1)))';
cc=repelem( round(splitMean( I0,J )) ,k);
zz=min(max(I0-cc+J+1,1),n+2);
vals=[nan,x,nan];
vals=vals(zz);
I=I0-repelem( find(diff([0,J]))-1 ,k);
X=accumarray([I(:),J(:)], vals(:), [max(k),n],[],nan);
M = median(X,1,'omitnan')
Anyway, I will be very happy for any hint how to apply robust median filter on my use case, where separate parts of signal shoud be filtered with different filter windows (something like weighting).
If your movmedian windows are simply varying over a small sequence of consecutive intervals, then the code below shows a little bit of speed-up. It won't give the exact same output near the break points between intervals, but it should be fairly close.
x = rand(1,1e5);
k = 8000*ones(1,1e5);
k(20000:30000) =50;
k(18000:20000) =200;
k(30000:32000) =200;
timeit(@() winmedianMichal(x,k))
timeit(@() winmedianMatt(x,k))
function M = winmedianMichal(x,k)
if length(k) ~= length(x)
error('Incomaptible input data')
end
M = zeros(size(x));
[uk,~,ck] = unique(k);
for i = 1:length(uk)
M_i = movmedian(x,uk(i));
I_i = (ck == i);
M(I_i) = M_i(I_i);
end
end
%Requires download of groupConsec
%https://www.mathworks.com/matlabcentral/fileexchange/78008-tools-for-processing-consecutive-repetitions-in-vectors
function M = winmedianMatt(x,k)
M=splitapply(@(a,b){movmedian(a,b(1))}, x,k, groupConsec(k));
M=[M{:}];
end
5 件のコメント
Bruno Luong
2024 年 9 月 24 日
編集済み: Bruno Luong
2024 年 9 月 24 日
M=splitapply(@(a,b){movmedian(a,b(1))}, x,k, groupConsec(k));
Not sure if you have to extend subgroup of x by more or less k/2 on each sides to avoid boundary truncation of each group.
Test shows the results do not match, I strongly suspect thtis is the cause:
x = rand(1,1e5);
k = 8000*ones(1,1e5);
k(20000:30000) =50;
k(18000:20000) =200;
k(30000:32000) =200;
M_Matt = winmedianMatt(x,k);
M_Michael = winmedianMichal(x,k);
isequal(M_Michael,M_Matt)
ans =
logical
0
Matt J
2024 年 9 月 24 日
Correct:
"It won't give the exact same output near the break points between intervals, but it should be fairly close."
Bruno Luong
2024 年 9 月 25 日
編集済み: Bruno Luong
2024 年 9 月 25 日
It is not stated which algorithm movmedian uses in the official document. Or at least I can't see it.
I assume it is somewhat based on https://en.wikipedia.org/wiki/Quickselect to select the two median elements in the sliding windows. The average complexity is n * k; where n is the length of x for fixed sliding windows size k.
As your use case k is quite large, some other algorithms exist and are better such as algorithm based on histogram. The complexity is log(r), where r is the histogram resolution. It is not difficult to implement with constant resolution, the resolution being selected to be suitable for a specific goal. The idea is to kep track the histogram and update it by binary search when old element is removed and new element is inserted.
For more riguruous implementation, see Weiss paper. For k up to 8000, I would suggest to investigate to such algorithm rather than using MATLAB stock function it time is critical.
Michal
2024 年 9 月 25 日
Bruno Luong
2024 年 9 月 25 日
Done; somehow this Answers forum and firefox have issue when I edit it, must use another browser.
カテゴリ
ヘルプ センター および File Exchange で Creating and Concatenating Matrices についてさらに検索
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!