function [y,w,r] = ccabss(x)
% CCABSS -  Blind Source Separation by Canonical Correlation Analysis
%
% Y = CCABSS(X) is the BSS of X=A*S where S is a set of unknown source signals
% and A is an unknown mixing matrix. The components in S are supposed to
% be independent. Y is an estimate of S appart from permutation and scaling.
% For mixed 1-D signals, X is 2-D. The first index refer to the different
% components and the second index refers to the signal parameter (e.g. time)
% For mixed images, X is 3-D where the first index refers to the different 
% mixed images and the second and third indeces are the spatial coordinates.
%
% [Y W] = CCABSS(X) also gives the 'de-mixing' matrix W, such that Y = W'*X.
%
% © 2000 Magnus Borga


switch ndims(x)
 case 2 % 1D signals
  spatial_mode = 0;
  A = x(:,2:end-1);
  B = conv2(x,[1 0 1],'valid'); % Temporal correlation
  [wa wb r] = cc(A,B); % CCA
  y = wa'*x;
 case 3 % 2D signals
  spatial_mode = 1;
  x_size = size(x);
  im_size = x_size(2)*x_size(3);
  ab_size = (x_size(2)-2)*(x_size(3)-2);
  for k = 1:x_size(1) % Flatten 2D-signals after convolution
    X(k,:) = reshape(x(k,:,:),1,im_size);
    A(k,:) = reshape(x(k,2:end-1,2:end-1),1,ab_size);
    B(k,:) = reshape(conv2(squeeze(x(k,:,:)),[0 1 0;1 0 1;0 1 0],'valid'),1,ab_size);
  end
    [wa wb r] = cc(A,B); % CCA
  for k = 1:x_size(1) % Flatten 2D-signals after convolution
    y(k,:,:) = reshape(wa(:,k)'*X,x_size(2),x_size(3));
  end
 otherwise, error('x must be 2- or 3-dimensional.')
end

if nargout > 1
  w = wa;
  r = r;
end

% ------------
% --- CCA ----
% ------------

function [Wx, Wy, r] = cc(X,Y)

% --- Calculate covariance matrices ---

z = [X;Y];
C = cov(z.');
sx = size(X,1);
sy = size(Y,1);
Cxx = C(1:sx, 1:sx) + 10^(-8)*eye(sx);
Cxy = C(1:sx, sx+1:sx+sy);
Cyx = Cxy';
Cyy = C(sx+1:sx+sy, sx+1:sx+sy) + 10^(-8)*eye(sy);
invCyy = inv(Cyy);

% --- Calcualte Wx and r ---

[Wx,r] = eig(inv(Cxx)*Cxy*invCyy*Cyx); % Basis in X
r = sqrt(real(r));      % Canonical correlations

% --- Sort correlations ---

V = fliplr(Wx);     % reverse order of eigenvectors
r = flipud(diag(r));    % extract eigenvalues anr reverse their orrer
[r,I]= sort((real(r))); % sort reversed eigenvalues in ascending order
r = flipud(r);      % restore sorted eigenvalues into descending order
for j = 1:length(I)
  Wx(:,j) = V(:,I(j));  % sort reversed eigenvectors in ascending order
end
Wx = fliplr(Wx);    % restore sorted eigenvectors into descending order

% --- Calcualte Wy  ---

Wy = invCyy*Cyx*Wx;     % Basis in Y
Wy = Wy./repmat(sqrt(sum(abs(Wy).^2)),sy,1); % Normalize Wy
