Code covered by the BSD License  

Highlights from
Toolbox signal

image thumbnail
from Toolbox signal by Gabriel Peyre
Signal processing related functions.

perform_moment_equalization(x,y,numdim, options)
function x = perform_moment_equalization(x,y,numdim, options)

% perform_kurtosis_equalization - equalize moments of order 1,2,3,4.
%
%   x = perform_moment_equalization(x,y,numdim,options);
%
%   (numdim=1 by default).
%
%   Equalizes the mean, variance, skewness and kurtosis.
%   Set options.xx=0 to avoid equalizing one of the moment, where
%       xx='skewness' or 'kurtosis'.
%
%   For 2D arrays, operates along columns unless you specify
%       x = perform_kurtosis_equalization(x,y,2);
%
% Copyright (c) 2006 Gabriel Peyr?

%   mem=mean2(chm);
%   sk2 = mean2((chm-mem).^3)/mean2((chm-mem).^2).^(3/2);

options.null = 0;
if nargin<3
    numdim = 1;
end

if size(x,1)>1 && size(x,2)>1
    if numdim>1
        x = x'; y = y';
    end
    p = size(x,2);
    for i=1:p
        if p>100
            progressbar(i,p);
        end
        x(:,i) = perform_moment_equalization( x(:,i),y(:,i),1,options );
    end
    if numdim>1
        x = x';
    end
    return;
end

dokurt = 1;
doskew = 1;
if isfield(options, 'kurtosis')
    dokurt = options.kurtosis;
end
if isfield(options, 'skewness')
    doskew = options.skewness;
end

niter = 1;


if std(x(:))<eps && std(y(:))>eps
    % regenerate random noise ...
    x = randn(size(x)) * std(y(:)) + mean(y(:));
end

if std(y(:))>eps
    sk = skew2(y);
    k = kurt2(y);
    for i=1:niter
        if dokurt
            [x, snrk] = modkurt(x,k);
        end
        if doskew
            [x, snrk] = modskew(x,sk);
        end
    end
end
% correct mean and variance
if std(x(:))>1e-9
    x = (x-mean(x(:))) * std(y(:))/std(x(:)) + mean(y(:));
end

function [chm, snrk] = modkurt(ch,k,p);

% Modify the kurtosis in one step, by moving in gradient direction until
% reaching the desired kurtosis value. 
% It does not affect the mean nor the variance, but it affects the skewness.
% This operation is not an orthogonal projection, but the projection angle is
% near pi/2 when k is close to the original kurtosis, which is a realistic assumption
% when doing iterative projections in a pyramid, for example (small corrections
% to the channels' statistics). 
%
% [chm, snrk] = modkurt(ch,k,p);
%	ch: channel
%	k: desired kurtosis (k=M4/M2^2)	
%       p [OPTIONAL]:   mixing proportion between k0 and k
%                       it imposes (1-p)*k0 + p*k,
%                       being k0 the current kurtosis.
%                       DEFAULT: p = 1;


% Javier Portilla,  Oct.12/97, NYU

Warn = 0;  % Set to 1 if you want to see warning messages
if ~exist('p'),
  p = 1;
end

me=mean2(ch);
ch=ch-me;

% Compute the moments

m=zeros(12,1);
for n=2:12,
	m(n)=mean2(ch.^n);
end

% The original kurtosis

k0=m(4)/m(2)^2;
snrk = snr(k, k-k0);
if snrk > 60,
	chm = ch+me;
	return
end
k = k0*(1-p) + k*p;

% Some auxiliar variables

a=m(4)/m(2);

% Coeficients of the numerator (A*lam^4+B*lam^3+C*lam^2+D*lam+E)

A=m(12)-4*a*m(10)-4*m(3)*m(9)+6*a^2*m(8)+12*a*m(3)*m(7)+6*m(3)^2*m(6)-...
	4*a^3*m(6)-12*a^2*m(3)*m(5)+a^4*m(4)-12*a*m(3)^2*m(4)+...
	4*a^3*m(3)^2+6*a^2*m(3)^2*m(2)-3*m(3)^4;
B=4*(m(10)-3*a*m(8)-3*m(3)*m(7)+3*a^2*m(6)+6*a*m(3)*m(5)+3*m(3)^2*m(4)-...
	a^3*m(4)-3*a^2*m(3)^2-3*m(4)*m(3)^2);
C=6*(m(8)-2*a*m(6)-2*m(3)*m(5)+a^2*m(4)+2*a*m(3)^2+m(3)^2*m(2));
D=4*(m(6)-a^2*m(2)-m(3)^2);
E=m(4);

% Define the coefficients of the denominator (F*lam^2+G)^2

F=D/4;
G=m(2);

% test
test = 0;

if test,

        grd = ch.^3 - a*ch - m(3);
        lam = -0.001:0.00001:0.001;
        k = (A*lam.^4+B*lam.^3+C*lam.^2+D*lam+E)./...
                (F*lam.^2 + G).^2;
        for lam = -0.001:0.00001:0.001,
                n = lam*100000+101;
                chp = ch + lam*grd;
                k2(n) = mean2(chp.^4)/mean2(chp.^2)^2;
                %k2(n) = mean2(chp.^4);
        end
        lam = -0.001:0.00001:0.001;
        snr(k2, k-k2)

end % test

% Now I compute its derivative with respect to lambda
% (only the roots of derivative = 0 )

d(1) = B*F;
d(2) = 2*C*F - 4*A*G;
d(3) = 4*F*D -3*B*G - D*F;
d(4) = 4*F*E - 2*C*G;
d(5) = -D*G;

mMlambda = roots(d);

tg = imag(mMlambda)./real(mMlambda);
mMlambda = mMlambda(find(abs(tg)<1e-6));
lNeg = mMlambda(find(mMlambda<0));
if length(lNeg)==0,
        lNeg = -1/eps;
end
lPos = mMlambda(find(mMlambda>=0));
if length(lPos)==0,
        lPos = 1/eps;
end
lmi = max(lNeg);
lma = min(lPos);

lam = [lmi lma];
mMnewKt = polyval([A B C D E],lam)./(polyval([F 0 G],lam)).^2;
kmin = min(mMnewKt);
kmax = max(mMnewKt);

% Given a desired kurtosis, solves for lambda

if k<=kmin
        lam = lmi;
        if Warn
        warning('Saturating (down) kurtosis!');
        kmin
        end
elseif k>=kmax
        lam = lma;
        if Warn
        warning('Saturating (up) kurtosis!');
        kmax
        end
else

% Coeficients of the algebraic equation

c0 = E - k*G^2;
c1 = D;
c2 = C - 2*k*F*G;
c3 = B;
c4 = A - k*F^2;

% Solves the equation

r=roots([c4 c3 c2 c1 c0]);

% Chose the real solution with minimum absolute value with the rigth sign

denom = real(r);
denom(abs(denom)<eps) = 1;
tg = imag(r)./denom;

%lambda = real(r(find(abs(tg)<1e-6)));
lambda = real(r(find(abs(tg)==0)));
if length(lambda)>0,
	lam = lambda(find(abs(lambda)==min(abs(lambda))));
	lam = lam(1);
else
	lam = 0;
end

end % if ... else


% Modify the channel

chm=ch+lam*(ch.^3-a*ch-m(3));	% adjust the kurtosis
chm=chm*sqrt(m(2)/mean2(chm.^2));	% adjust the variance
chm=chm+me;				% adjust the mean

% Check the result
%k2=mean2((chm-me).^4)/(mean2((chm-me).^2))^2;
%SNR=snr(k,k-k2)








 


function [chm, snrk] = modskew(ch,sk,p);

% Adjust the sample skewness of a vector/matrix, using gradient projection,
% without affecting its sample mean and variance.
%
% This operation is not an orthogonal projection, but the projection angle is
% near pi/2 when sk is close to the original skewness, which is a realistic
% assumption when doing iterative projections in a pyramid, for example
% (small corrections to the channels' statistics).
%
% 	[xm, snrk] = modskew(x,sk,p);
%		sk: new skweness
%       	p [OPTIONAL]:   mixing proportion between sk0 and sk 
%               	        it imposes (1-p)*sk0 + p*sk,
%                      		being sk0 the current skewness.
%                       	DEFAULT: p = 1;

%
% JPM. 2/98, IODV, CSIC
% 4/00, CNS, NYU

Warn = 0;  % Set to 1 if you want to see warning messages
if ~exist('p'), 
  p = 1;
end

N=prod(size(ch));	% number of samples
me=mean2(ch);
ch=ch-me;

for n=2:6,
	m(n)=mean2(ch.^n);
end

sd=sqrt(m(2));	% standard deviation
s=m(3)/sd^3;	% original skewness
snrk = snr(sk, sk-s); 
sk = s*(1-p) + sk*p;

% Define the coefficients of the numerator (A*lam^3+B*lam^2+C*lam+D)

A=m(6)-3*sd*s*m(5)+3*sd^2*(s^2-1)*m(4)+sd^6*(2+3*s^2-s^4);
B=3*(m(5)-2*sd*s*m(4)+sd^5*s^3);
C=3*(m(4)-sd^4*(1+s^2));
D=s*sd^3;

a(7)=A^2;
a(6)=2*A*B;
a(5)=B^2+2*A*C;
a(4)=2*(A*D+B*C);
a(3)=C^2+2*B*D;
a(2)=2*C*D;
a(1)=D^2;

% Define the coefficients of the denominator (A2+B2*lam^2)

A2=sd^2;
B2=m(4)-(1+s^2)*sd^4;

b=zeros(1,7);
b(7)=B2^3;
b(5)=3*A2*B2^2;
b(3)=3*A2^2*B2;
b(1)=A2^3;


if 0, % test

	lam = -2:0.02:2;
	S = (A*lam.^3+B*lam.^2+C*lam+D)./...
		sqrt(b(7)*lam.^6 + b(5)*lam.^4 + b(3)*lam.^2 + b(1));
%	grd = ch.^2 - m(2) - sd * s * ch;
%	for lam = -1:0.01:1,
%		n = lam*100+101;
%		chp = ch + lam*grd;
%		S2(n) = mean2(chp.^3)/abs(mean2(chp.^2))^(1.5);
%	end
	lam = -2:0.02:2;
figure(1);plot(lam,S);grid;drawnow
%	snr(S2, S-S2)

end % test

% Now I compute its derivative with respect to lambda

d(8) = B*b(7);
d(7) = 2*C*b(7) - A*b(5);
d(6) = 3*D*b(7);
d(5) = C*b(5) - 2*A*b(3);
d(4) = 2*D*b(5) - B*b(3);
d(3) = -3*A*b(1);
d(2) = D*b(3) - 2*B*b(1);
d(1) = -C*b(1);

d = d(8:-1:1);
mMlambda = roots(d);

denom = real(mMlambda);
[tmp,I] = find( min(abs(denom))<eps );
denom(I) = 1;
tg = imag(mMlambda)./denom;
mMlambda = real(mMlambda(find(abs(tg)<1e-6)));
lNeg = mMlambda(find(mMlambda<0));
if length(lNeg)==0,
	lNeg = -1/eps;
end
lPos = mMlambda(find(mMlambda>=0));
if length(lPos)==0,
        lPos = 1/eps;
end
lmi = max(lNeg);
lma = min(lPos);

lam = [lmi lma];
denom = (polyval(b(7:-1:1),lam)).^0.5;
if 0
[tmp,I] = find( min(abs(denom))<eps );
denom(I) = 1;
end
denom(abs(denom)<eps) = 1;

mMnewSt = polyval([A B C D],lam)./denom;
    
skmin = min(mMnewSt);
skmax = max(mMnewSt);


% Given a desired skewness, solves for lambda

if sk<=skmin
        lam = lmi;
        if Warn
            warning('Saturating (down) skewness!');
            skmin
        end
elseif sk>=skmax & Warn,
        lam = lma;
        if Warn
            warning('Saturating (up) skewness!');
            skmax
        end
else


% The equation is sum(c.*lam.^(0:6))=0

c=a-b*sk^2;

c=c(7:-1:1);

r=roots(c);

% Chose the real solution with minimum absolute value with the rigth sign
lam=-Inf;
co=0;
for n=1:min(6,length(r)),
    denom = real(r(n));
    [tmp,I] = find( min(abs(denom))<eps );
    denom(I) = 1;
	tg = imag(r(n))/denom;
	if (abs(tg)<1e-6)&(sign(real(r(n)))==sign(sk-s)),
		co=co+1;
		lam(co)=real(r(n));
	end
end
if min(abs(lam))==Inf
    if Warn
        display('Warning: Skew adjustment skipped!');
    end
	lam=0;
end

p=[A B C D];

if length(lam)>1,
	foo=sign(polyval(p,lam));
	if any(foo==0),
		lam = lam(find(foo==0));
	else
		lam = lam(find(foo==sign(sk)));		% rejects the symmetric solution
	end
	if length(lam)>0,
		lam=lam(find(abs(lam)==min(abs(lam))));	% the smallest that fix the skew
		lam=lam(1);
	else
		lam = 0;
	end
end
end % if else

% Modify the channel
chm=ch+lam*(ch.^2-sd^2-sd*s*ch);	% adjust the skewness
chm=chm*sqrt(m(2)/mean2(chm.^2));		% adjust the variance
chm=chm+me;				% adjust the mean
					% (These don't affect the skewness)
% Check the result
%mem=mean2(chm);
%sk2=mean2((chm-mem).^3)/mean2((chm-mem).^2).^(3/2);
%sk - sk2
%SNR=snr(sk,sk-sk2)





% S = SKEW2(MTX,MEAN,VAR)
%
% Sample skew (third moment divided by variance^3/2) of a matrix.
%  MEAN (optional) and VAR (optional) make the computation faster.

function res = skew2(mtx, mn, v)

if (exist('mn') ~= 1)
  mn =  mean2(mtx);
end

if (exist('v') ~= 1)
  v =  var2(mtx,mn);
end

if (isreal(mtx))
  res = mean(mean((mtx-mn).^3)) / (v^(3/2));
else
  res = mean(mean(real(mtx-mn).^3)) / (real(v)^(3/2)) + ...
      i * mean(mean(imag(mtx-mn).^3)) / (imag(v)^(3/2));
end


% K = KURT2(MTX,MEAN,VAR)
%
% Sample kurtosis (fourth moment divided by squared variance) 
% of a matrix.  Kurtosis of a Gaussian distribution is 3.
%  MEAN (optional) and VAR (optional) make the computation faster.

% Eero Simoncelli, 6/96.

function res = kurt2(mtx, mn, v)

if (exist('mn') ~= 1)
	mn =  mean(mean(mtx));
end

if (exist('v') ~= 1)
	v =  var2(mtx,mn);
end

if (isreal(mtx))
  res = mean(mean(abs(mtx-mn).^4)) / (v^2);
else
  res = mean(mean(real(mtx-mn).^4)) / (real(v)^2) + ...
      i*mean(mean(imag(mtx-mn).^4)) / (imag(v)^2);
end


% M = MEAN2(MTX)
%
% Sample mean of a matrix.

function res = mean2(mtx)

res = mean(mean(mtx));


% V = VAR2(MTX,MEAN)
%
% Sample variance of a matrix.
%  Passing MEAN (optional) makes the calculation faster.

function res = var2(mtx, mn)

if (exist('mn') ~= 1)
  mn =  mean2(mtx);
end

if (isreal(mtx))
  res = sum(sum(abs(mtx-mn).^2)) / max((prod(size(mtx)) - 1),1);
else
  res = sum(sum(real(mtx-mn).^2)) + i*sum(sum(imag(mtx-mn).^2));
  res = res  / max((prod(size(mtx)) - 1),1);
end


function X=snr(s,n);

% Compute the signal-to-noise ratio in dB
%  	X=SNR(signal,noise);
% (it does not subtract the means).

es=sum(sum(abs(s).^2));
en=sum(sum(abs(n).^2));
if en<eps
    X = en;
else
    X=10*log10(es/en);
end

Contact us at files@mathworks.com