function [b,noise] = wiener2(a,nhood,block,noise)
%WIENER2 Adaptive 2-D Wiener filtering.
%	WIENER2 is used to adaptively low-pass filter an image
%	that has been degraded by constant power additive noise.
%
%	J = WIENER2(I,[M N],NOISE) processes the intensity image I
%	using pixel-wise adaptive Wiener filtering.  Neighborhoods
%	of size M-by-N are used to estimate the local image mean
%	and standard deviation.  The additive noise power is
%	assumed to be NOISE.  Block processing is used to
%	save memory (see BESTJLK).
%
%	[J,NOISE] = WIENER2(I,[M N]) also estimates the additive 
%	noise power before doing the filtering.  This estimate
%	is returned in NOISE. [...] = WIENER2(I) uses a 3-by-3
%	neighborhood.
%
%	J = WIENER2(I,[M N],[MBLOCK NBLOCK],NOISE) or
%	[J,NOISE] = WIENER2(I,[M N],[MBLOCK NBLOCK]) processes the
%	intensity image I as above but in blocks of size 
%	MBLOCK-by-NBLOCK.  Use J = WIENER2(I,[M N],SIZE(I),NOISE)
%	to process the matrix all at once.
%
%	See also FILTER2, MFILTER2, NLFILTER, COLFILT.

%	Clay M. Thompson 2-17-93
%	Copyright (c) 1993 by The MathWorks, Inc.
%	$Revision: 1.13 $  $Date: 1993/09/10 16:22:17 $

% Uses algorithm developed by Lee (1980).
% Reference: "Two-Dimensional Signal and Image Processing" by 
% Jae S. Lim, pp.536-540.

error(nargchk(1,4,nargin));

if nargin<2, nhood = [3 3]; end
if nargin<=2, noise = []; block = bestblk(size(a)); end
if nargin==3,
  if max(size(block))==1, 
    noise = block; block = bestblk(size(a));
  else
    noise = [];
  end
end

if all(block>=size(a)), % Process the whole matrix at once.
  % Expand A
  [ma,na] = size(a);
  aa = zeros(size(a)+nhood-1);
  aa(floor((nhood(1)-1)/2)+[1:ma],floor((nhood(2)-1)/2)+[1:na]) = a;

  % Convert neighborhoods of matrix A to columns
  x = im2col(aa,nhood,'sliding');

  % Estimate neighborhood mean
  mean = zeros(size(a));
  mean(:) = sum(x)/prod(nhood);

  % Estimate neighborhood variance
  var = zeros(size(a));
  var(:) = sum(x.*x)/prod(nhood);
  var = var - mean.*mean;

  % Estimate noise power if necessary
  if isempty(noise), 
    noise = sum(sum(var))/prod(size(var));
  else
    noise = max(noise,eps);
  end
  
  % Pixel-wise filter the image
  b = mean + (max(0,var-noise)./max(noise,var)).*(a - mean);

else, % Process the matrix in blocks of size BLOCK.
  % Estimate noise power if necessary
  if isempty(noise), 
    noise = sum(sum(colfilt(a,nhood,block,'sliding', ...
      'sum(x.*x)/P1-(sum(x)/P1).^2',prod(nhood))))/prod(size(a));
  else
    noise = max(noise,eps);
  end

  % Expand A: Add border, pad if size(a) is not divisible by block.
  [ma,na] = size(a);
  mpad = rem(ma,block(1)); if mpad>0, mpad = block(1)-mpad; end
  npad = rem(na,block(2)); if npad>0, npad = block(2)-npad; end
  
  aa = zeros(size(a) + [mpad npad] + (nhood-1));
  aa(floor((nhood(1)-1)/2)+(1:ma),floor((nhood(2)-1)/2)+(1:na)) = a;

  %
  % Process each block in turn.
  %
  m = block(1) + nhood(1)-1;
  n = block(2) + nhood(2)-1;
  mblocks = (ma+mpad)/block(1);
  nblocks = (na+npad)/block(2);
  b = zeros(ma+mpad,na+npad);
  arows = 1:m; acols = 1:n;
  brows = 1:block(1); bcols = 1:block(2);
  aarows = (1:block(1))+floor((nhood(1)-1)/2);
  aacols = (1:block(2))+floor((nhood(2)-1)/2);
  mb = block(1); nb = block(2);
  for i=0:mblocks-1,
    for j=0:nblocks-1,
      x = im2col(aa(i*mb+arows,j*nb+acols),nhood,'sliding');

      % Estimate neighborhood mean
      mean = zeros(block);
      mean(:) = sum(x)/prod(nhood);

      % Estimate neighborhood variance
      var = zeros(block);
      var(:) = sum(x.*x)/prod(nhood);
      var = var - mean.*mean;

      % Pixel-wise filter the image
      b(i*mb+brows,j*nb+bcols) = mean + (max(0,var-noise)./max(noise,var)) ...
         .* (aa(i*mb+aarows,j*nb+aacols) - mean);
    end
  end
  b = b(1:ma,1:na);
end
