Example: Dual basis pursuit denoising (dual BPD) with the DFT and STFT

Decompose a signal into two distinct signal components using dual basis pursuit denoising (dual BPD); i.e., MCA. In this example, the data is the sum of a sinusoid, a pulse, and white noise. The DFT and STFT are used for the two transforms.

Ivan Selesnick
November, 2013
Revised January, 2014

Contents

Start

clear
% close all

I = sqrt(-1);

Create test data

Sinusoid + pulse + white noise

N = 100;                    % N : signal length
n = 0:N-1;

f1 = 0.1;
s1 = sin(2*pi*f1*n);      % s1 : sinusoid

f2 = 0.15;
K = 20;                     % K : duration of pulse
M = 55;                     % M : start-time of pulse
A = 3;
s2 = zeros(size(n));        % s2 : pulse
s2(M+(1:K)) = A*sin(2*pi*f2*(1:K)) .* hamming(K)';

randn('state', 0);          % set state for reproducibility of example

sigma = 1;                  % sigma : noise standard deviation
w = sigma/sqrt(2) * randn(1, N);
                            % w : white Gaussian noise

y = s1 + s2 + w;            % y : two components plus noise

figure(1)
clf

subplot(2, 1, 1)
plot(n, y)
title('Noisy data')
xlabel('Time')

Define transforms

Nfft1 = 256;
[A1, A1H, normA1] = MakeTransforms('DFT', N, Nfft1);

R = 16;         % R : frame length
Nfft2 = 16;      % Nfft2 : FFT length in STFT (Nfft >= R)
[A2, A2H, normA2] = MakeTransforms('STFT', N, [R Nfft2]);
Reconstruction error = 0.000000
Energy ratio = 1.000000
Reconstruction error = 0.000000
Energy ratio = 1.000000

Peform signal separation using dual BPD

% Algorithm parameters
beta = 2.5;
lam1 = beta * sigma * normA1;
lam2 = beta * sigma * normA2;

Nit = 100;                  % Nit : number of iterations
mu = 1.0;                   % mu : ADMM parameter

[x1, x2, c1, c2, cost] = dualBPD(y, A1, A1H, A2, A2H, lam1, lam2, mu, Nit);

% Display cost function history
figure(1)
clf
plot(cost)
title('Cost function history')
xlabel('Iteration')

Display signal components

r = y - x1 - x2;        % residual

% DFT component
subplot(3, 1, 1)
plot(n, real(x1))
title('DFT component [Output of dual BP]');
xlabel('Time')
ylabel('real( A1 c1)')

% STFT component
subplot(3, 1, 2)
plot(n, real(x2))
title('STFT component [Output of dual BP]');
xlabel('Time')
ylabel('real( A2 c2)')

% residual
subplot(3, 1, 3)
plot(n, real(y - x1 - x2))
title('Residual');
xlabel('Time')
ylabel('real( y - A1 c1 - A2 c2)')

Display sparse component coefficients

figure(1)
clf

f = (0:Nfft1-1)/Nfft1;

subplot(2, 1, 1)
plot(f, abs(c1))
title('FFT coefficients (c1)')

tt = R/2 * ( (0:size(c2, 2)) - 1 );  % tt : time axis for STFT

subplot(2, 1, 2)
imagesc(tt, [0 1], abs(c2))
axis xy
xlim([0 N])
ylim([0 1])
title('STFT coefficients (c2)')
xlabel('Time')
ylabel('Frequency')

cm = colormap(gray);                % cm : colormap for STFT
cm = cm(end:-1:1, :);
colormap(cm)

Optimality scatter plot (component 1)

g1 = A1H(y - A1(c1) - A2(c2)) .* exp(-I*angle(c1)) / lam1;

c1max = max(abs(c1(:)));

theta = linspace(0, 2*pi, 200);

figure(2)
clf
plot3( real(g1), imag(g1), abs(c1), '.')
zlabel('abs( c1 )')
% xlabel('Re(A1^H(y - A1 c1 - A2 c2) exp(-j\anglec))/\lambda_1')
% ylabel('Im(A1^H(y - A1 c1 - A2 c2) exp(-j\anglec))/\lambda_1')
title('Scatter plot (component 1)')
grid off
box off
line( cos(theta), sin(theta), 'color', [1 1 1]*0.5)
line([1 1], [0 0], [0 c1max],  'color', [1 1 1]*0.5)
xlim([-1 1] * 1.2)
ylim([-1 1] * 1.2)
line([-1 1]*1.2, [1 1]*1.2, 'color', 'black')
line([1 1]*1.2, [-1 1]*1.2, 'color', 'black')

% Animate: vary view orientation
az = -36;
for el = [40:-1:5]
    view([az el])
    drawnow
end
for az = [-36:-3]
    view([az el])
    drawnow
end

Optimality scatter plot (component 2)

g2 = A2H(y - A1(c1) - A2(c2)) .* exp(-I*angle(c2)) / lam2;

c2max = max(abs(c2(:)));

figure(2)
clf
plot3( real(g2(:)), imag(g2(:)), abs(c2(:)), '.')
zlabel('abs( c2 )')
% xlabel('Re(A2^H(y - A1 c1 - A2 c2) exp(-j\anglec))/\lambda_2')
% ylabel('Im(A2^H(y - A1 c1 - A2 c2) exp(-j\anglec))/\lambda_2')
title('Scatter plot (component 2)')
grid off
box off
line( cos(theta), sin(theta), 'color', [1 1 1]*0.5)
line([1 1], [0 0], [0 c2max],  'color', [1 1 1]*0.5)
xlim([-1 1] * 1.2)
ylim([-1 1] * 1.2)
line([-1 1]*1.2, [1 1]*1.2, 'color', 'black')
line([1 1]*1.2, [-1 1]*1.2, 'color', 'black')

% Animate: vary view orientation
az = -36;
for el = [40:-1:5]
    view([az el])
    drawnow
end
for az = [-36:-3]
    view([az el])
    drawnow
end

Save figure to file

MyGraphPrefs('on')

figure(3)
clf

ylim1 = [-4 4];

% Noise-free signal

subplot(3, 2, 1)
line(n, s1+s2)
mytitle('Noise-free signal (s1 + s2)');
xlabel('Time')
ylim(ylim1)

% Noisy data

subplot(3, 2, 2)
line(n, y)
mytitle('y = s1 + s2 + white noise');
xlabel('Time')
ylim(ylim1)

% Coefficients (output of dual BPD)

subplot(3, 2, 3)
line(f, abs(c1), 'marker', '.')
mytitle('DFT coefficients (c1) [Output of dual-BPD]');
xlabel('Frequency')
ylabel('abs( c1 )')
xlim([0 0.5])
ylim([0 7])

subplot(3, 2, 5)
imagesc(tt, [0 1], abs(c2))
axis xy
xlim([0 N])
ylim([0 0.5])
mytitle('STFT coefficients (c2) [Output of dual-BPD]');
xlabel('Time')
ylabel('Frequency')
colormap(cm)
box off

% Signals (reconstructed from dual-BPD coefficients)

subplot(3, 2, 4)
line(n, real(x1))
mytitle('Sinusoid component (reconstructed from c1)');
ylabel('real(A1 c1)')
xlabel('Time')
ylim(ylim1)

subplot(3, 2, 6)
line(n, real(x2))
ylim(ylim1)
mytitle('Pulse component (reconstructed from c2)');
ylabel('real(A2 c2)')
xlabel('Time')


% print figure to pdf file
orient landscape
print -dpdf figures/dualBPD_example1

% print figure to eps file
orient portrait
set(gcf, 'PaperPosition', [1 1 7.5 6])
print -deps figures_eps/dualBPD_example1

MyGraphPrefs('off')

Animate: vary lambda

figure(4)
clf

% Modify figure size (try to make larger than default)
ss = get(0, 'screensize');
set(gcf, 'position', round([0.2*ss(3), 0.1*ss(4), 0.4*ss(3), 0.8*ss(4)]));
% [left bottom width height]

for beta = 0.1:0.1:3

    lam1 = beta * sigma * normA1;
    lam2 = beta * sigma * normA2;
    [x1, x2, c1, c2, cost] = dualBPD(y, A1, A1H, A2, A2H, lam1, lam2, mu, Nit);

    subplot(4, 1, 1)
    plot(f, abs(c1), '.-')
    title(sprintf('DFT coefficients (c1), \\lambda1 = \\beta\\sigma ||a1||, \\beta = %.1f', beta));
    ylabel('abs( c1 )')
    xlabel('Frequency')
    xlim([0 0.5])
    ylim([0 1.5*c1max])

    subplot(4, 1, 2)
    plot(n, real(x1))
    ylim([-1 1]*2)
    title(sprintf('DFT component, \\beta = %.1f', beta));
    ylabel('real( A1 c1 )')
    xlabel('Time')

    subplot(4, 1, 3)
    imagesc(tt, [0 1], abs(c2))
    colormap(cm)
    axis xy
    xlim([0 N])
    ylim([0 0.5])
    title(sprintf('STFT coefficients (c2), \\lambda2 = \\beta\\sigma ||a2||, \\beta = %.1f', beta));
    xlabel('Time')
    ylabel('Frequency')

    subplot(4, 1, 4)
    plot(n, real(x2))
    ylim([-1 1]*3)
    title(sprintf('STFT component, \\beta = %.1f', beta));
    xlabel('Time')
    ylabel('real( A2 c2 )')

    drawnow

end