├── .gitignore ├── Auxiliary ├── deltaSNR.m ├── distAngle.m ├── distTransformL1.m ├── randCP.m ├── randl.m └── wrapAngle.m ├── Demos ├── demo_L1TV_Circ.m └── demo_L1TV_Real.m ├── Docs ├── L1TV_Circ.png └── L1TV_Real.png ├── L1TV_Circ.m ├── L1TV_Real.m ├── LICENSE ├── README.md └── setPath.m /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | unitTests/swUnitTestDistTransformL1.m 3 | 4 | Docs/create_images.m 5 | 6 | unitTests/swDistTransformL1FH.m 7 | 8 | unitTests/swUnitTestDistAngles.m 9 | 10 | unitTests/swUnitTestDistTransformNE.m 11 | 12 | unitTests/swUnitTestMinL1TVCirc.m 13 | -------------------------------------------------------------------------------- /Auxiliary/deltaSNR.m: -------------------------------------------------------------------------------- 1 | function dsnr = deltaSNR( groundTruth, data, estimate, dataSpace) 2 | %deltaSNR Computes the signal to noise ratio improvement of a restoration 3 | 4 | switch dataSpace 5 | case 'circ' 6 | dist = @(x,y) distAngle(x,y); 7 | case 'real' 8 | dist = @(x,y) abs(x - y); 9 | end 10 | 11 | dsnr = 10 * log10(sum( dist(groundTruth, data).^2 ) / sum( dist(groundTruth, estimate).^2 )); 12 | 13 | end 14 | 15 | -------------------------------------------------------------------------------- /Auxiliary/distAngle.m: -------------------------------------------------------------------------------- 1 | function d = distAngle( phi, psi ) 2 | %distAngle Distance between two angles phi and psi 3 | 4 | if (all(abs(phi) <= pi)) && (all(abs(psi) <= pi)) 5 | % cheap computation for angles in the interval [-pi, pi] 6 | aux = phi - psi; 7 | d = min(abs([aux(:) + 2 * pi, aux(:), aux(:) - 2 * pi]), [], 2); 8 | d = reshape(d, size(aux)); 9 | else 10 | % more expensive calculation for angles outside the interval [-pi, pi] 11 | aux = abs(angle(exp(1i * (phi - psi)))); 12 | d = min(aux, 2*pi-aux); 13 | end 14 | 15 | -------------------------------------------------------------------------------- /Auxiliary/distTransformL1.m: -------------------------------------------------------------------------------- 1 | function B = distTransformL1( B, R, alpha ) 2 | %distTransformL1 Fast computation of L1 distance transform 3 | 4 | K = numel(B); 5 | % forward pass 6 | for k=2:K 7 | B(k) = min( B(k), B(k-1) + alpha * (R(k) - R(k-1))); 8 | end 9 | % backward pass 10 | for k=K-1:-1:1 11 | B(k) = min( B(k), B(k+1) + alpha * (R(k+1) - R(k))); 12 | end 13 | 14 | end 15 | 16 | -------------------------------------------------------------------------------- /Auxiliary/randCP.m: -------------------------------------------------------------------------------- 1 | function r = randCP( r, lambda) 2 | %randCP Generates a random vector distributed according to a certain 3 | % compound Poisson distribution with parameter lambda 4 | 5 | x = rand(size(r)); % uniform distr. vector 6 | r(x(:) <= exp(-lambda)) = 0; % compound Poisson 7 | 8 | end 9 | 10 | -------------------------------------------------------------------------------- /Auxiliary/randl.m: -------------------------------------------------------------------------------- 1 | function y = randl( varargin ) 2 | %randl Random normal Laplacian noise, mean = 0, variance sigma^2 = 1 3 | % pdf: $\frac{1}{\sqrt{2}} e^{- \sqrt{2} |x|}$ 4 | % Same input arguments as function rand 5 | 6 | x = rand( varargin{:} ) - 0.5; 7 | y = -sign(x) .* log(1 - 2 * abs(x)) / sqrt(2); 8 | 9 | end 10 | 11 | -------------------------------------------------------------------------------- /Auxiliary/wrapAngle.m: -------------------------------------------------------------------------------- 1 | function x = wrapAngle( y ) 2 | %wrapAngle Wraps the angle y to the interval [-pi, pi] 3 | 4 | x = angle(exp(1i * y)); 5 | 6 | end 7 | 8 | -------------------------------------------------------------------------------- /Demos/demo_L1TV_Circ.m: -------------------------------------------------------------------------------- 1 | %%% Demo for denoising a circle-valued signal by the L1-TV model 2 | 3 | % create random signal (smoothed pcw constant signal) 4 | rng(12345) % random seed for reproducibility 5 | N = 2000; 6 | t = 2*pi; 7 | lambda = 20 / N; 8 | K = 20; 9 | sigma= 0.3; 10 | innovation = randCP((rand([N, 1])-0.5) * t, lambda ); 11 | signalUnwrapped = cumsum(innovation); 12 | h = fspecial('Gaussian', [N/10, 1], 10); 13 | smoothed = conv(signalUnwrapped, h, 'same'); 14 | groundTruth = wrapAngle(smoothed); 15 | 16 | % add noise 17 | y = wrapAngle(groundTruth + sigma* randl(size(groundTruth))); 18 | 19 | % perform restoration using L1TV_Circ 20 | alpha = sqrt(N)*sigma; 21 | x = L1TV_Circ(y, alpha); 22 | 23 | % plot the results 24 | figure('Color', 'w') 25 | subplot(1,2,1) 26 | plot(y, '.') 27 | ylim([-pi,pi]) 28 | title('Data with values on the unit circle') 29 | 30 | subplot(1,2,2) 31 | plot(x, '.') 32 | ylim([-pi,pi]) 33 | title(sprintf('L1TV restoration, SNR improvement: %.2f dB', deltaSNR(groundTruth, y, x, 'circ'))) 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /Demos/demo_L1TV_Real.m: -------------------------------------------------------------------------------- 1 | %%% Demo for denoising a circle-valued signal by the L1-TV model 2 | 3 | % create random signal (smoothed pcw constant signal) 4 | rng(12345) % random seed for reproducibility 5 | N = 2000; 6 | lambda = 20 / N; 7 | K = 20; 8 | scale = 100; % scale of signal (change to observe constrast invariance of L1TV) 9 | sigma= scale* 0.3; 10 | innovation = randCP(randn([N, 1]), lambda ); 11 | signal = scale * cumsum(innovation); 12 | h = fspecial('Gaussian', [N/10, 1], 10); 13 | groundTruth = conv(signal, h, 'same'); 14 | 15 | % add noise 16 | y = groundTruth + sigma * randl(size(groundTruth)); 17 | 18 | % perform restoration using L1TV_Real 19 | alpha = sqrt(N)*sigma/scale; 20 | x = L1TV_Real(y, alpha); 21 | 22 | % plot the results 23 | figure('Color', 'w') 24 | subplot(1,2,1) 25 | plot(y, '.') 26 | ylim_set = ylim; 27 | title('Real valued data') 28 | 29 | subplot(1,2,2) 30 | plot(x, '.') 31 | ylim(ylim_set); 32 | title(sprintf('L1TV restoration, SNR improvement: %.2f dB', deltaSNR(groundTruth, y, x, 'real'))) 33 | 34 | -------------------------------------------------------------------------------- /Docs/L1TV_Circ.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/L1TV/fdc0b2e24cd23db137ecb22c518d29bcdc0b4e81/Docs/L1TV_Circ.png -------------------------------------------------------------------------------- /Docs/L1TV_Real.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/L1TV/fdc0b2e24cd23db137ecb22c518d29bcdc0b4e81/Docs/L1TV_Real.png -------------------------------------------------------------------------------- /L1TV_Circ.m: -------------------------------------------------------------------------------- 1 | function x = L1TV_Circ( y, alpha, varargin ) 2 | %L1TV_Circ Exact solver for the univariate L1TV problem with circle valued 3 | %data. i.e. it computes the solution of 4 | % min_{x \in T^n} \alpha sum_{i=1}^{n-1} d(x_i, x_{i+1}) + sum_{i=1}^{n} w_i d(x_i, y_{i}) 5 | % where T is the unit circle (torus) and d(x, y) is the shortest arc 6 | % distance between x and y 7 | % 8 | % Input: 9 | % y: n-vector of phase angles between [-pi, pi] 10 | % alpha: regularization parameter 11 | % Optional arguments 12 | % 'weights': pointwise weights for the data fidelity (n-vector of positve numbers) 13 | % 14 | % Reference: 15 | % Storath, M., Weinmann, A., & Unser, M. (2016). 16 | % Exact algorithms for L^1-TV regularization of real-valued or circle-valued signals. 17 | % SIAM Journal on Scientific Computing, 38(1), A614-A630. 18 | 19 | 20 | % parse input 21 | ip = inputParser; 22 | ip.addParameter('useDistTrans', true); 23 | ip.addParameter('weights', ones(size(y))); 24 | 25 | parse(ip, varargin{:}); 26 | par = ip.Results; 27 | 28 | % initialization 29 | yCom = exp(1i * y); % complex number representation of y 30 | yAng = angle(yCom); % assure angle to be in [-pi, pi] 31 | uniqueValues = unique(yAng); % determine uniqe angles 32 | V = [uniqueValues; angle( -exp(1i * uniqueValues))]; % add antipodal points 33 | V = sort(V); % sort candidate values 34 | N = numel(yAng); 35 | K = numel(V); 36 | B = zeros(K,N); 37 | pen = zeros(K,1); 38 | 39 | 40 | % compute tabulation 41 | B(:,1) = par.weights(1) .* distAngle(V, yAng(1)); 42 | if par.useDistTrans % Using distance transforms, the complexity is O(N K) 43 | % auxiliary structure for distance transforms 44 | Vrep = cat(1, V-2*pi, V, V+2*pi); 45 | for n=2:N 46 | d = par.weights(n) .* distAngle(V, yAng(n)); 47 | Brep = repmat(B(:,n-1), [3,1]); 48 | aux = distTransformL1(Brep, Vrep, alpha); 49 | pen = min(reshape(aux, K, 3), [], 2); 50 | B(:,n) = d + pen; 51 | end 52 | else 53 | for n=2:N % Naive implementation is O(N K^2) 54 | d = par.weights(n) .* distAngle(V, yAng(n)); 55 | for k=1:K 56 | pen(k) = min( B(:,n-1) + alpha * distAngle(V, V(k))); 57 | end 58 | B(:,n) = d + pen; 59 | end 60 | 61 | end 62 | 63 | % backtracking 64 | [~,l] = min(B(:,N)); 65 | x(N,1) = V(l); 66 | for n=N-1:-1:1 67 | [~, l] = min(B(:, n) + alpha * distAngle(V, x(n+1))); 68 | x(n) = V(l); 69 | end 70 | 71 | 72 | end 73 | 74 | -------------------------------------------------------------------------------- /L1TV_Real.m: -------------------------------------------------------------------------------- 1 | function x = L1TV_Real( y, alpha, varargin ) 2 | %L1TV_Real Exact solver for the univariate L1TV problem with real-valued 3 | %data. i.e. it computes the solution of 4 | % min_{x \in R^n} \alpha sum_{i=1}^{n-1} |x_i - x_{i+1}| + sum_{i=1}^{n} w_i |x_i - y_{i}| 5 | % where R is the real numbers 6 | % 7 | % Input: 8 | % y: n-vector of real numbers 9 | % alpha: regularization parameter 10 | % Optional arguments 11 | % 'weights': pointwise weights for the data fidelity (n-vector of positve numbers) 12 | % 13 | % Reference: 14 | % Storath, M., Weinmann, A., & Unser, M. (2016). 15 | % Exact algorithms for L^1-TV regularization of real-valued or circle-valued signals. 16 | % SIAM Journal on Scientific Computing, 38(1), A614-A630. 17 | 18 | % parse input 19 | ip = inputParser; 20 | ip.addParameter('useDistTrans', true); 21 | ip.addParameter('weights', ones(size(y))); 22 | 23 | parse(ip, varargin{:}); 24 | par = ip.Results; 25 | 26 | % initialization 27 | uniqueValues = unique(y); % determine unique angles 28 | V = sort(uniqueValues); % sort values 29 | N = numel(y); 30 | K = numel(V); 31 | B = zeros(K,N); % tabulation 32 | pen = zeros(K,1); 33 | 34 | % tabulation 35 | B(:,1) = par.weights(1) .* abs(V - y(1)); 36 | for n=2:N 37 | d = par.weights(n) .* abs(V - y(n)); 38 | if par.useDistTrans % Using distance transforms, the complexity is O(NK) 39 | pen = distTransformL1(B(:,n-1), V, alpha); 40 | else % The naive implemention is O(NK^2) 41 | for k=1:K 42 | pen(k) = min( B(:,n-1) + alpha * abs(V - V(k))); 43 | end 44 | end 45 | B(:,n) = d + pen; 46 | end 47 | 48 | % backtracking 49 | [~,l] = min(B(:,N)); 50 | x(N,1) = V(l); 51 | for n=N-1:-1:1 52 | [~, l] = min(B(:, n) + alpha * abs(V - x(n+1))); 53 | x(n) = V(l); 54 | end 55 | 56 | 57 | end 58 | 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Martin Storath 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Matlab functions for estimation (denoising/reconstruction) of approximately piecewise constant signals. 2 | The functions are reference implementations of the method described in the paper 3 | 4 | M. Storath, A. Weinmann, M. Unser. Exact algorithms for L^1-TV regularization of real-valued or circle-valued signals. 5 | SIAM Journal on Scientific Computing, 38(1), A614-A630, 2016 6 | 7 | [Technical report here](https://arxiv.org/pdf/1504.00499.pdf) 8 | 9 | [See also Pottslab for related functions](https://github.com/mstorath/Pottslab) 10 | 11 | ## Estimation of real-valued signals 12 | Estimates a real-valued signal using the L1-TV model (exact non-iterative solver) 13 | 14 | ![L1 TV Denoising of real-valued signal](/Docs/L1TV_Real.png) 15 | 16 | ## Estimation of circle-valued signals 17 | Estimates a circle-valued signal using the L1-TV model (exact non-iterative solver) 18 | 19 | ![L1 TV Denoising of circle-valued signal](/Docs/L1TV_Circ.png) 20 | 21 | ## Installation and usage 22 | - Set the Matlab path by calling setPath.m 23 | - Run the demos of the Demos folder 24 | -------------------------------------------------------------------------------- /setPath.m: -------------------------------------------------------------------------------- 1 | % sets the paths 2 | disp('Setting Matlab path...'); 3 | folder = fileparts(which(mfilename)); 4 | addpath(... 5 | fullfile(folder, ''),... 6 | fullfile(folder, 'Auxiliary') ... 7 | ); 8 | 9 | % save pathdef 10 | savepath; --------------------------------------------------------------------------------