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개)
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!