function A = hmf(A,n)
%HMF Hybrid median filtering.
% B = HMF(A,N) performs hybrid median filtering of the matrix A using a
% NxN box. Hybrid median filtering preserves edges better than a NxN
% square kernel-based median filter because data from different spatial
% directions are ranked separately. Three median values are calculated in
% the NxN box: MR is the median of horizontal and vertical R pixels, and
% MD is the median of diagonal D pixels. The filtered value is the median
% of the two median values and the central pixel C: median([MR,MD,C]).
% For N = 5:
% |D * R * D|
% |* D R D *|
% |R R C R R|
% |* D R D *|
% |D * R * D|
%
% B = HMF(A) uses N = 5 (default value).
%
% A can be a 2-D array or an RGB image. If A is an RGB image, hybrid
% median filtering is performed in the HSV color space.
%
% Notes
% -----
% 1) N must be odd. If N is even then N is incremented by 1.
% 2) The Image Processing Toolbox is required.
% 3) If the function NANMEDIAN exists (Statistics Toolbox), NaN are
% treated as missing values and are ignored.
%
% Examples
% --------
% % -- original grayscale image --
% I = imread('eight.tif');
% % noisy image
% J = imnoise(I,'salt & pepper',0.03);
% % hybrid median filtering
% K = hmf(J);
% % figures
% subplot(121),imshow(J),subplot(122),imshow(K)
%
% % -- original RGB image --
% [I,map] = imread('trees.tif');
% I = ind2rgb(I,map);
% % noisy image
% J = imnoise(I,'salt & pepper',0.02);
% % hybrid median filtering (using a 9x9 box)
% K = hmf(J,9);
% % figures
% subplot(121),imshow(J),subplot(122),imshow(K)
%
% See also MEDFILT2, MEDFILT2RGB, MEDFILT3, COLFILT
%
% -- Damien Garcia -- 2007/08, revised 2010/02
error(nargchk(1,2,nargin));
if nargin==1, n = 5; end
if ~isscalar(n) || n<0, n = 5; end
% --- n must be odd. If not then n = n+1
n = round(n);
if rem(n+1,2)~=0, n = n+1; end
% --- Do we have an RGB image?
% RGB images can be only be uint8, uint16, single, or double
isRGB = ndims(A)==3 && (isfloat(A) || isa(A,'uint8') || isa(A,'uint16'));
% ---- Adapted from the obsolete function ISRGB ----
if isRGB
if isfloat(A)
% At first just test a small chunk to get a possible quick negative
mm = size(A,1);
nn = size(A,2);
chunk = A(1:min(mm,10),1:min(nn,10),:);
isRGB = (min(chunk(:))>=0 && max(chunk(:))<=1);
% If the chunk is an RGB image, test the whole image
if isRGB
isRGB = (min(A(:))>=0 && max(A(:))<=1);
end
end
end
% ---- end of isrgb ----
assert(isRGB | ndims(A)==2,...
'The input must be a 2-D array or an RGB image.')
classA = class(A);
% --- If the input is an RGB image, HMF is used in the HSV color space
if isRGB
A = rgb2hsv(A);
for k = 1:3, A(:,:,k) = hmf(A(:,:,k),n); end
A = hsv2rgb(A);
% HSV2RGB returns a double: change the class if necessary
switch classA
case 'uint8'
A = uint8(A*255);
case 'uint16'
A = uint16(A*65535);
case 'single'
A = single(A);
end
return
end
% --- Plus & Cross masks
Plus = false(n,n);
Plus((n+1)/2,:) = true;
Plus(:,(n+1)/2) = true;
Plus = Plus(:);
Cross = false(n,n);
Cross((1:n)+n*(0:n-1)) = true;
Cross((1:n)+n*((n-1):-1:0)) = true;
Cross = Cross(:);
%% --- Hybrid median filtering
% Note: NANMEDIAN is used if this function exists (Statistics Toolbox)
existNaNmedian = exist('nanmedian','file');
% --- the COLFILT function zero-pads! => replicate boundaries
A = padarray(A,[(n-1)/2 (n-1)/2],'replicate');
M1 = colfilt(A,[n n],'sliding',@CrossMedian);
M2 = colfilt(A,[n n],'sliding',@PlusMedian);
if existNaNmedian
A = nanmedian(cat(3,A,M1,M2),3);
else
A = median(cat(3,A,M1,M2),3);
end
% remove the borders that were added by PADARRAY
A = A((n+1)/2:end-(n-1)/2,(n+1)/2:end-(n-1)/2);
function CM = CrossMedian(X)
ncol = size(X,2);
I = repmat(Cross,[1 ncol]);
X = reshape(X(I),[2*n-1 ncol]);
if existNaNmedian
CM = nanmedian(X);
else
CM = median(X);
end
end
function PM = PlusMedian(X)
ncol = size(X,2);
I = repmat(Plus,[1 ncol]);
X = reshape(X(I),[2*n-1 ncol]);
if existNaNmedian
PM = nanmedian(X);
else
PM = median(X);
end
end
end