├── Auxiliary ├── Operators │ ├── convop.m │ ├── linop.m │ └── radonop.m ├── convkernel.m ├── countJumps.m ├── energyL2Potts.m ├── intmatrix.m ├── medianw.m ├── plimage2double.m ├── plpsnr.m ├── psnr.m ├── randidx.m ├── randl.m ├── samplesToWeights.m ├── segToLabel.m ├── setPLJavaPath.m ├── showPotts.m ├── showSparse.m ├── showdbl.m ├── softThreshold.m ├── spconvmatrix.m └── spdiffmatrix.m ├── Data ├── church.jpg ├── colors.png ├── desert.jpg ├── imageSources.txt ├── loadPcwConst.m ├── loadSparse.m └── lookmickey.jpg ├── Demos ├── 1D │ ├── demoL1iPotts_DecImp.m │ ├── demoL1iPotts_DecLap.m │ ├── demoL1iSpars_DecImp.m │ ├── demoL1iSpars_DecLap.m │ ├── demoL2iPotts_Deconv.m │ ├── demoL2iPotts_Fourier.m │ ├── demoL2iPotts_Linop.m │ ├── demoL2iSpars_Deconv.m │ ├── demoPotts_GaussianNoise.m │ ├── demoPotts_ImpulsiveNoise.m │ └── demoPotts_LaplacianNoise.m ├── 2D │ ├── demoPotts2DColorDenoising.m │ ├── demoPotts2DColorInpainting.m │ ├── demoPotts2DColorMissing.m │ ├── demoPotts2DColorSegmentation.m │ ├── demoPotts2DRadon.m │ └── results │ │ └── plFigPottsRec7Angles.fig └── pottsLabDemo.m ├── Docs ├── deconv │ ├── noisy.png │ └── uPottsRho.png ├── potts1d.png ├── radon │ ├── phantom.png │ ├── recFBP.png │ ├── recFBPRamLak.png │ └── recPotts.png ├── texture.png └── titleImage.png ├── Java ├── bin │ └── pottslab │ │ ├── IndexedLinkedHistogram$HistNode.class │ │ ├── IndexedLinkedHistogram.class │ │ ├── IndexedLinkedHistogramUnweighted$HistNode.class │ │ ├── IndexedLinkedHistogramUnweighted.class │ │ ├── JavaTools.class │ │ ├── L2Potts.class │ │ ├── PLImage$1.class │ │ ├── PLImage.class │ │ ├── PLProcessor.class │ │ └── PLVector.class └── src │ ├── META-INF │ └── MANIFEST.MF │ └── pottslab │ ├── IndexedLinkedHistogram.java │ ├── IndexedLinkedHistogramUnweighted.java │ ├── JavaTools.java │ ├── L2Potts.java │ ├── PLImage.java │ ├── PLProcessor.java │ ├── PLVector.java │ └── RunMe.java ├── License.txt ├── Plugins └── PottsSegmentationJ_.jar ├── Potts ├── PottsCore │ ├── findBestPartition.m │ ├── iPottsADMM.m │ └── reconstructionFromPartition.m ├── minL1Potts.m ├── minL1iPotts.m ├── minL2Potts.m └── minL2iPotts.m ├── Potts2D ├── Core │ └── iPotts2DADMM.m ├── minL2Potts2DADMM.m └── minL2iPotts2DADMM.m ├── README.md ├── Sparsity ├── SparsityCore │ ├── iSparsADMM.m │ ├── iSparsByPottsADMM.m │ └── minSpars.m ├── minL1Spars.m ├── minL1iSpars.m ├── minL2Spars.m └── minL2iSpars.m ├── Tikhonov ├── minL1Tikhonov.m ├── minL2Tikhonov.m └── minL2TikhonovFBP.m ├── installPottslab.m └── pottslab-standalone.jar /Auxiliary/Operators/convop.m: -------------------------------------------------------------------------------- 1 | classdef convop < double 2 | %convop A class for circular convolution by FFT 3 | 4 | % written by M. Storath 5 | % $Date: 2014-05-07 11:42:11 +0200 (Mi, 07. Mai 2014) $ $Revision: 89 $ 6 | 7 | 8 | methods 9 | % constructor 10 | function obj = convop(fourier) 11 | obj = obj@double(fourier); 12 | end 13 | 14 | % ctranspose (') 15 | function C = ctranspose(A) 16 | C = convop(conj(A)); 17 | end 18 | 19 | % mtimes (*) 20 | function C = mtimes( A, B ) 21 | if isa(B, 'convop') 22 | C = convop( A .* B ); 23 | else 24 | C = ifftn( A .* fftn(B) ); 25 | end 26 | end 27 | 28 | end 29 | end 30 | 31 | -------------------------------------------------------------------------------- /Auxiliary/Operators/linop.m: -------------------------------------------------------------------------------- 1 | classdef linop 2 | %linop A class for linear operators 3 | 4 | % written by M. Storath 5 | % $Date: 2015-10-15 15:07:43 +0200 (Do, 15. Okt 2015) $ $Revision: 132 $ 6 | 7 | 8 | properties 9 | % function handle for evaluation A * x 10 | eval; 11 | % function handle for evaluation A' * x 12 | ctrans; 13 | % A'A 14 | normalOp; 15 | % true if A'A positive definite 16 | posdef; 17 | % true if A'A positive definite 18 | lseSolver; 19 | end 20 | 21 | methods 22 | % constructor 23 | function A = linop(eval, ctrans, varargin) 24 | % if it is a matrix 25 | if ~isa(eval, 'function_handle') 26 | A.eval = @(x) eval * x; 27 | A.ctrans = @(x) eval' * x; 28 | M = eval' * eval; 29 | A.normalOp = @(x) M * x; 30 | else 31 | A.eval = eval; 32 | A.ctrans = ctrans; 33 | A.normalOp = @(x) A.ctrans(A.eval(x)); 34 | end 35 | ip = inputParser; 36 | addParamValue(ip, 'posdef', false); 37 | addParamValue(ip, 'lseSolver', []); 38 | parse(ip, varargin{:}); 39 | par = ip.Results; 40 | A.posdef = par.posdef; 41 | A.lseSolver = par.lseSolver; 42 | end 43 | 44 | % ctranspose (') 45 | function C = ctranspose(A) 46 | C = linop(A.ctrans, A.eval); 47 | end 48 | 49 | % mtimes (*) 50 | function C = mtimes( A, B ) 51 | if isa(B, 'linop') 52 | C = linop( @(x) A.eval(B.eval(x)), @(x) B.ctrans(A.ctrans(x))); 53 | else 54 | C = A.eval(B); 55 | end 56 | end 57 | 58 | % size 59 | function s = size(A) 60 | %warning('Size is deprecated for class linop'); 61 | s = [1 1]; 62 | end 63 | 64 | end 65 | end 66 | 67 | -------------------------------------------------------------------------------- /Auxiliary/Operators/radonop.m: -------------------------------------------------------------------------------- 1 | classdef radonop < linop 2 | %radonop A class for Radon transform 3 | 4 | % written by M. Storath 5 | % $Date: 2014-09-02 14:56:23 +0200 (Di, 02. Sep 2014) $ $Revision: 105 $ 6 | 7 | properties 8 | % solution method for prox 9 | useFBP; 10 | end 11 | 12 | methods 13 | % constructor 14 | function A = radonop(theta, imgSize) 15 | 16 | eval = @(x) radon(x, theta); 17 | ctrans = @(x) iradon(x, theta, 'linear', 'None', 1, imgSize); 18 | 19 | A = A@linop(eval, ctrans); 20 | A.posdef = true; 21 | A.useFBP = false; 22 | end 23 | end 24 | end 25 | 26 | -------------------------------------------------------------------------------- /Auxiliary/convkernel.m: -------------------------------------------------------------------------------- 1 | function h = convkernel( type, n, scale ) 2 | %CONVKERNEL Creates a convolution kernel 3 | 4 | % written by M. Storath 5 | % $Date: 2012/10/29 01:19:08 $ $Revision: 0.1 $ 6 | 7 | if not(exist('scale', 'var')) 8 | scale = 1; 9 | end 10 | 11 | % set coordinates 12 | d = n/2; 13 | x = linspace(-d, d, n)/scale; 14 | 15 | switch type 16 | case 'box' 17 | h = ones(n, 1); 18 | 19 | case 'hat' 20 | h(x >= 0) = 1-x(x >= 0); 21 | h(x < 0) = 1 + x(x < 0); 22 | h(abs(x) > 1) = 0; 23 | 24 | case 'gaussian' 25 | h = exp(-0.5 * x.^2); 26 | 27 | case 'doubleexp' 28 | h = exp(-0.5 * abs(x)); 29 | 30 | case 'mollifier' 31 | h = exp(-1./(1 - abs(x).^2)); 32 | h(abs(x) > 1) = 0; 33 | 34 | case 'ramp' 35 | h(x >= 0) = 1-x(x >= 0); 36 | h(abs(x) > 1) = 0; 37 | 38 | otherwise 39 | error('This option does not exist.') 40 | end 41 | 42 | % normalize 43 | h = h(:)./sum(h); 44 | 45 | end 46 | 47 | -------------------------------------------------------------------------------- /Auxiliary/countJumps.m: -------------------------------------------------------------------------------- 1 | function j = countJumps( f ) 2 | %COUNTJUMPS Counts the jumps of a vector f 3 | j = sum( diff(f) ~= 0); 4 | 5 | end 6 | 7 | -------------------------------------------------------------------------------- /Auxiliary/energyL2Potts.m: -------------------------------------------------------------------------------- 1 | function [energy, jumpPenalty, dataError] = energyL2Potts( u, f, gamma, A, isotropic ) 2 | %energyL2Potts Computes the energy of the L2 Potts functional 3 | 4 | if exist('A', 'var') && not(isempty(A)) 5 | Au = A * u; 6 | dataError = sum((Au(:) - f(:)).^2); 7 | else 8 | dataError = sum((u(:) - f(:)).^2); 9 | end 10 | 11 | 12 | if isvector(u) 13 | % vectors 14 | jumpPenalty = sum(diff(u(:)) ~= 0); 15 | else 16 | % matrices 17 | nJumpComp = 0; % jumps in compass directions 18 | nJumpDiag = 0; % jumps in diagonal directions 19 | 20 | % count jumps 21 | for i = 1:size(u, 1) 22 | for j = 1:size(u, 2)-1 23 | if ~all((u(i,j,:) == u(i,j+1,:))) 24 | nJumpComp = nJumpComp + 1; 25 | end 26 | end 27 | end 28 | for i = 1:size(u, 1)-1 29 | for j = 1:size(u, 2) 30 | if ~all((u(i,j,:) == u(i+1,j,:))) 31 | nJumpComp = nJumpComp + 1; 32 | end 33 | end 34 | end 35 | for i = 1:size(u, 1)-1 36 | for j = 1:size(u, 2)-1 37 | if ~all((u(i,j,:) == u(i+1,j+1,:))) 38 | nJumpDiag = nJumpDiag + 1; 39 | end 40 | end 41 | end 42 | for i = 1:size(u, 1)-1 43 | for j = 2:size(u, 2) 44 | if ~all((u(i,j,:) == u(i+1,j-1,:))) 45 | nJumpDiag = nJumpDiag + 1; 46 | end 47 | end 48 | end 49 | 50 | % set weights (isotropic by default) 51 | if ~exist('isotropic', 'var') || isotropic 52 | omega1 = sqrt(2) - 1; 53 | omega2 = 1 - sqrt(2)/2; 54 | else 55 | omega1 = 1; 56 | omega2 = 0; 57 | end 58 | 59 | % compute energy 60 | jumpPenalty = (omega1 * nJumpComp + omega2* nJumpDiag); 61 | 62 | end 63 | 64 | energy = gamma * jumpPenalty + dataError; 65 | 66 | end -------------------------------------------------------------------------------- /Auxiliary/intmatrix.m: -------------------------------------------------------------------------------- 1 | function A = intmatrix( n ) 2 | %INTMATRIX Creates a matrix that acts as integration 3 | % (cumulative sum) on the input vector 4 | % 5 | % Au = cumsum(u) 6 | 7 | % written by M. Storath 8 | % $Date: 2012/10/29 01:19:08 $ $Revision: 0.1 $ 9 | 10 | A = tril(ones(n)); 11 | 12 | end 13 | 14 | -------------------------------------------------------------------------------- /Auxiliary/medianw.m: -------------------------------------------------------------------------------- 1 | function mu = medianw( data, weight ) 2 | % medianw Computes a weighted median of the data 3 | 4 | % written by M. Storath 5 | % $Date: 2012/10/29 01:19:08 $ $Revision: 0.1 $ 6 | 7 | if numel(data) == 1 8 | mu = data; 9 | else 10 | [dataSorted, idx] = sort(data); 11 | weightSorted = weight(idx); 12 | weightSum = sum(weight); 13 | cumWeight = cumsum(weightSorted)/weightSum; 14 | % find first median index 15 | idx = find(cumWeight < 0.5, 1, 'last' ) + 1; 16 | if isempty(idx) 17 | idx = 1; 18 | end 19 | mu = dataSorted(idx); 20 | end 21 | 22 | -------------------------------------------------------------------------------- /Auxiliary/plimage2double.m: -------------------------------------------------------------------------------- 1 | function img = plimage2double( plimg ) 2 | %PLIMAGE2DOUBLE Summary of this function goes here 3 | % Detailed explanation goes here 4 | img = reshape(plimg.toDouble(), [plimg.mRow, plimg.mCol, plimg.mLen]); 5 | 6 | end 7 | 8 | -------------------------------------------------------------------------------- /Auxiliary/plpsnr.m: -------------------------------------------------------------------------------- 1 | function r = plpsnr( groundTruth, signal ) 2 | %plPSNR Computes the peak signal to noise ratio 3 | 4 | % written by M. Storath 5 | % $Date: 2014-05-07 14:23:10 +0200 (Mi, 07. Mai 2014) $ $Revision: 90 $ 6 | 7 | mse = mean(abs(signal(:) - groundTruth(:)).^2); 8 | r = 10 * log10(max(groundTruth(:))^2 / mse); 9 | 10 | end -------------------------------------------------------------------------------- /Auxiliary/psnr.m: -------------------------------------------------------------------------------- 1 | function r = psnr( groundTruth, signal ) 2 | %PSNR Computes the peak signal to noise ratio 3 | 4 | % written by M. Storath 5 | % $Date: 2012/10/29 01:19:08 $ $Revision: 0.1 $ 6 | 7 | warning('The function psnr conflicts with the new function psnr from the image processing toolbox from Matlab v2014a. Use plpsnr instead.') 8 | 9 | mse = mean(abs(signal(:) - groundTruth(:)).^2); 10 | r = 10 * log10(max(groundTruth(:))^2 / mse); 11 | 12 | end 13 | 14 | -------------------------------------------------------------------------------- /Auxiliary/randidx.m: -------------------------------------------------------------------------------- 1 | function idx = randidx( siz, fraction ) 2 | %randidx Selects randomly a fraction of indices for a matrix of size siz 3 | 4 | % written by M. Storath 5 | % $Date: 2012/10/29 01:19:08 $ $Revision: 0.1 $ 6 | 7 | n = prod(siz); 8 | idx = randperm(n) ; 9 | idx = idx(1:round(fraction * n)); 10 | end 11 | 12 | -------------------------------------------------------------------------------- /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 | 5 | % written by M. Storath 6 | % $Date: 2012/10/29 01:19:08 $ $Revision: 0.1 $ 7 | 8 | x = rand( varargin{:} ) - 0.5; 9 | y = -sign(x) .* log(1 - 2 * abs(x)) / sqrt(2); 10 | end 11 | 12 | -------------------------------------------------------------------------------- /Auxiliary/samplesToWeights.m: -------------------------------------------------------------------------------- 1 | function weights = samplesToWeights( samples ) 2 | %samplesToWeights Computes weights from a set of sample points 3 | 4 | % written by M. Storath 5 | % $Date: 2012/10/29 01:19:08 $ $Revision: 0.1 $ 6 | 7 | %init 8 | weights = zeros(size(samples)); 9 | 10 | % first and last weight 11 | weights(1) = samples(2) - samples(1); 12 | weights(end) = samples(end) - samples(end-1); 13 | 14 | % central weights 15 | subs = 2:numel(samples)-1; 16 | weights(subs) = 0.5 * ( abs(samples(subs) - samples(subs-1)) ... 17 | + abs(samples(subs) - samples(subs+1)) ); 18 | 19 | end 20 | 21 | -------------------------------------------------------------------------------- /Auxiliary/segToLabel.m: -------------------------------------------------------------------------------- 1 | function labelImg = segToLabel(u, conn) 2 | %SEGTOLABEL Converts the output image of a segmentation function (e.g. minL2Potts2DADMM) to a 3 | %label image with integer labels (each connected component gets a unique integer) 4 | % u: image to label (typically the output of minL2Potts2DADMM)) 5 | % conn: connectivity as in bwconncomp (default: 8) 6 | 7 | if ~exist('conn', 'var') 8 | conn = 8; 9 | end 10 | 11 | [m,n,c] = size(u); 12 | vals = unique(reshape(u, m*n, c), 'rows'); 13 | labelImg = zeros(m, n); 14 | labelCounter = 0; 15 | for i=1:size(vals,1) 16 | bw_mult = (vals(i,:) == reshape(u, m*n, c)); 17 | bw = bw_mult(:,1); 18 | CC = bwconncomp(reshape(double(bw), m, n), conn); 19 | for j = 1:CC.NumObjects 20 | labelImg(CC.PixelIdxList{j}) = labelCounter; 21 | labelCounter = labelCounter + 1; 22 | end 23 | end 24 | 25 | end 26 | 27 | -------------------------------------------------------------------------------- /Auxiliary/setPLJavaPath.m: -------------------------------------------------------------------------------- 1 | function setPLJavaPath(static) 2 | 3 | % the path 4 | jpath = fullfile( fileparts(which(mfilename)), '..', 'Java', 'bin'); 5 | 6 | % dynamic path 7 | javaaddpath(jpath); 8 | 9 | % static path 10 | if exist('static', 'var') && static 11 | % static path (is faster, requires restart to take effect) 12 | upath = userpath; 13 | jpathfile = fullfile(upath, 'javaclasspath.txt'); 14 | disp(['Appending java class path to ' jpathfile]); 15 | pathExists = false; 16 | % check if path exists 17 | currentPath = javaclasspath('-static'); 18 | 19 | for i = 1:numel(currentPath) 20 | pathExists = strcmp(currentPath{i}, jpath) || pathExists; 21 | end 22 | % append path to classpath 23 | if ~pathExists 24 | try 25 | fid = fopen(jpathfile, 'at'); 26 | fprintf(fid, '\n'); 27 | % windows path need double backslash 28 | jpath = strrep(jpath,'\','\\'); 29 | fprintf(fid, jpath); 30 | fclose(fid); 31 | catch 32 | warning('Failed to add java path to static class path. Using dynamic classpath instead. This requires you to call setPLJavaPath.m each time you use Pottslab.') 33 | end 34 | end 35 | end 36 | 37 | 38 | 39 | end 40 | 41 | -------------------------------------------------------------------------------- /Auxiliary/showPotts.m: -------------------------------------------------------------------------------- 1 | function showPotts(data, rec, groundTruth, method) 2 | %showPotts Display result of Potts reconstructions 3 | 4 | subplot(1,2,1) 5 | plot(data, '.', 'MarkerSize', 10) 6 | title('Data') 7 | 8 | % reconstruction 9 | subplot(1,2,2) 10 | plot(rec, '.', 'MarkerSize', 10) 11 | 12 | % legend 13 | if ~exist('method', 'var') 14 | leg = {'Reconstruction'}; 15 | else 16 | leg = {method}; 17 | end 18 | 19 | % add groundTruth 20 | if exist('groundTruth', 'var') 21 | hold on 22 | plot(groundTruth, '--r') 23 | hold off 24 | leg = [leg, {'Ground truth'}]; 25 | title(['PSNR: ' num2str(plpsnr(groundTruth, rec)), ', #Jumps: ', num2str(countJumps(rec))] ); 26 | end 27 | 28 | legend(leg); 29 | 30 | end 31 | -------------------------------------------------------------------------------- /Auxiliary/showSparse.m: -------------------------------------------------------------------------------- 1 | function showSparse(data, rec, groundTruth, method) 2 | %showSparse Display result of sparse reconstructions 3 | 4 | % written by M. Storath 5 | % $Date: 2012/10/29 01:19:08 $ $Revision: 0.1 $ 6 | 7 | % data 8 | subplot(1,2,1) 9 | plot(data) 10 | title('Data') 11 | 12 | % reconstruction 13 | subplot(1,2,2) 14 | plot(rec, '.', 'MarkerSize', 10) 15 | 16 | % legend 17 | if ~exist('method', 'var') 18 | leg = {'Reconstruction'}; 19 | else 20 | leg = {method}; 21 | end 22 | 23 | % add groundTruth 24 | if exist('groundTruth', 'var') 25 | hold on 26 | stem(groundTruth, 'r') 27 | hold off 28 | leg = [leg, {'Ground truth'}]; 29 | title(['PSNR: ' num2str(plpsnr(groundTruth, rec))] ); 30 | end 31 | 32 | legend(leg); 33 | 34 | end 35 | -------------------------------------------------------------------------------- /Auxiliary/showdbl.m: -------------------------------------------------------------------------------- 1 | %show A show method for numeric data 2 | function showdbl( input, varargin ) 3 | 4 | if isreal(input) 5 | %minVal = min(input(:)); 6 | %maxVal = max(input(:)); 7 | if isvector(input) 8 | % in case of vector, we do a simple plot 9 | plot(input, varargin{:}); 10 | %title(['Min:' num2str(minVal) ', Max: ' num2str(maxVal) ]); 11 | elseif ndims(input) == 2 12 | % in case of 2D-image, we show the image 13 | colormap bone; 14 | imagesc(input,'CDataMapping','scaled', varargin{:}); 15 | axis equal; axis tight; 16 | %axis image; 17 | colorbar('location', 'SouthOutside'); 18 | %title(['Black:' num2str(minVal) ', White: ' num2str(maxVal) ]); 19 | elseif ndims(input) == 3 20 | if size(input, 3) == 3 21 | % in case of color 2D-image, we show the colored image 22 | imagesc(input); 23 | axis equal; axis tight; 24 | elseif islogical(input) 25 | [x y z] = ind2sub(size(input), find(input)); 26 | plot3(y, x, z, 'b.', 'MarkerSize', 0.5); 27 | grid on; 28 | axis equal; 29 | else 30 | % in case of true 3D-image, we show the central slices 31 | s = ceil ( size(input) / 2 ); 32 | slice(input, s(2), s(1), s(3), varargin{:}); 33 | shading flat; 34 | colormap gray; axis equal; 35 | end 36 | else 37 | error('More than 3 dimensions are not supported.') 38 | end 39 | else 40 | error('Input must be real.'); 41 | end 42 | 43 | set(gcf, 'color', 'white'); 44 | 45 | end 46 | 47 | -------------------------------------------------------------------------------- /Auxiliary/softThreshold.m: -------------------------------------------------------------------------------- 1 | function y = softThreshold(x, tau) 2 | %softThreshold The classical soft threshold function 3 | 4 | % written by M. Storath 5 | % $Date: 2013-01-05 17:25:45 +0100 (Sat, 05 Jan 2013) $ $Revision: 63 $ 6 | 7 | y = (abs(x) - tau) .* (abs(x) > tau) .* sign(x); 8 | end -------------------------------------------------------------------------------- /Auxiliary/spconvmatrix.m: -------------------------------------------------------------------------------- 1 | function A = spconvmatrix( filt, n ) 2 | %SPCONVMATRIX Computes a sparse convolution matrix of size n x n from the given 3 | %filter 4 | % 5 | % The output matrix A is such that it acts on a column vector as convolution, i.e., 6 | % 7 | % A v = filt * v 8 | 9 | % written by M. Storath 10 | % $Date: 2012/10/29 01:19:08 $ $Revision: 0.1 $ 11 | 12 | % convolution interchanges order 13 | filt = flipud(filt(:)); 14 | 15 | % build toeplitz matrix 16 | m = numel(filt); 17 | idx = (1:m) - ceil(numel(filt)/2); 18 | A = spdiags( ones(n, 1) * filt', idx, n, n); 19 | 20 | end 21 | 22 | -------------------------------------------------------------------------------- /Auxiliary/spdiffmatrix.m: -------------------------------------------------------------------------------- 1 | function A = spdiffmatrix( n ) 2 | %spdiffmatrix Creates a sparse matrix that acts as differentiation on the input vector 3 | % 4 | % Au = diff(u) 5 | 6 | % written by M. Storath 7 | % $Date: 2012/10/29 01:19:08 $ $Revision: 0.1 $ 8 | 9 | A = spdiags(ones(n-1, 1) * [-1 1], [0, 1], n-1, n); 10 | 11 | end 12 | 13 | -------------------------------------------------------------------------------- /Data/church.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Data/church.jpg -------------------------------------------------------------------------------- /Data/colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Data/colors.png -------------------------------------------------------------------------------- /Data/desert.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Data/desert.jpg -------------------------------------------------------------------------------- /Data/imageSources.txt: -------------------------------------------------------------------------------- 1 | The images stem from the following sources: 2 | 3 | The Berkeley Segmentation Dataset and Benchmark (http://www.eecs.berkeley.edu/Research/Projects/CS/vision/grouping/segbench/) 4 | - church.jpg 5 | - desert.jpg 6 | 7 | Wikipedia (http://en.wikipedia.org/wiki/Look_Mickey) 8 | - lookmickey.jpg (Copyright Board of Trustees, National Gallery of Art, Washington) 9 | 10 | Own production 11 | - colors.png -------------------------------------------------------------------------------- /Data/loadPcwConst.m: -------------------------------------------------------------------------------- 1 | function y = loadPcwConst(type, nSamples) 2 | %LOADPCWCONST Creates a piecewise constant signal or image 3 | 4 | % written by M. Storath 5 | % $Date: 2014-09-02 14:56:23 +0200 (Di, 02. Sep 2014) $ $Revision: 105 $ 6 | 7 | if not(exist('nSamples', 'var')) 8 | nSamples = 2^8; 9 | end 10 | 11 | 12 | x = linspace(-1, 1, nSamples)'; 13 | y = zeros(size(x)); 14 | 15 | switch type 16 | case 'rect' 17 | y = (-0.5 < x) & ( x < 0.5); 18 | case {'step', 'heaviside'} 19 | y = x >= 0; 20 | case 'jumps' 21 | % two jumps 22 | y = (-0.3 < x) & ( x < -0.1) + 2 * ((0.1 < x) & ( x < 0.4)); 23 | case 'equidistant' 24 | n = 8; % number of partitions 25 | step = nSamples / n; 26 | heights = [3, 1, 7, 6, 5, 0,6,4]; 27 | for i = 1:n 28 | iv = ((i-1) * step + 1) : (i * step); 29 | y(iv) = heights(i) / max(heights); 30 | end 31 | 32 | case 'sampleDec' 33 | heights = [3, 1, 7, 6, 5, 0,6,6]; 34 | jumps = diff(heights); 35 | jumpDist = [30, 40, 32, 32, 32, 50, 60]; 36 | jumpIdx = cumsum(jumpDist); 37 | y(jumpIdx) = jumps; 38 | y = cumsum(y); 39 | y = mat2gray(y); 40 | 41 | case 'sampleDec2' 42 | heights = [3, 1, 7, 6, 5, 0,6,6]; 43 | jumps = diff(heights); 44 | jumpDist = [30, 40, 32, 32, 32, 50, 40]; 45 | jumpIdx = cumsum(jumpDist); 46 | y(jumpIdx) = jumps; 47 | y = cumsum(y); 48 | y = mat2gray(y); 49 | 50 | case 'equidistant2' 51 | n = 8; % number of partitions 52 | step = nSamples / n; 53 | heights = [3, 1, 7, 6, 5, 4,6,4]; 54 | for i = 1:n 55 | iv = ((i-1) * step + 1) : (i * step); 56 | y(iv) = heights(i) / max(heights); 57 | end 58 | 59 | case 'sample1' 60 | steps = [2, 4, 3, 3, 2, 5, 3, 3, 3]; 61 | steps = (cumsum(steps ./ sum(steps)) - 0.5) * 2; 62 | heights = [3, 1, 7, 5.5, 4.5, 3.5,6,0]; 63 | n= numel(heights); 64 | for i = 1:n 65 | idx = find((steps(i) < x) & (x <= steps(i+1))); 66 | y( idx ) = heights(i) / max(heights); 67 | end 68 | 69 | case 'sample2' 70 | steps = [2, 4, 3, 3, 2, 5, 2, 3, 3,6,4,2,8,3,4, 4]; %15 71 | steps = (cumsum(steps ./ sum(steps)) - 0.5) * 2; 72 | heights = [3, 1, 7, 6, 5, 4,6,0, 2, 4, 3, 1.5, 7, 9, 2]; 73 | n= numel(heights); 74 | for i = 1:n 75 | idx = find((steps(i) < x) & (x <= steps(i+1))); 76 | y( idx ) = heights(i) / max(heights); 77 | end 78 | 79 | case 'sample3' 80 | steps = [2, 4, 3, 6, 4, 5, 4, 3, 3]; 81 | steps = (cumsum(steps ./ sum(steps)) - 0.5) * 2; 82 | heights = [3, 1, 7, 6, 5, 4,6,0]; 83 | n= numel(heights); 84 | for i = 1:n 85 | idx = find((steps(i) < x) & (x <= steps(i+1))); 86 | y( idx ) = heights(i) / max(heights); 87 | end 88 | 89 | case 'sample4' 90 | %steps = [3, 2, 5, 10, 4, 3, 3]; 91 | %steps = [ -Inf, (cumsum(steps ./ sum(steps)) - 0.5) * 2]; 92 | %heights = [4, 2,3, 3.1, 3.05, 1, 1]; 93 | steps = [2, 4, 2, 2, 1, 1]; 94 | steps = [ -Inf, (cumsum(steps ./ sum(steps)) - 0.5) * 2]; 95 | heights = [0.0, 0.1, 0.05, 1, 0.5, 0]; 96 | n= numel(heights); 97 | for i = 1:n 98 | idx = find((steps(i) < x) & (x <= steps(i+1))); 99 | y( idx ) = heights(i) / max(abs(heights)); 100 | end 101 | 102 | case 'sample5' 103 | steps = [3, 5, 3, 4, 6, 4, 3, 5, 3]; 104 | steps = (cumsum(steps ./ sum(steps)) - 0.5) * 2; 105 | heights = [3, 7, 2, 4, 5, 2,6,0]; 106 | n= numel(heights); 107 | for i = 1:n 108 | idx = find((steps(i) < x) & (x <= steps(i+1))); 109 | y( idx ) = heights(i) / max(heights); 110 | end 111 | 112 | case 'geo1' 113 | y = double(imread('geo1.png'))/255; 114 | 115 | case 'geo2' 116 | y = double(imread('geo2.png'))/255; 117 | 118 | case 'geo3' 119 | y = double(imread('geo3.png'))/255; 120 | 121 | case 'geo4' 122 | y = double(imread('geo4.png'))/255; 123 | 124 | case 'geo5' 125 | y = double(imread('geo5.png'))/255; 126 | 127 | case 'geo6' 128 | y = double(imread('geo6.png'))/255; 129 | 130 | case 'geo7' 131 | y = double(imread('geo7.png'))/255; 132 | 133 | case 'geo8' 134 | y = double(imread('geo8.png'))/255; 135 | 136 | case 'overlay' 137 | I1 = double(imread('rectangle.png')); 138 | I2 = double(imread('octo.png')); 139 | y = (I1 + 2*I2)/ (3*255); 140 | 141 | otherwise 142 | error('This option does not exist.') 143 | end 144 | 145 | % cast to double 146 | y = double(y); 147 | 148 | 149 | 150 | 151 | end 152 | -------------------------------------------------------------------------------- /Data/loadSparse.m: -------------------------------------------------------------------------------- 1 | function y = loadSparse(type, nSamples) 2 | %LOADSPARSE Creates a sparse signal or image 3 | 4 | % written by M. Storath 5 | % $Date: 2013-09-11 13:37:23 +0200 (Mi, 11. Sep 2013) $ $Revision: 79 $ 6 | 7 | d=0; 8 | if not(exist('nSamples', 'var')) 9 | nSamples = 2^(8 + d); 10 | end 11 | 12 | 13 | y = zeros(nSamples, 1); 14 | 15 | 16 | switch type 17 | case 'sig1' 18 | m = 2^d; 19 | y([50, 170] * m) = 2; 20 | y([100, 200] * m) = 3; 21 | y([190] * m) = -4; 22 | y([90] * m) = -2; 23 | y([120] * m) = 10; 24 | 25 | case 'sig2' 26 | m = 2^d; 27 | y([50, 170] * m) = 2; 28 | y([100, 200] * m) = 6; 29 | y([190] * m) = -4; 30 | y([90] * m) = -2; 31 | y([120] * m) = 10; 32 | 33 | case 'sig3' 34 | m = 2^d; 35 | y([50, 170] * m) = 2; 36 | y([100, 200] * m) = 3; 37 | y([190] * m) = -4; 38 | y([90] * m) = -2; 39 | y([120] * m) = 10; 40 | y([25, 75, 250] * m) = [-1, -1, 1]; 41 | 42 | case 'sigrand' 43 | idx = randidx(nSamples, 0.01); 44 | val = (rand(numel(idx), 1) - 0.5) * 5; 45 | y(idx) = val; 46 | 47 | case 'imgrand' 48 | n = 30; 49 | y = zeros(n); 50 | idx = randidx(numel(y), 0.02); 51 | %y(idx) = rand(1,numel(idx)); 52 | y(idx) = ceil(2 * (rand(1,numel(idx) )))/2 ; 53 | 54 | case 'img1' 55 | n = 40; 56 | y = zeros(n); 57 | a = [8, 7, 19, 35, 25]; 58 | b = [10, 37, 17, 10, 30]; 59 | y(a + n * b) = 1; 60 | 61 | case 'img2' 62 | n = 20; 63 | y = zeros(n); 64 | a = [8, 7, 8, 19, 6, 9]; 65 | b = [10, 17, 18, 10, 17, 10]; 66 | c = [-1, 2, 3, 1, 2, 2]; 67 | y(a + n * b) = c; 68 | 69 | case 'img3' 70 | n = 20; 71 | y = zeros(n); 72 | a = [8, 7, 19]; 73 | b = [10, 17, 10]; 74 | y(a + n * b) = 1; 75 | 76 | case 'img4' 77 | n = 100; 78 | y = zeros(n); 79 | a = [8, 7, 19, 35, 25, 90, 70, 70]; 80 | b = [10, 37, 17, 10, 30, 60, 30, 15]; 81 | y(a + n * b) = 1; 82 | 83 | case 'img5' 84 | n = 30; 85 | y = zeros(n); 86 | a = [27, 28, 29, 29, 28, 27]; 87 | b = 23:28; 88 | y(a + n * b) = 1; 89 | a = [20, 20]; 90 | b = [23,28]; 91 | y(a + n * b) = -2; 92 | 93 | otherwise 94 | error('Signal does not exist'); 95 | 96 | end 97 | 98 | % cast to double 99 | y = double(y); 100 | 101 | 102 | end 103 | -------------------------------------------------------------------------------- /Data/lookmickey.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Data/lookmickey.jpg -------------------------------------------------------------------------------- /Demos/1D/demoL1iPotts_DecImp.m: -------------------------------------------------------------------------------- 1 | %demoL1iPotts_DecImp 2 | % Reconstruction of a blurred jump-sparse signal from incomplete measurements under 3 | % impulsive noise using the inverse L1-Potts functional 4 | 5 | % load signal 6 | groundTruth = loadPcwConst('sampleDec'); 7 | n = numel(groundTruth); 8 | 9 | % create Gaussian kernel 10 | K = convkernel('gaussian', 51, 6); 11 | 12 | % create measurement matrix 13 | Afull = spconvmatrix(K, numel(groundTruth)); 14 | fraction = 0.5; 15 | idx = sort(randidx(n, fraction)) ; 16 | A = Afull(idx, :); 17 | 18 | % create blurred signal 19 | fBlurry = A * groundTruth(:); 20 | 21 | % impulsive noise (noiseFraction = number of pixels destroyed) 22 | noiseFraction = 0.3; 23 | ridx = randidx(numel(fBlurry), noiseFraction); 24 | f = fBlurry; 25 | f(ridx) = (rand(size(ridx))); 26 | 27 | % Solve inverse L1-Potts problem 28 | gamma = 0.4; 29 | u = minL1iPotts(f, gamma, A); 30 | 31 | % show result 32 | showPotts(f, u, groundTruth, 'L^1-iPotts') 33 | 34 | -------------------------------------------------------------------------------- /Demos/1D/demoL1iPotts_DecLap.m: -------------------------------------------------------------------------------- 1 | %demoL1iPotts_DecLap 2 | % Reconstruction of a blurred jump-sparse signal from incomplete measurements under 3 | % Laplacian noise by the inverse L1-Potts functional 4 | 5 | % load signal 6 | groundTruth = loadPcwConst('sampleDec'); 7 | n = numel(groundTruth); 8 | 9 | % create Gaussian kernel 10 | K = convkernel('gaussian', 51, 6); 11 | 12 | % create measurement matrix 13 | Afull = spconvmatrix(K, numel(groundTruth)); 14 | fraction = 0.5; 15 | idx = sort(randidx(n, fraction)) ; 16 | A = Afull(idx, :); 17 | 18 | % create blurred signal 19 | fBlurry = A * groundTruth; 20 | 21 | % add Laplacian noise of std. dev. sigma 22 | sigma = 0.05; 23 | fNoisy = fBlurry + sigma * randl(size(fBlurry)); 24 | 25 | % Solve inverse L1-Potts problems 26 | gamma = 0.7; 27 | u = minL1iPotts(fNoisy, gamma, A); 28 | 29 | % show result 30 | showPotts(fNoisy, u, groundTruth, 'L^1-iPotts') 31 | 32 | 33 | -------------------------------------------------------------------------------- /Demos/1D/demoL1iSpars_DecImp.m: -------------------------------------------------------------------------------- 1 | %demoL1iPotts_DecImp 2 | % Reconstruction of a blurred sparse signal from incomplete measurements under 3 | % impulsive noise using the inverse L1-Potts functional 4 | 5 | % create signal 6 | groundTruth = loadSparse('sig2'); 7 | n = numel(groundTruth); 8 | 9 | % create Gaussian convolution matrix 10 | K = convkernel('gaussian', 51, 5); 11 | Afull = spconvmatrix(K, n); 12 | % select randoom measurements 13 | idx = sort(randidx(numel(groundTruth), 0.5)); 14 | A = Afull(idx, :); 15 | 16 | % create blurred and noisy signal (impulsive noise) 17 | fBlurry = A * groundTruth; 18 | sigma = 0.3; 19 | ridx = randidx(numel(fBlurry), sigma); 20 | fNoisy = fBlurry; 21 | fNoisy(ridx) = (rand(size(ridx)) - 0.5); 22 | 23 | % reconstruction 24 | gamma = 0.45; 25 | u = minL1iSpars(fNoisy, gamma, A); 26 | 27 | % show result 28 | showSparse(fNoisy, u, groundTruth) 29 | 30 | 31 | -------------------------------------------------------------------------------- /Demos/1D/demoL1iSpars_DecLap.m: -------------------------------------------------------------------------------- 1 | %demoL1iPotts_DecLap 2 | % Reconstruction of a blurred sparse signal from incomplete measurements under 3 | % Laplacian noise using the inverse L1-Potts functional 4 | 5 | % create signal 6 | groundTruth = loadSparse('sig2'); 7 | n = numel(groundTruth); 8 | 9 | % create Gaussian convolution matrix 10 | K = convkernel('gaussian', 51, 5); 11 | Afull = spconvmatrix(K, n); 12 | 13 | % select random measurements 14 | idx = sort(randidx(numel(groundTruth), 0.5)); 15 | A = Afull(idx, :); 16 | 17 | % create blurred and noisy signal (Laplacian noise) 18 | fBlurry = A * groundTruth; 19 | fNoisy = fBlurry + 0.05* randl(size(fBlurry)); 20 | 21 | % reconstruction 22 | gamma = 0.5; 23 | u = minL1iSpars(fNoisy, gamma, A); 24 | 25 | % show result 26 | showSparse(fNoisy, u, groundTruth) 27 | 28 | -------------------------------------------------------------------------------- /Demos/1D/demoL2iPotts_Deconv.m: -------------------------------------------------------------------------------- 1 | %demoL2iPotts_Deconv 2 | % Reconstruction of a blurred jump-sparse signal from incomplete measurements under 3 | % Gaussian noise using the inverse L2-Potts functional 4 | 5 | % load signal 6 | groundTruth = loadPcwConst('sampleDec'); 7 | n = numel(groundTruth); 8 | 9 | % create Gaussian kernel 10 | K = convkernel('gaussian', 51, 6); 11 | Afull = spconvmatrix(K, numel(groundTruth)); 12 | 13 | % select random measurements 14 | idx = sort(randidx(n, 0.5)) ; 15 | A = Afull(idx, :); 16 | 17 | % create blurred and noisy signal (Gaussian noise) 18 | fBlurry = A * groundTruth(:); 19 | fNoisy = fBlurry + 0.05 * randn(size(fBlurry)); 20 | 21 | % reconstruction using the inverse L2-Potts problem 22 | gamma = 0.03; 23 | [u, dataError, nJumps, energy] = minL2iPotts(fNoisy, gamma, A); 24 | 25 | % show result 26 | showPotts(fNoisy, u, groundTruth, 'L^2-iPotts') 27 | -------------------------------------------------------------------------------- /Demos/1D/demoL2iPotts_Fourier.m: -------------------------------------------------------------------------------- 1 | %demoL2iPotts_Deconv 2 | % Reconstruction of a jump-sparse signal from incomplete Fourier measurements under 3 | % Gaussian noise using the inverse L2-Potts functional 4 | 5 | % load signal 6 | groundTruth = loadPcwConst('sampleDec'); 7 | 8 | % create reduced Fourier matrix 9 | n = numel(groundTruth); 10 | F = fft(eye(n)) / sqrt(n); 11 | fraction = 0.75; 12 | idx = [1, randidx(n-1, fraction) + 1]; % random indices but keep DC component 13 | A = F(idx, :); 14 | fFourier = A * groundTruth; 15 | 16 | % 17 | sigma = 0.05; 18 | noise = randn(size(groundTruth)) + 1i * randn(size(groundTruth)); 19 | fNoisy = fFourier + sigma * noise(idx); 20 | 21 | % reconstruction with inverse L^2-Potts functional 22 | gamma = 0.05; 23 | u = minL2iPotts(fNoisy, gamma, A); 24 | 25 | % show reconstruction 26 | showPotts(fftshift(log(abs(fNoisy))), real(u), groundTruth, 'L^2-iPotts') 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Demos/1D/demoL2iPotts_Linop.m: -------------------------------------------------------------------------------- 1 | %demoL2iPotts_Deconv 2 | % Reconstruction of a blurred jump-sparse signal from incomplete measurements under 3 | % Gaussian noise using the inverse L2-Potts functional 4 | % 5 | % Here it is demonstrated how to handle it if the operator A is given by a 6 | % function 7 | 8 | % load signal 9 | groundTruth = loadPcwConst('equidistant'); 10 | n = numel(groundTruth); 11 | 12 | % create Gaussian kernel 13 | K = convkernel('gaussian', 51, 6); 14 | Afull = spconvmatrix(K, numel(groundTruth)); 15 | 16 | % select random measurements 17 | idx = sort(randidx(n, 0.5)) ; 18 | A = Afull(idx, :); 19 | 20 | % create linear operator 21 | Aeval = @(x) A * x; %(evaluation) 22 | Atrans = @(x) A' * x; %(evaluation of conj. transpose of A) 23 | B = linop( Aeval, Atrans ); 24 | 25 | % create blurred and noisy signal (Gaussian noise) 26 | fBlurry = B * groundTruth(:); 27 | fNoisy = fBlurry + 0.05 * randn(size(fBlurry)); 28 | 29 | % reconstruction using the inverse L2-Potts problem 30 | gamma = 0.03; 31 | [u, dataError, nJumps, energy] = minL2iPotts(fNoisy, gamma, B); 32 | 33 | % show result 34 | showPotts(fNoisy, u, groundTruth, 'L^2-iPotts') 35 | -------------------------------------------------------------------------------- /Demos/1D/demoL2iSpars_Deconv.m: -------------------------------------------------------------------------------- 1 | %demoL2iPotts_DecLap 2 | % Reconstruction of a blurred sparse signal from incomplete measurements under 3 | % Laplacian noise using the inverse L2-Potts functional 4 | 5 | % create signal 6 | groundTruth = loadSparse('sig1'); 7 | n = numel(groundTruth); 8 | 9 | % create Gaussian convolution matrix 10 | K = convkernel('gaussian', 51, 5); 11 | Afull = spconvmatrix(K, n); 12 | 13 | % select random measurements 14 | idx = sort(randidx(numel(groundTruth), 0.5)); 15 | A = Afull(idx, :); 16 | 17 | % create blurred and noisy signal (Gaussian noise) 18 | fBlurry = A * groundTruth; 19 | sigma = 0.05; 20 | fNoisy = fBlurry + sigma* randn(size(fBlurry)); 21 | 22 | % reconstruction 23 | gamma = 0.025; 24 | [u, nSpikes] = minL2iSpars(fNoisy, gamma, A); 25 | 26 | % show result 27 | showSparse(fNoisy, u, groundTruth) 28 | -------------------------------------------------------------------------------- /Demos/1D/demoPotts_GaussianNoise.m: -------------------------------------------------------------------------------- 1 | %% Potts-denoising, Gaussian noise 2 | 3 | %% 4 | % Load signal 5 | original = loadPcwConst('sample1'); 6 | % Add Gaussian noise with parameter sigma = 0.1 7 | f = original + 0.1 * randn(size(original)); 8 | 9 | %% 10 | % Compute $L^2$ Potts estimator 11 | figure(1) 12 | pottsL2 = minL2Potts(f, 0.1); 13 | showPotts(f, pottsL2, original, 'L2-Potts') 14 | 15 | %% 16 | % Compute $L^1$ Potts estimator 17 | figure(2) 18 | pottsL1 = minL1Potts(f, 0.4); 19 | showPotts(f, pottsL1, original, 'L1-Potts') 20 | -------------------------------------------------------------------------------- /Demos/1D/demoPotts_ImpulsiveNoise.m: -------------------------------------------------------------------------------- 1 | %% Potts-denoising, impulsive noise 2 | 3 | %% 4 | % Load signal 5 | original = loadPcwConst('sample1'); 6 | % Impulsive noise, 30 % corrupted 7 | f = original; 8 | idx = randidx(size(f), 0.3); 9 | f(idx) = rand(size(idx)); 10 | 11 | %% 12 | % $L^2$ Potts estimator 13 | figure(1) 14 | pottsL2 = minL2Potts(f, 0.3); 15 | showPotts(f, pottsL2, original, 'L2-Potts') 16 | 17 | %% 18 | % $L^1$ Potts estimator 19 | figure(2) 20 | pottsL1 = minL1Potts(f, 1); 21 | showPotts(f, pottsL1, original, 'L1-Potts') 22 | -------------------------------------------------------------------------------- /Demos/1D/demoPotts_LaplacianNoise.m: -------------------------------------------------------------------------------- 1 | %% Potts-denoising, Laplacian noise 2 | 3 | %% 4 | % Load signal 5 | original = loadPcwConst('sample1'); 6 | % Add Laplacian noise with parameter $\lambda = 0.1$ 7 | f = original + 0.1 * randl(size(original)); 8 | 9 | %% 10 | % $L^2$ Potts estimator 11 | pottsL2 = minL2Potts(f, 0.6); 12 | figure(1) 13 | showPotts(f, pottsL2, original, 'L2-Potts') 14 | 15 | %% 16 | % $L^1$ Potts estimator 17 | pottsL1 = minL1Potts(f, 0.8); 18 | figure(2) 19 | showPotts(f, pottsL1, original, 'L1-Potts') -------------------------------------------------------------------------------- /Demos/2D/demoPotts2DColorDenoising.m: -------------------------------------------------------------------------------- 1 | % load image (image source: Wikipedia, painting of Roy Lichtenstein) 2 | img = double(imread('lookmickey.jpg'))/255; 3 | 4 | % add Gaussian noise 5 | rng(123); 6 | sigma = 0.3; 7 | imgNoisy = img + sigma * randn(size(img)); 8 | 9 | % Potts restoration 10 | gamma = 0.75; 11 | tic 12 | u = minL2Potts2DADMM(imgNoisy, gamma, 'verbose', true); 13 | toc 14 | 15 | % show results 16 | subplot(1,3,1) 17 | imshow(img) 18 | title('Original') 19 | subplot(1,3,2) 20 | imshow(imgNoisy) 21 | title('Noisy image') 22 | subplot(1,3,3) 23 | imshow(u) 24 | energyU = energyL2Potts(u, img, gamma); 25 | title(sprintf('Potts restoration (Potts energy: %.1f)', energyU)); -------------------------------------------------------------------------------- /Demos/2D/demoPotts2DColorInpainting.m: -------------------------------------------------------------------------------- 1 | % This is a demonstration of inpainting using the Potts model. 2 | % Note that it is an intrinsic property of the Potts model to 3 | % create two triple junctions instead of a four-junction in the center. 4 | 5 | % load image 6 | img = double(imread('colors.png'))/255; 7 | 8 | % set inpainting mask 9 | simg = sum(img, 3); 10 | weights = simg ~= 0; 11 | 12 | % add noise 13 | rng(123) % set seed value of random number gen. for reproducibility 14 | imgNoisy = img + 0.3 * randn(size(img)) .* cat(3, weights, weights, weights); 15 | 16 | %% Potts restoration 17 | gamma = 2; 18 | clear opts; 19 | opts.weights = weights; 20 | opts.verbose = true; 21 | opts.muStep = 1.1; 22 | opts.muInit = 1e-4; 23 | tic 24 | u = minL2Potts2DADMM(imgNoisy, gamma, opts); 25 | toc 26 | 27 | %% Show result 28 | subplot(1,2,1) 29 | imshow(imgNoisy) 30 | title('Noisy data (Black pixels are missing') 31 | subplot(1,2,2) 32 | imshow(u) 33 | title('Potts inpainting') -------------------------------------------------------------------------------- /Demos/2D/demoPotts2DColorMissing.m: -------------------------------------------------------------------------------- 1 | % load image 2 | img = double(imread('church.jpg'))/255; 3 | 4 | % add Gaussian noise 5 | sigma = 0.2; 6 | imgNoisy = img + sigma * randn(size(img)); 7 | 8 | % set weights and destroy image 9 | [m,n,l] = size(img); 10 | missingFraction = 0.6; 11 | weights = rand(m,n) > missingFraction; 12 | imgNoisy(~cat(3, weights, weights, weights)) = 0; 13 | 14 | %% Potts restoration 15 | gamma = 0.4; 16 | clear opts; 17 | opts.weights = weights; 18 | opts.verbose = true; 19 | tic 20 | u = minL2Potts2DADMM(imgNoisy, gamma, opts); 21 | toc 22 | 23 | %% Show result 24 | subplot(2,2,1) 25 | imshow(img) 26 | title('Original') 27 | subplot(2,2,2) 28 | imshow(imgNoisy) 29 | title('Noisy image and black pixels are missing') 30 | subplot(2,2,3) 31 | imshow(u) 32 | title('Potts restoration') -------------------------------------------------------------------------------- /Demos/2D/demoPotts2DColorSegmentation.m: -------------------------------------------------------------------------------- 1 | % load image (image source: The Berkeley Segmentation Dataset and Benchmark) 2 | img = double(imread('desert.jpg'))/255; 3 | scale = 0.5; 4 | img = imresize(img, scale); 5 | 6 | % Potts restoration 7 | gamma = 2 * scale; 8 | tic; 9 | u = minL2Potts2DADMM(img, gamma, 'verbose', true); 10 | toc 11 | 12 | %% 13 | subplot(1,3,1) 14 | imshow(img) 15 | energyImg = energyL2Potts(img, img, gamma); 16 | title(sprintf('Original (Potts energy: %.1f)', energyImg)); 17 | 18 | %% 19 | subplot(1,3,2) 20 | imshow(u) 21 | energyU = energyL2Potts(u, img, gamma); 22 | title(sprintf('Potts segmentation (Potts energy: %.1f)', energyU)); 23 | 24 | %% 25 | subplot(1,3,3) 26 | labelImg = segToLabel(u); 27 | imshow(label2rgb(labelImg)) 28 | title('Label Image'); 29 | 30 | -------------------------------------------------------------------------------- /Demos/2D/demoPotts2DRadon.m: -------------------------------------------------------------------------------- 1 | % This is a demonstration of Radon inversion from few angles using the Potts model. 2 | % Tested on Mac OS 10.9, Matlab 2015a, 3 | % If everything worked correctly a result as 4 | % in results/plFigPottsRec7Angles.fig should appear 5 | % Warning: This experiment can take more than one hour 6 | 7 | % load Shepp-Logan 8 | img = phantom(256); 9 | 10 | % Radon transform with 7 equidistant projection angles 11 | nAngles = 7; 12 | theta = (1:nAngles)/nAngles * 180; 13 | A = radonop(theta, size(img,1)); 14 | data = A * img; 15 | 16 | % raw filtered backprojection and a regularized one 17 | uFbp = iradon(data, theta, 'Linear', 'Ram-Lak', 1, size(img, 1)); 18 | uFbpReg = iradon(data, theta, 'Linear', 'Hamming', 0.3, size(img, 1)); 19 | 20 | % joint reconstruction and segmentation using the Potts model 21 | gamma = 0.04; 22 | uPotts = minL2iPotts2DADMM(data, gamma, A, 'verbose', 1, 'groundTruth', img, 'isotropic', 1 ); 23 | 24 | % show results 25 | subplot(2,3,1) 26 | imshow(img) 27 | title('Original') 28 | subplot(2,3,2) 29 | imagesc(data) 30 | title('Data (Sinogram)') 31 | subplot(2,3,4) 32 | imshow(uFbp) 33 | title('FBP result') 34 | subplot(2,3,5) 35 | imshow(uFbpReg) 36 | title('FBP with Hamming window') 37 | subplot(2,3,6) 38 | imshow(uPotts) 39 | title('Potts result') 40 | -------------------------------------------------------------------------------- /Demos/2D/results/plFigPottsRec7Angles.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Demos/2D/results/plFigPottsRec7Angles.fig -------------------------------------------------------------------------------- /Demos/pottsLabDemo.m: -------------------------------------------------------------------------------- 1 | % run demo 2 | disp('Demo 1: Denoising of a jump-sparse signal'); 3 | demoPotts_LaplacianNoise; 4 | 5 | disp('Press space bar to continue'); 6 | pause; 7 | disp('Demo 2: Segmentation of an image'); 8 | demoPotts2DColorSegmentation; -------------------------------------------------------------------------------- /Docs/deconv/noisy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Docs/deconv/noisy.png -------------------------------------------------------------------------------- /Docs/deconv/uPottsRho.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Docs/deconv/uPottsRho.png -------------------------------------------------------------------------------- /Docs/potts1d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Docs/potts1d.png -------------------------------------------------------------------------------- /Docs/radon/phantom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Docs/radon/phantom.png -------------------------------------------------------------------------------- /Docs/radon/recFBP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Docs/radon/recFBP.png -------------------------------------------------------------------------------- /Docs/radon/recFBPRamLak.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Docs/radon/recFBPRamLak.png -------------------------------------------------------------------------------- /Docs/radon/recPotts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Docs/radon/recPotts.png -------------------------------------------------------------------------------- /Docs/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Docs/texture.png -------------------------------------------------------------------------------- /Docs/titleImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Docs/titleImage.png -------------------------------------------------------------------------------- /Java/bin/pottslab/IndexedLinkedHistogram$HistNode.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Java/bin/pottslab/IndexedLinkedHistogram$HistNode.class -------------------------------------------------------------------------------- /Java/bin/pottslab/IndexedLinkedHistogram.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Java/bin/pottslab/IndexedLinkedHistogram.class -------------------------------------------------------------------------------- /Java/bin/pottslab/IndexedLinkedHistogramUnweighted$HistNode.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Java/bin/pottslab/IndexedLinkedHistogramUnweighted$HistNode.class -------------------------------------------------------------------------------- /Java/bin/pottslab/IndexedLinkedHistogramUnweighted.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Java/bin/pottslab/IndexedLinkedHistogramUnweighted.class -------------------------------------------------------------------------------- /Java/bin/pottslab/JavaTools.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Java/bin/pottslab/JavaTools.class -------------------------------------------------------------------------------- /Java/bin/pottslab/L2Potts.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Java/bin/pottslab/L2Potts.class -------------------------------------------------------------------------------- /Java/bin/pottslab/PLImage$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Java/bin/pottslab/PLImage$1.class -------------------------------------------------------------------------------- /Java/bin/pottslab/PLImage.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Java/bin/pottslab/PLImage.class -------------------------------------------------------------------------------- /Java/bin/pottslab/PLProcessor.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Java/bin/pottslab/PLProcessor.class -------------------------------------------------------------------------------- /Java/bin/pottslab/PLVector.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Java/bin/pottslab/PLVector.class -------------------------------------------------------------------------------- /Java/src/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: pottslab.RunMe 3 | 4 | -------------------------------------------------------------------------------- /Java/src/pottslab/IndexedLinkedHistogram.java: -------------------------------------------------------------------------------- 1 | package pottslab; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * @author martinstorath 7 | * 8 | */ 9 | public class IndexedLinkedHistogram { 10 | 11 | ArrayList originalOrder; // stores the nodes in order of the incoming insertions 12 | double[] weights; // stores all weights 13 | 14 | HistNode first, last, median; // list pointers 15 | HistNode firstTemp, lastTemp; // temporary list pointers 16 | 17 | double totalDeviation; // stores the deviation from the median 18 | double weightAboveMedian, weightBelowMedian; 19 | double totalWeight; 20 | 21 | public double[] currentMedians; 22 | /** 23 | * Constructor 24 | * @param initialSize 25 | * the expected list size 26 | */ 27 | public IndexedLinkedHistogram(double[] weights) { 28 | // initialize the array which indexes the nodes in their original order 29 | originalOrder = new ArrayList(weights.length); 30 | // init weights 31 | this.weights = weights; 32 | } 33 | 34 | /** 35 | * Node inner class 36 | */ 37 | class HistNode { 38 | // the value 39 | double value; 40 | double weight, weightTemp; 41 | int count, countTemp; 42 | // pointer to the next and previous elements 43 | HistNode next, prev; 44 | // temporary pointers to the next and previous elements 45 | HistNode nextTemp, prevTemp; 46 | 47 | /** 48 | * Node constructor 49 | * @param element 50 | */ 51 | HistNode(double element, double weight) { 52 | this.value = element; 53 | this.weight = weight; 54 | this.count = 1; 55 | } 56 | 57 | void addWeight(double weight) { 58 | this.weight += weight; 59 | count++; 60 | } 61 | 62 | void removeWeightTemp(double weight) { 63 | this.weightTemp -= weight; 64 | countTemp--; 65 | if (countTemp < 0) { 66 | throw new RuntimeException("Error in List."); 67 | } 68 | if (weightTemp < 0) { 69 | weightTemp = 0; 70 | } 71 | } 72 | 73 | /** 74 | * Reset all temporary values to the non-temporary ones 75 | */ 76 | void resetTemp() { 77 | nextTemp = next; 78 | prevTemp = prev; 79 | weightTemp = weight; 80 | countTemp = count; 81 | } 82 | } 83 | 84 | /** 85 | * inserts an element sorted by value in ascending order from first to last 86 | */ 87 | public void insertSorted(double elem) { 88 | double weight = weights[this.size()]; 89 | HistNode pivot = null; 90 | HistNode iterator = null; 91 | if (first == null) { // empty list 92 | pivot = new HistNode(elem, weight); 93 | first = pivot; 94 | last = first; 95 | median = first; 96 | totalDeviation = 0; 97 | weightAboveMedian = 0; 98 | weightBelowMedian = 0; 99 | totalWeight = weight; 100 | } else { // non-empty list 101 | iterator = first; 102 | while (iterator != null) { 103 | if (iterator.value >= elem) { 104 | break; 105 | } 106 | iterator.resetTemp(); 107 | iterator = iterator.next; 108 | } 109 | // insert node at right place or add weight to existing node 110 | if (iterator != null) { 111 | if (iterator.value == elem) { 112 | pivot = iterator; 113 | pivot.addWeight(weight); 114 | } else { 115 | pivot = new HistNode(elem, weight); 116 | insertBefore(iterator, pivot); 117 | } 118 | } else { 119 | pivot = new HistNode(elem, weight); 120 | insertAfter(last, pivot); 121 | } 122 | // continue loop to reset temporary pointers 123 | while (iterator != null) { 124 | iterator.resetTemp(); 125 | iterator = iterator.next; 126 | } 127 | // add weight to total weight 128 | totalWeight += weight; 129 | } 130 | // add the pivot node to the original order (allows removeTemp in O(1)!) 131 | originalOrder.add(pivot); 132 | // reset temporary first and last 133 | 134 | 135 | /* OBSOLETE: leads to loss of significance 136 | // update weights 137 | if (elem > median.value) { 138 | weightAboveMedian += weight; 139 | } else if (elem < median.value) { 140 | weightBelowMedian += weight; 141 | } 142 | shifting median and updating deviations 143 | totalDeviation += Math.abs(median.value - elem) * weight; // new element increases deviation 144 | double oldMedian = median.value; // store old median 145 | double medDiff; // difference between old and new median 146 | // if weight above the median is to large, execute the following loop 147 | while ((weightAboveMedian > totalWeight/2) && (median.next != null)) { 148 | // the null check is important if loss of significance occurs 149 | weightBelowMedian += median.weight; // update weight above 150 | median = median.next; // shift median 151 | // update median deviation 152 | medDiff = Math.abs(oldMedian - median.value); // difference between old and new median 153 | totalDeviation -= medDiff * Math.abs(weightBelowMedian - weightAboveMedian); // update deviation 154 | weightAboveMedian -= median.weight; // update weight below 155 | } 156 | // if the weight below the median is to large, execute the following loop 157 | while ((weightBelowMedian > totalWeight/2) && (median.prev != null)) { 158 | // the null check is important if loss of significance occurs 159 | weightAboveMedian += median.weight; 160 | // shift median to the left 161 | median = median.prev; 162 | // update median deviation 163 | medDiff = Math.abs(oldMedian - median.value); 164 | totalDeviation -= medDiff * Math.abs(weightAboveMedian - weightBelowMedian); 165 | weightBelowMedian -= median.weight; 166 | } 167 | */ 168 | 169 | // determine median 170 | iterator = first; 171 | double wbm = 0; 172 | double twh = totalWeight/2.0; 173 | while (iterator != null) { 174 | wbm += iterator.weight; 175 | if (wbm > twh) { 176 | median = iterator.prev; 177 | break; 178 | } 179 | iterator = iterator.next; 180 | } 181 | if (median == null) { 182 | median = first; 183 | } 184 | // determine weight below and above 185 | weightAboveMedian = 0; 186 | weightBelowMedian = 0; 187 | totalDeviation = 0; 188 | iterator = first; 189 | while (iterator != null) { 190 | if (iterator.value < median.value) { 191 | weightBelowMedian += iterator.weight; 192 | } else if (iterator.value > median.value) { 193 | weightAboveMedian += iterator.weight; 194 | } 195 | totalDeviation += Math.abs(median.value - iterator.value) * iterator.weight; 196 | iterator = iterator.next; 197 | } 198 | } 199 | 200 | /** 201 | * 202 | * @return 203 | * the number of elements currently in the list, equals the (outer) r-loop index 204 | */ 205 | public int size() { 206 | return originalOrder.size(); 207 | } 208 | 209 | /** 210 | * inserts the newNode after the pivot 211 | * @param pivot 212 | * @param newNode 213 | */ 214 | private void insertAfter(HistNode pivot, HistNode newNode) { 215 | newNode.next = pivot.next; 216 | pivot.next = newNode; 217 | newNode.prev = pivot; 218 | if (pivot == last) { 219 | last = newNode; 220 | } else { 221 | newNode.next.prev = newNode; 222 | newNode.next.resetTemp(); 223 | } 224 | // reset temporary pointers 225 | newNode.prev.resetTemp(); 226 | newNode.resetTemp(); 227 | } 228 | 229 | /** 230 | * inserts the newNode before the pivot 231 | * @param pivot 232 | * @param newNode 233 | */ 234 | private void insertBefore(HistNode pivot, HistNode newNode) { 235 | newNode.prev = pivot.prev; 236 | pivot.prev = newNode; 237 | newNode.next = pivot; 238 | if (pivot == first) { 239 | first = newNode; 240 | } else { 241 | newNode.prev.next = newNode; 242 | newNode.prev.resetTemp(); 243 | } 244 | // reset temporary pointers 245 | newNode.resetTemp(); 246 | newNode.next.resetTemp(); 247 | } 248 | 249 | /** 250 | * Computes the deviations from the median of every connected interval in the original data vector (stored in indices) 251 | * @return 252 | */ 253 | public double[] computeDeviations() { 254 | // init the output array, i.e., deviation between d_[l,r] 255 | double[] deviationsArray = new double[this.size()]; 256 | // init the corresponding median array, i.e., deviation between d_[l,r] 257 | currentMedians = new double[this.size()]; 258 | // init all temporary values 259 | firstTemp = first; 260 | lastTemp = last; 261 | HistNode medianTemp = median; 262 | double deviationTemp = totalDeviation; 263 | double weightAboveMedianTemp = weightAboveMedian; 264 | double weightBelowMedianTemp = weightBelowMedian; 265 | double totalWeightTemp = totalWeight; 266 | double medDiff, oldMedian; 267 | for (int l = 1; l < this.size(); l++) { 268 | // set the deviation array entry to the temporary distance 269 | deviationsArray[l-1] = deviationTemp; 270 | // set the median array entry to the temporary median 271 | currentMedians[l-1] = medianTemp.value; 272 | // pointer to the node to be removed 273 | HistNode nodeToRemove = originalOrder.get(l-1); 274 | // removes the node temporarily 275 | double weightToRemove = weights[l-1]; 276 | // remove weight from node 277 | nodeToRemove.removeWeightTemp(weightToRemove); 278 | // update deviation 279 | deviationTemp -= weightToRemove * Math.abs(nodeToRemove.value - medianTemp.value); 280 | // update total weight 281 | totalWeightTemp -= weightToRemove; 282 | // update weights above, below 283 | if (nodeToRemove.value > medianTemp.value) { 284 | weightAboveMedianTemp -= weightToRemove; 285 | } else if (nodeToRemove.value < medianTemp.value) { 286 | weightBelowMedianTemp -= weightToRemove; 287 | } 288 | 289 | 290 | // if weights are unbalanced, the median pointer has to be shifted and the deviations require updates 291 | // if weight above is too large, shift right 292 | double twth = totalWeightTemp/2.0; 293 | while ((weightAboveMedianTemp > twth) && (medianTemp.nextTemp != null)) { 294 | oldMedian = medianTemp.value; 295 | weightBelowMedianTemp += medianTemp.weightTemp; // update weight below 296 | medianTemp = medianTemp.nextTemp; 297 | // calculate difference between old and new median 298 | medDiff = Math.abs(oldMedian - medianTemp.value); 299 | // decrease deviation according median and weight differences 300 | deviationTemp -= medDiff * Math.abs(weightBelowMedianTemp - weightAboveMedianTemp); 301 | weightAboveMedianTemp -= medianTemp.weightTemp; // update weight above 302 | } 303 | // if weight below is too large, shift right 304 | while ((weightBelowMedianTemp > twth) && (medianTemp.prevTemp != null)) { 305 | oldMedian = medianTemp.value; // store old median 306 | weightAboveMedianTemp += medianTemp.weightTemp; // update weight above 307 | medianTemp = medianTemp.prevTemp; // shift median to left 308 | // calculate difference between old and new median 309 | medDiff = Math.abs(oldMedian - medianTemp.value); 310 | // decrease deviation according median and weight differences 311 | deviationTemp -= medDiff * Math.abs(weightAboveMedianTemp - weightBelowMedianTemp); 312 | weightBelowMedianTemp -= medianTemp.weightTemp; // update weight below 313 | } 314 | 315 | // remove node temporary, if it contains no more weights 316 | if (nodeToRemove.countTemp == 0) { 317 | removeTemp(nodeToRemove); 318 | } 319 | } 320 | return deviationsArray; 321 | } 322 | 323 | /** 324 | * remove temporarily a HistNode from the histogram 325 | * 326 | * @param index 327 | */ 328 | private void removeTemp(HistNode nodeToRemove) { 329 | // remove node temporarily 330 | if (nodeToRemove == lastTemp) { 331 | lastTemp = lastTemp.prevTemp; 332 | lastTemp.nextTemp = null; 333 | } else if (nodeToRemove == firstTemp) { 334 | firstTemp = firstTemp.nextTemp; 335 | firstTemp.prevTemp = null; 336 | } else { 337 | nodeToRemove.nextTemp.prevTemp = nodeToRemove.prevTemp; 338 | nodeToRemove.prevTemp.nextTemp = nodeToRemove.nextTemp; 339 | } 340 | 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /Java/src/pottslab/IndexedLinkedHistogramUnweighted.java: -------------------------------------------------------------------------------- 1 | package pottslab; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * @author martinstorath 7 | * 8 | */ 9 | public class IndexedLinkedHistogramUnweighted { 10 | 11 | private ArrayList originalOrder; // stores the nodes in order of the incoming insertions 12 | 13 | private HistNode first, last, median; // list pointers 14 | private HistNode firstTemp, lastTemp; // temporary list pointers 15 | 16 | private double currentDeviation; // stores the deviation from the median 17 | 18 | /** 19 | * Constructor 20 | * @param initialSize 21 | * the expected list size 22 | */ 23 | public IndexedLinkedHistogramUnweighted(int initialSize) { 24 | // initialize the array which indexes the nodes in their original order 25 | originalOrder = new ArrayList(initialSize); 26 | } 27 | 28 | /** 29 | * Node inner class 30 | */ 31 | class HistNode { 32 | // the value 33 | double value; 34 | // pointer to the next and previous elements 35 | HistNode next, prev; 36 | // temporary pointers to the next and previous elements 37 | HistNode nextTemp, prevTemp; 38 | 39 | /** 40 | * Node constructor 41 | * @param element 42 | */ 43 | HistNode(double element) { 44 | this.value = element; 45 | } 46 | 47 | void resetTemp() { 48 | nextTemp = next; 49 | prevTemp = prev; 50 | } 51 | } 52 | 53 | /** 54 | * inserts an element sorted in ascending order from first to last 55 | */ 56 | public void insertSorted(double elem) { 57 | HistNode node = new HistNode(elem); 58 | if (first == null) { // empty list 59 | first = node; 60 | last = node; 61 | median = node; 62 | currentDeviation = 0; 63 | } else { // non empty list 64 | HistNode iterator = first; 65 | // isEven is true, if the size of the list is even 66 | boolean isEven = (this.size() % 2 == 0); 67 | // for speed-up, we insert and reset the temporary pointers in one sweep 68 | HistNode pivot = null; 69 | while (iterator != null) { 70 | // find pivot element 71 | if ((pivot == null) && (iterator.value > node.value)) { 72 | pivot = iterator; 73 | } 74 | // set temporary pointers 75 | iterator.prevTemp = iterator.prev; 76 | iterator.nextTemp = iterator.next; 77 | iterator = iterator.next; 78 | } 79 | // insert node at right place 80 | if (pivot != null) { 81 | insertBefore(pivot, node); 82 | } else { // iterator is null if elem is greater than all elements in the list 83 | insertAfter(last, node); 84 | } 85 | // update deviation from (old) median 86 | currentDeviation = currentDeviation + Math.abs(node.value - median.value); 87 | HistNode medianOld = median; // store old median 88 | // update median and distances 89 | boolean insertAboveMedian = median.value <= node.value; 90 | if (!insertAboveMedian && isEven) { 91 | median = median.prev; 92 | currentDeviation = currentDeviation - medianOld.value + median.value; 93 | } else if (insertAboveMedian && !isEven) { 94 | median = median.next; 95 | } 96 | } 97 | // add the node to the original order (allows removeTemp in O(1)!) 98 | originalOrder.add(node); 99 | // set temporary first and last 100 | firstTemp = first; 101 | lastTemp = last; 102 | } 103 | 104 | /** 105 | * 106 | * @return 107 | * the number of elements currently in the list 108 | */ 109 | public int size() { 110 | return originalOrder.size(); 111 | } 112 | 113 | /** 114 | * inserts the newNode after the pivot 115 | * @param pivot 116 | * @param newNode 117 | */ 118 | private void insertAfter(HistNode pivot, HistNode newNode) { 119 | newNode.next = pivot.next; 120 | pivot.next = newNode; 121 | newNode.prev = pivot; 122 | if (pivot == last) { 123 | last = newNode; 124 | } else { 125 | newNode.next.prev = newNode; 126 | newNode.next.resetTemp(); 127 | } 128 | // reset temporary pointers 129 | newNode.prev.resetTemp(); 130 | newNode.resetTemp(); 131 | } 132 | 133 | /** 134 | * inserts the newNode before the pivot 135 | * @param pivot 136 | * @param newNode 137 | */ 138 | private void insertBefore(HistNode pivot, HistNode newNode) { 139 | newNode.prev = pivot.prev; 140 | pivot.prev = newNode; 141 | newNode.next = pivot; 142 | if (pivot == first) { 143 | first = newNode; 144 | } else { 145 | newNode.prev.next = newNode; 146 | newNode.prev.resetTemp(); 147 | } 148 | // reset temporary pointers 149 | newNode.resetTemp(); 150 | newNode.next.resetTemp(); 151 | } 152 | 153 | /** 154 | * Computes the deviations from the median of every connected interval in the original data vector (stored in indices) 155 | * @return 156 | */ 157 | public double[] computeDeviations() { 158 | // Initialization 159 | double[] deviationsArray = new double[this.size()]; 160 | HistNode medianTemp = median; 161 | double deviationTemp = currentDeviation; 162 | // reset all temporary values and pointers to the non-temporary ones 163 | //resetTemp(); 164 | for (int l = 1; l < this.size(); l++) { 165 | // set the deviation to the temporary distance 166 | deviationsArray[l-1] = deviationTemp; 167 | // pointer to the node to be removed 168 | HistNode nodeToRemove = originalOrder.get(l-1); 169 | // removes the node temporarily 170 | removeTemp(nodeToRemove); 171 | // update median and distances 172 | deviationTemp = deviationTemp - Math.abs(medianTemp.value - nodeToRemove.value); 173 | /* the correct update of medTemp and deviationTemp requires to differentiate 174 | between even and odd temporary length and if removedNode was 175 | above or below medianTemp */ 176 | double medianValueOld = medianTemp.value; 177 | boolean isEven = ((this.size() - l + 1) % 2 == 0); 178 | if (isEven) { 179 | if ((medianValueOld < nodeToRemove.value) || (medianTemp == nodeToRemove)) { 180 | medianTemp = medianTemp.prevTemp; 181 | deviationTemp = deviationTemp - medianValueOld + medianTemp.value; 182 | } 183 | } else if ((nodeToRemove.value <= medianValueOld) ) { 184 | medianTemp = medianTemp.nextTemp; 185 | } 186 | } 187 | return deviationsArray; 188 | } 189 | 190 | /** 191 | * remove temporarily 192 | * 193 | * @param index 194 | */ 195 | private void removeTemp(HistNode nodeToRemove) { 196 | // remove node temporarily 197 | if (nodeToRemove == lastTemp) { 198 | lastTemp = lastTemp.prevTemp; 199 | lastTemp.nextTemp = null; 200 | } else if (nodeToRemove == firstTemp) { 201 | firstTemp = firstTemp.nextTemp; 202 | firstTemp.prevTemp = null; 203 | } else { 204 | nodeToRemove.nextTemp.prevTemp = nodeToRemove.prevTemp; 205 | nodeToRemove.prevTemp.nextTemp = nodeToRemove.nextTemp; 206 | } 207 | } 208 | 209 | public String printList(boolean temp) { 210 | String str = ""; 211 | HistNode iterator = first; 212 | while (iterator != null) { 213 | // find pivot element 214 | str = str + ", " + iterator.value; 215 | if (temp) { 216 | iterator = iterator.nextTemp; 217 | } else { 218 | iterator = iterator.next; 219 | } 220 | } 221 | return str; 222 | } 223 | 224 | public static void main(String[] args) { 225 | IndexedLinkedHistogramUnweighted list = new IndexedLinkedHistogramUnweighted(10); 226 | list.insertSorted(1); 227 | list.insertSorted(0); 228 | list.insertSorted(0); 229 | list.insertSorted(0); 230 | list.computeDeviations(); 231 | System.out.println(list.printList(true)); 232 | System.out.println(list.printList(false)); 233 | 234 | list.insertSorted(2); 235 | 236 | System.out.println(list.printList(true)); 237 | System.out.println(list.printList(false)); 238 | } 239 | 240 | } 241 | -------------------------------------------------------------------------------- /Java/src/pottslab/JavaTools.java: -------------------------------------------------------------------------------- 1 | package pottslab; 2 | 3 | /** 4 | * @author Martin Storath 5 | * 6 | */ 7 | 8 | public class JavaTools { 9 | 10 | public static int OR_HORIZONTAL = 1, 11 | OR_VERTICAL = 2, 12 | OR_DIAGONAL = 3, 13 | OR_ANTIDIAGONAL = 4; 14 | 15 | 16 | 17 | /** 18 | * minimization of univariate scalar valued Potts functional 19 | * @param f 20 | * @param gamma 21 | * @param weights 22 | * @return 23 | */ 24 | public static double[] minL2Potts(double[] f, double gamma, double[] weights) { 25 | PLVector[] plVec = PLVector.array1DToVector1D(f); 26 | L2Potts pottsCore = new L2Potts(plVec, weights, gamma); 27 | pottsCore.call(); 28 | return PLVector.vector1DToArray1D(plVec); 29 | } 30 | 31 | /** 32 | * minimization of univariate vector-valued Potts functional 33 | * @param f 34 | * @param gamma 35 | * @param weights 36 | * @return 37 | */ 38 | public static double[][] minL2Potts(double[][] f, double gamma, double[] weights) { 39 | PLVector[] plVec = PLVector.array2DToVector1D(f); 40 | L2Potts pottsCore = new L2Potts(plVec, weights, gamma); 41 | pottsCore.call(); 42 | return PLVector.vector1DToArray2D(plVec); 43 | } 44 | 45 | /** 46 | * Minimization of univariate L2-Potts along indicated orientation 47 | * @param img 48 | * @param gamma 49 | * @param weights 50 | * @param orientation 51 | * @return 52 | */ 53 | public static PLImage minL2PottsOrientation(PLImage img, double gamma, double[][] weights, String orientation) { 54 | PLProcessor proc = new PLProcessor(); 55 | proc.setMultiThreaded(true); 56 | proc.setGamma(gamma); 57 | proc.set(img, weights); 58 | 59 | switch (orientation) { 60 | case "horizontal": 61 | proc.applyHorizontally(); 62 | break; 63 | case "vertical": 64 | proc.applyVertically(); 65 | break; 66 | case "diagonal": 67 | proc.applyDiag(); 68 | break; 69 | case "antidiagonal": 70 | proc.applyAntiDiag(); 71 | break; 72 | 73 | } 74 | return img; 75 | } 76 | 77 | /** 78 | * ADMM strategy to the scalar-valued 2D Potts problem anisotropic neighborhood (4-connected) 79 | * @param f 80 | * @param gamma 81 | * @param weights 82 | * @param muInit 83 | * @param muStep 84 | * @param stopTol 85 | * @param verbose 86 | * @return 87 | */ 88 | public static PLImage minL2PottsADMM4(PLImage img, double gamma, double[][] weights, double muInit, double muStep, double stopTol, boolean verbose, boolean multiThreaded, boolean useADMM) { 89 | // init 90 | int m = img.mRow; 91 | int n = img.mCol; 92 | int l = img.mLen; 93 | PLImage u = PLImage.zeros(m,n,l); 94 | PLImage v = img.copy(); 95 | PLImage lam = PLImage.zeros(m,n,l); 96 | PLImage temp = PLImage.zeros(m,n,l); 97 | double[][] weightsPrime = new double[m][n]; 98 | double error = Double.POSITIVE_INFINITY; 99 | double mu = muInit; 100 | double gammaPrime; 101 | int nIter = 0; 102 | PLProcessor proc = new PLProcessor(); 103 | proc.setMultiThreaded(multiThreaded); 104 | double fNorm = img.normQuad(); 105 | 106 | // a shortcut 107 | if (fNorm == 0) { 108 | return img; 109 | } 110 | 111 | // main loop 112 | while (error >= stopTol * fNorm) { 113 | // set Potts parameters 114 | gammaPrime = 2 * gamma; 115 | 116 | // set weights 117 | for (int i = 0; i < m; i++) for (int j = 0; j < n; j++){ 118 | weightsPrime[i][j] = weights[i][j] + mu; 119 | } 120 | 121 | // solve horizontal univariate Potts problems 122 | for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) for (int k = 0; k < l; k++) { 123 | u.get(i, j).set(k, (img.get(i, j).get(k) * weights[i][j] + v.get(i, j).get(k) * mu - lam.get(i, j).get(k)) / weightsPrime[i][j]); 124 | } 125 | proc.set(u, weightsPrime); 126 | proc.setGamma(gammaPrime); 127 | proc.applyHorizontally(); 128 | 129 | // solve vertical univariate Potts problems 130 | for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) for (int k = 0; k < l; k++) { 131 | v.get(i, j).set(k, (img.get(i, j).get(k) * weights[i][j] + u.get(i, j).get(k) * mu + lam.get(i, j).get(k)) / weightsPrime[i][j]); 132 | } 133 | proc.set(v, weightsPrime); 134 | proc.setGamma(gammaPrime); 135 | proc.applyVertically(); 136 | 137 | // update Lagrange multiplier and calculate difference between u and v 138 | error = 0; 139 | for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) for (int k = 0; k < l; k++){ 140 | temp.get(i, j).set(k, u.get(i, j).get(k) - v.get(i, j).get(k)); 141 | if (useADMM) { 142 | lam.get(i, j).set(k, lam.get(i, j).get(k) + temp.get(i, j).get(k) * mu); // update Lagrange multiplier 143 | } 144 | error += Math.pow(temp.get(i, j).get(k), 2); // compute error 145 | } 146 | // update coupling 147 | mu *= muStep; 148 | 149 | // count iterations 150 | nIter++; 151 | 152 | // show some information 153 | if (verbose) { 154 | System.out.print("*"); 155 | if (nIter % 50 == 0) { 156 | System.out.print("\n"); 157 | } 158 | } 159 | } 160 | 161 | // show some information 162 | if (verbose) { 163 | System.out.println("\n Total number of iterations " + nIter + "\n"); 164 | } 165 | 166 | // shut down the processor 167 | proc.shutdown(); 168 | 169 | return u; 170 | } 171 | 172 | 173 | 174 | /** 175 | * ADMM strategy to the scalar-valued 2D Potts problem with near-isotropic neighborhood (8-connected) 176 | * @param img 177 | * @param gamma 178 | * @param weights 179 | * @param muInit 180 | * @param muStep 181 | * @param stopTol 182 | * @param verbose 183 | * @return 184 | */ 185 | public static PLImage minL2PottsADMM8(PLImage img, double gamma, double[][] weights, double muInit, double muStep, double stopTol, boolean verbose, boolean multiThreaded, boolean useADMM, double[] omega) { 186 | // init image dimensions 187 | int m = img.mRow; 188 | int n = img.mCol; 189 | int l = img.mLen; 190 | // init ADMM variables 191 | PLImage u = PLImage.zeros(m,n,l); 192 | PLImage v = img.copy(); 193 | PLImage w = img.copy(); 194 | PLImage z = img.copy(); 195 | PLImage lam1 = PLImage.zeros(m,n,l); 196 | PLImage lam2 = PLImage.zeros(m,n,l); 197 | PLImage lam3 = PLImage.zeros(m,n,l); 198 | PLImage lam4 = PLImage.zeros(m,n,l); 199 | PLImage lam5 = PLImage.zeros(m,n,l); 200 | PLImage lam6 = PLImage.zeros(m,n,l); 201 | // init modified weight vector 202 | double[][] weightsPrime = new double[m][n]; 203 | // init coupling 204 | double mu = muInit; 205 | // auxiliary variables 206 | double gammaPrimeC, gammaPrimeD; 207 | // set neighborhood weights 208 | double omegaC = omega[0]; 209 | double omegaD = omega[1]; 210 | // init up the Potts processer 211 | PLProcessor proc = new PLProcessor(); 212 | proc.setMultiThreaded(multiThreaded); 213 | // compute the norm of the input image 214 | double fNorm = img.normQuad(); 215 | if (fNorm == 0) { 216 | return img; 217 | } 218 | double error = Double.POSITIVE_INFINITY; 219 | // init number of iterations 220 | int nIter = 0; 221 | 222 | // main ADMM iteration 223 | while (error >= stopTol * fNorm) { 224 | // set jump penalty 225 | gammaPrimeC = 4.0 * omegaC * gamma; 226 | gammaPrimeD = 4.0 * omegaD * gamma; 227 | 228 | // set weights 229 | for (int i = 0; i < m; i++) for (int j = 0; j < n; j++){ 230 | weightsPrime[i][j] = weights[i][j] + 6 * mu; 231 | } 232 | 233 | // solve univariate Potts problems horizontally 234 | for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) for (int k = 0; k < l; k++){ 235 | u.get(i, j).set(k, ( img.get(i, j).get(k) * weights[i][j] + 2 * mu * (w.get(i, j).get(k) + v.get(i, j).get(k) + z.get(i, j).get(k)) 236 | + 2 * (-lam1.get(i, j).get(k) - lam2.get(i, j).get(k) - lam3.get(i, j).get(k)) ) / weightsPrime[i][j] ); 237 | } 238 | proc.set(u, weightsPrime); 239 | proc.setGamma(gammaPrimeC); 240 | proc.applyHorizontally(); 241 | 242 | // solve 1D Potts problems diagonally 243 | for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) for (int k = 0; k < l; k++){ 244 | w.get(i,j).set(k, (img.get(i,j).get(k) * weights[i][j] + 2 * mu * (u.get(i,j).get(k) + v.get(i,j).get(k) + z.get(i,j).get(k)) 245 | + 2 * (lam2.get(i,j).get(k) + lam4.get(i,j).get(k) - lam6.get(i,j).get(k))) / weightsPrime[i][j] ); 246 | } 247 | proc.set(w, weightsPrime); 248 | proc.setGamma(gammaPrimeD); 249 | proc.applyDiag(); 250 | 251 | // solve 1D Potts problems vertically 252 | for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) for (int k = 0; k < l; k++){ 253 | v.get(i,j).set(k, (img.get(i,j).get(k) * weights[i][j] + 2 * mu * (u.get(i,j).get(k) + w.get(i,j).get(k) + z.get(i,j).get(k)) 254 | + 2 * (lam1.get(i,j).get(k) - lam4.get(i,j).get(k) - lam5.get(i,j).get(k))) / weightsPrime[i][j]); 255 | } 256 | proc.set(v, weightsPrime); 257 | proc.setGamma(gammaPrimeC); 258 | proc.applyVertically(); 259 | 260 | // solve 1D Potts problems antidiagonally 261 | for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) for (int k = 0; k < l; k++){ 262 | z.get(i,j).set(k, (img.get(i,j).get(k) * weights[i][j] + 2 *mu * (u.get(i,j).get(k) + w.get(i,j).get(k) + v.get(i,j).get(k)) 263 | + 2 * (lam3.get(i,j).get(k) + lam5.get(i,j).get(k) + lam6.get(i,j).get(k))) / weightsPrime[i][j]); 264 | } 265 | proc.set(z, weightsPrime); 266 | proc.setGamma(gammaPrimeD); 267 | proc.applyAntiDiag(); 268 | 269 | // update Lagrange multiplier and calculate difference between u and v 270 | error = 0; 271 | for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) for (int k = 0; k < l; k++){ 272 | if (useADMM) { 273 | //lam1.get(i,j).set(k, lam1.get(i,j).get(k) + mu * (u.get(i,j).get(k) - u.get(i,j).get(k)) ); 274 | //lam2.get(i,j).set(k, lam2.get(i,j).get(k) + mu * (u.get(i,j).get(k) - v.get(i,j).get(k)) ); 275 | lam1.get(i,j).set(k, lam1.get(i,j).get(k) + mu * (u.get(i,j).get(k) - v.get(i,j).get(k)) ); 276 | lam2.get(i,j).set(k, lam2.get(i,j).get(k) + mu * (u.get(i,j).get(k) - w.get(i,j).get(k)) ); 277 | lam3.get(i,j).set(k, lam3.get(i,j).get(k) + mu * (u.get(i,j).get(k) - z.get(i,j).get(k)) ); 278 | lam4.get(i,j).set(k, lam4.get(i,j).get(k) + mu * (v.get(i,j).get(k) - w.get(i,j).get(k)) ); 279 | lam5.get(i,j).set(k, lam5.get(i,j).get(k) + mu * (v.get(i,j).get(k) - z.get(i,j).get(k)) ); 280 | lam6.get(i,j).set(k, lam6.get(i,j).get(k) + mu * (w.get(i,j).get(k) - z.get(i,j).get(k)) ); 281 | } 282 | error += Math.pow(u.get(i,j).get(k) - v.get(i,j).get(k), 2); 283 | } 284 | 285 | // increase coupling parameter 286 | mu *= muStep; 287 | 288 | // increase iteration counter 289 | nIter++; 290 | 291 | // show some information 292 | if (verbose) { 293 | System.out.print("*"); 294 | if (nIter % 50 == 0) { 295 | System.out.print("\n"); 296 | } 297 | } 298 | } 299 | // show some information 300 | if (verbose) { 301 | System.out.println("\n Total number of iterations " + nIter + "\n"); 302 | } 303 | 304 | // shut down the processor 305 | proc.shutdown(); 306 | 307 | return u; 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /Java/src/pottslab/L2Potts.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package pottslab; 5 | 6 | import java.util.concurrent.Callable; 7 | 8 | /** 9 | * @author Martin Storath 10 | * 11 | */ 12 | @SuppressWarnings("rawtypes") 13 | public class L2Potts implements Callable { 14 | 15 | PLVector[] mData; 16 | double mGamma; 17 | double[] mWeights; 18 | int mExcludedIntervalSize; 19 | 20 | public L2Potts (PLVector[] data, double[] weights, double gamma) { 21 | set(data, weights, gamma); 22 | } 23 | 24 | /** 25 | * Set up the data and parameters 26 | * @param data 27 | * @param weights 28 | * @param gamma 29 | */ 30 | public void set(PLVector[] data, double[] weights, double gamma) { 31 | mData = data; 32 | mWeights = weights; 33 | mGamma = gamma; 34 | mExcludedIntervalSize = 0; 35 | } 36 | 37 | /** 38 | * Set up the data and parameters 39 | * @param data 40 | * @param weights 41 | * @param gamma 42 | */ 43 | public void setExcludedIntervalSize(int excludedIntervalSize) { 44 | mExcludedIntervalSize = excludedIntervalSize; 45 | } 46 | 47 | 48 | public double getWeight(int i) { 49 | return (mWeights == null) ? 1 : mWeights[i]; 50 | } 51 | 52 | /** 53 | * Solve one-dimensional Potts problem in situ on mData 54 | */ 55 | public Object call() { 56 | 57 | int n = mData.length; 58 | int[] arrJ = new int[n]; 59 | int nVec = mData[0].length(); 60 | 61 | double[] arrP = new double[n]; // array of Potts values 62 | double d = 0, p = 0, dpg = 0; // temporary variables to save some float operation 63 | PLVector[] m = new PLVector[n + 1]; // cumulative first moments 64 | double[] s = new double[n + 1]; // cumulative second moments (summed up in 3 dimension) 65 | double[] w = new double[n + 1]; // cumulative weights 66 | 67 | // precompute cumulative moments 68 | m[0] = PLVector.zeros(nVec); 69 | s[0] = 0; 70 | double wTemp, mTemp, wDiffTemp; 71 | for (int j = 0; j < n; j++) { 72 | wTemp = getWeight(j); 73 | m[j + 1] = mData[j].mult(wTemp); 74 | m[j + 1].plusAssign(m[j]); 75 | s[j + 1] = mData[j].normQuad() * wTemp + s[j]; 76 | w[j + 1] = w[j] + wTemp; 77 | } 78 | 79 | // main loop 80 | for (int r = 1; r <= n; r++) { 81 | arrP[r-1] = s[r] - m[r].normQuad() / (w[r]); // set Potts value of constant solution 82 | arrJ[r-1] = 0; // set jump location of constant solution 83 | for (int l = r - mExcludedIntervalSize; l >= 2 ; l--) { 84 | // compute squared deviation from mean value d 85 | mTemp = 0; 86 | for (int k = 0; k < nVec; k++) { 87 | mTemp = mTemp + Math.pow(m[r].get(k) - (m[l - 1].get(k)), 2); 88 | } 89 | wDiffTemp = (w[r] - w[l - 1]); 90 | if (wDiffTemp == 0) { 91 | d = 0; 92 | } else { 93 | d = s[r] - s[l - 1] - mTemp / wDiffTemp; 94 | } 95 | dpg = d + mGamma; // temporary variable 96 | if (dpg > arrP[r-1]) { 97 | // Acceleration: if deviation plus jump penalty is larger than best 98 | // Potts functional value, we can break the loop 99 | break; 100 | } 101 | p = arrP[l - 2] + dpg; // candidate Potts value 102 | if (p < arrP[r-1]) { 103 | arrP[r-1] = p; // set optimal Potts value 104 | arrJ[r-1] = l - 1; // set jump location 105 | } 106 | } 107 | } 108 | // Reconstruction from best partition 109 | int r = n; 110 | int l = arrJ[r-1]; 111 | PLVector mu = PLVector.zeros(nVec); 112 | while (r > 0) { 113 | // compute mean value on interval [l+1, r] 114 | for (int k = 0; k < nVec; k++) { 115 | mu.set(k, (m[r].get(k) - m[l].get(k)) / (w[r] - w[l])); 116 | } 117 | // set mean value on interval [l+1, r] 118 | for (int j = l; j < r; j++) { 119 | for (int k = 0; k < mu.length(); k++) { 120 | mData[j].set(k, mu.get(k)); 121 | } 122 | } 123 | r = l; 124 | if (r < 1) break; 125 | // go to next jump 126 | l = arrJ[r-1]; 127 | } 128 | return mData; 129 | } 130 | 131 | public void clear() { 132 | mData = null; 133 | mWeights = null; 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /Java/src/pottslab/PLImage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package pottslab; 5 | 6 | import java.awt.Color; 7 | import java.awt.Dimension; 8 | import java.awt.Graphics; 9 | import java.awt.Graphics2D; 10 | import java.awt.RenderingHints; 11 | import java.awt.image.BufferedImage; 12 | 13 | import javax.swing.JFrame; 14 | import javax.swing.JPanel; 15 | 16 | 17 | /** 18 | * A structure for a vector valued image 19 | * 20 | * @author Martin Storath 21 | * 22 | */ 23 | public class PLImage { 24 | 25 | public PLVector[][] mData; 26 | public int mRow, mCol, mLen; 27 | 28 | public PLImage(PLVector[][] vecArr) { 29 | mRow = vecArr.length; 30 | mCol = vecArr[0].length; 31 | mLen = vecArr[0][0].length(); 32 | mData = vecArr; 33 | } 34 | 35 | 36 | public PLImage(double[][] img) { 37 | set(img); 38 | } 39 | 40 | public PLImage(double[][][] img) { 41 | set(img); 42 | } 43 | 44 | public void set(double[][] img) { 45 | mRow = img.length; 46 | mCol = img[0].length; 47 | mLen = 1; 48 | mData = new PLVector[mRow][mCol]; 49 | for (int i = 0; i < mRow; i++) for (int j = 0; j < mCol; j++) { 50 | this.set(i, j, new PLVector(img[i][j])); 51 | } 52 | } 53 | 54 | public void set(double[][][] img) { 55 | mRow = img.length; 56 | mCol = img[0].length; 57 | mLen = img[0][0].length; 58 | mData = new PLVector[mRow][mCol]; 59 | for (int i = 0; i < mRow; i++) for (int j = 0; j < mCol; j++) { 60 | this.set(i, j, new PLVector(img[i][j])); 61 | } 62 | } 63 | 64 | public PLVector get(int i, int j) { 65 | return mData[i][j]; 66 | } 67 | 68 | public void set(int i, int j, PLVector data) { 69 | mData[i][j] = data; 70 | } 71 | 72 | public double[][][] toDouble3D() { 73 | double[][][] arr = new double[mRow][mCol][mLen]; 74 | for (int i = 0; i < mRow; i++) for (int j = 0; j < mCol; j++) for (int k = 0; k < mLen; k++) { 75 | arr[i][j][k] = get(i,j).get(k); 76 | } 77 | return arr; 78 | } 79 | 80 | public double[] toDouble() { 81 | double[] arr = new double[mRow * mCol * mLen]; 82 | for (int i = 0; i < mRow; i++) for (int j = 0; j < mCol; j++) for (int k = 0; k < mLen; k++) { 83 | arr[mRow * mCol * k + mRow * j + i] = get(i,j).get(k); 84 | } 85 | return arr; 86 | } 87 | 88 | 89 | public double normQuad() { 90 | double norm = 0; 91 | for (int i = 0; i < mRow; i++) for (int j = 0; j < mCol; j++) { 92 | norm += get(i,j).normQuad(); 93 | } 94 | return norm; 95 | } 96 | 97 | public PLImage copy() { 98 | PLVector[][] newData = new PLVector[mRow][mCol]; 99 | for (int i = 0; i < mRow; i++) for (int j = 0; j < mCol; j++) { 100 | newData[i][j] = get(i, j).copy(); 101 | } 102 | return new PLImage(newData); 103 | } 104 | 105 | public static PLImage zeros(int rows, int cols, int len) { 106 | return new PLImage(new double[rows][cols][len]); 107 | } 108 | 109 | 110 | public void show() { 111 | final BufferedImage img = toBufferedImage(); 112 | 113 | JFrame frame = new JFrame("Image test"); 114 | //frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 115 | JPanel panel = new JPanel() { 116 | @Override 117 | protected void paintComponent(Graphics g) { 118 | Graphics2D g2d = (Graphics2D)g; 119 | g2d.clearRect(0, 0, getWidth(), getHeight()); 120 | g2d.setRenderingHint( 121 | RenderingHints.KEY_INTERPOLATION, 122 | RenderingHints.VALUE_INTERPOLATION_BILINEAR); 123 | // Or _BICUBIC 124 | g2d.scale(2, 2); 125 | g2d.drawImage(img, 0, 0, this); 126 | } 127 | }; 128 | panel.setPreferredSize(new Dimension(mRow*4, mCol*4)); 129 | frame.getContentPane().add(panel); 130 | frame.pack(); 131 | frame.setVisible(true); 132 | } 133 | 134 | public BufferedImage toBufferedImage() { 135 | final BufferedImage img = new BufferedImage(mRow, mCol, BufferedImage.TYPE_INT_RGB); 136 | if(mLen == 1) { 137 | for (int i = 0; i < mRow; i++) { 138 | for (int j = 0; j < mCol; j++) { 139 | float c = (float) Math.min(Math.abs(mData[i][j].get(0)), 1.0); 140 | img.setRGB(i, j, new Color(c, c, c).getRGB()); 141 | } 142 | } 143 | } else { 144 | for (int i = 0; i < mRow; i++) { 145 | for (int j = 0; j < mCol; j++) { 146 | float r = (float) Math.min(Math.abs(mData[i][j].get(0)), 1.0); 147 | float g = (float) Math.min(Math.abs(mData[i][j].get(1)), 1.0); 148 | float b = (float) Math.min(Math.abs(mData[i][j].get(2)), 1.0); 149 | img.setRGB(i, j, new Color(r, g, b).getRGB()); 150 | } 151 | } 152 | } 153 | return img; 154 | } 155 | 156 | public static PLImage fromBufferedImage(BufferedImage image) { 157 | double[][][] imgdata = new double[image.getWidth()][image.getHeight()][3]; 158 | float[] tmp = new float[3]; 159 | for(int i = 0; i < image.getWidth(); i++) { 160 | for(int j = 0; j < image.getHeight(); j++) { 161 | Color c = new Color(image.getRGB(i,j)); 162 | c.getRGBColorComponents(tmp); 163 | for (int k = 0; k < 3; k++) { 164 | imgdata[i][j][k] = tmp[k]; 165 | } 166 | } 167 | } 168 | return new PLImage(imgdata); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /Java/src/pottslab/PLProcessor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package pottslab; 5 | import java.util.ArrayList; 6 | import java.util.LinkedList; 7 | import java.util.List; 8 | import java.util.concurrent.Callable; 9 | import java.util.concurrent.ExecutionException; 10 | import java.util.concurrent.ExecutorService; 11 | import java.util.concurrent.Executors; 12 | import java.util.concurrent.Future; 13 | 14 | 15 | /** 16 | * @author Martin Storath 17 | * 18 | */ 19 | 20 | @SuppressWarnings("rawtypes") 21 | public class PLProcessor { 22 | 23 | public PLImage mImg; 24 | public double[][] mWeights; 25 | public int mCol, mRow; 26 | public int mProcessors; 27 | public double mGamma; 28 | public boolean mNonNegative; 29 | public ExecutorService eservice; 30 | 31 | public PLProcessor() { 32 | init(); 33 | } 34 | 35 | public PLProcessor(PLImage img, double[][] weights) { 36 | init(); 37 | set(img, weights); 38 | } 39 | 40 | protected void init() { 41 | setMultiThreaded(true); 42 | setNonNegative(false); 43 | } 44 | 45 | /** 46 | * @param b 47 | */ 48 | public void setNonNegative(boolean b) { 49 | mNonNegative = b; 50 | } 51 | 52 | public void set(PLImage img, double[][] weights) { 53 | mImg = img; 54 | mWeights = weights; 55 | mCol = img.mCol; 56 | mRow = img.mRow; 57 | } 58 | 59 | /** 60 | * Enable/disable parallel execution 61 | * @param bool 62 | */ 63 | public void setMultiThreaded(boolean bool) { 64 | if (bool) { 65 | mProcessors = Runtime.getRuntime().availableProcessors(); 66 | } else { 67 | mProcessors = 1; 68 | } 69 | eservice = Executors.newFixedThreadPool(mProcessors); 70 | } 71 | 72 | public void shutdown() { 73 | eservice.shutdown(); 74 | } 75 | 76 | public void setGamma(double gamma) { 77 | mGamma = gamma; 78 | } 79 | 80 | 81 | /** 82 | * Applies Potts algorithm in horizontal direction 83 | * 84 | * @param gamma 85 | */ 86 | public void applyHorizontally() { 87 | List futuresList = new ArrayList(mRow); 88 | PLVector[] array; 89 | double[] weightsArr; 90 | for (int i = 0; i < mRow; i++) { 91 | // set up arrays 92 | array = new PLVector[mCol]; 93 | weightsArr = new double[mCol]; 94 | for (int j = 0; j < mCol; j++) { 95 | array[j] = mImg.get(i, j); 96 | weightsArr[j] = mWeights[i][j]; 97 | } 98 | // add to task list 99 | futuresList.add(eservice.submit(createTask(array, weightsArr))); 100 | } 101 | for(Future future:futuresList) { 102 | try { 103 | future.get(); 104 | } 105 | catch (InterruptedException e) {e.printStackTrace();} 106 | catch (ExecutionException e) {e.printStackTrace();} 107 | } 108 | } 109 | 110 | /** 111 | * Applies Potts algorithm in vertical direction 112 | * 113 | * @param gamma 114 | */ 115 | public void applyVertically() { 116 | List futuresList = new ArrayList(mCol); 117 | PLVector[] array; 118 | double[] weightsArr; 119 | for (int j = 0; j < mCol; j++) { 120 | // set up arrays 121 | array = new PLVector[mRow]; 122 | weightsArr = new double[mRow]; 123 | for (int i = 0; i < mRow; i++) { 124 | array[i] = mImg.get(i, j); 125 | weightsArr[i] = mWeights[i][j]; 126 | } 127 | futuresList.add(eservice.submit(createTask(array, weightsArr))); 128 | } 129 | 130 | for(Future future:futuresList) { 131 | try { 132 | future.get(); 133 | } 134 | catch (InterruptedException e) {e.printStackTrace();} 135 | catch (ExecutionException e) {e.printStackTrace();} 136 | } 137 | } 138 | 139 | /** 140 | * Applies Potts algorithm in diagonal direction (top left to bottom right) 141 | * 142 | * @param gamma 143 | */ 144 | public void applyDiag() { 145 | int row, col; 146 | List futuresList = new ArrayList(mRow+mCol-1); 147 | PLVector[] array; 148 | double[] weightsArr; 149 | 150 | for (int k = 0; k < (mCol); k++) { 151 | 152 | int sDiag = Math.min(mRow, mCol - k); 153 | 154 | array = new PLVector[sDiag]; 155 | weightsArr = new double[sDiag]; 156 | for (int j = 0; j < sDiag; j++) { 157 | 158 | row = j; 159 | col = j + k; 160 | array[j] = mImg.get(row, col); 161 | weightsArr[j] = mWeights[row][col]; 162 | } 163 | futuresList.add(eservice.submit(createTask(array, weightsArr))); 164 | } 165 | for (int k = mRow-1; k > 0; k--) { 166 | int sDiag = Math.min(mRow - k, mCol); 167 | array = new PLVector[sDiag]; 168 | weightsArr = new double[sDiag]; 169 | for (int j = 0; j < sDiag; j++) { 170 | row = j + k; 171 | col = j; 172 | array[j] = mImg.get(row, col); 173 | weightsArr[j] = mWeights[row][col]; 174 | } 175 | futuresList.add(eservice.submit(createTask(array, weightsArr))); 176 | } 177 | for(Future future:futuresList) { 178 | try { 179 | future.get(); 180 | } 181 | catch (InterruptedException e) {e.printStackTrace();} 182 | catch (ExecutionException e) {e.printStackTrace();} 183 | } 184 | } 185 | 186 | /** 187 | * Applies Potts algorithm in antidiagonal direction (top right to bottom left) 188 | * 189 | * @param gamma 190 | */ 191 | public void applyAntiDiag() { 192 | int row, col; 193 | List futuresList = new ArrayList(mRow+mCol-1); 194 | PLVector[] array; 195 | double[] weightsArr; 196 | for (int k = 0; k < (mCol); k++) { 197 | int sDiag = Math.min(mRow, mCol - k); 198 | array = new PLVector[sDiag]; 199 | weightsArr = new double[sDiag]; 200 | for (int j = 0; j < sDiag; j++) { 201 | row = j; 202 | col = mCol - 1 - (j + k); 203 | array[j] = mImg.get(row, col); 204 | weightsArr[j] = mWeights[row][col]; 205 | } 206 | futuresList.add(eservice.submit(createTask(array, weightsArr))); 207 | } 208 | for (int k = mRow-1; k > 0; k--) { 209 | int sDiag = Math.min(mRow - k, mCol); 210 | array = new PLVector[sDiag]; 211 | weightsArr = new double[sDiag]; 212 | for (int j = 0; j < sDiag; j++) { 213 | row = (j + k); 214 | col = mCol - 1 - j; 215 | array[j] = mImg.get(row, col); 216 | weightsArr[j] = mWeights[row][col]; 217 | } 218 | futuresList.add(eservice.submit(createTask(array, weightsArr))); 219 | } 220 | for(Future future:futuresList) { 221 | try { 222 | future.get(); 223 | } 224 | catch (InterruptedException e) {e.printStackTrace();} 225 | catch (ExecutionException e) {e.printStackTrace();} 226 | } 227 | } 228 | 229 | /** 230 | * Applies univariate Potts algorithm into indicated direction 231 | * 232 | * @param gamma 233 | */ 234 | public void applyToDirection(int[] direction) { 235 | int pCol = direction[0]; 236 | int pRow = direction[1]; 237 | if (pRow == 0) { 238 | this.applyHorizontally(); 239 | return; 240 | } 241 | if (pCol == 0) { 242 | this.applyVertically(); 243 | return; 244 | } 245 | int row, col; 246 | List futuresList = new LinkedList(); 247 | PLVector[] array; 248 | double[] weightsArr; 249 | int sDiag; 250 | for(int rOffset = 0; rOffset < pRow ; rOffset++ ) { 251 | for(int cOffset = rOffset; cOffset < mCol; cOffset++ ) { 252 | sDiag = Math.min( (mCol - cOffset - 1)/pCol, (mRow - rOffset-1)/pRow ) + 1; 253 | array = new PLVector[sDiag]; 254 | weightsArr = new double[sDiag]; 255 | row = rOffset; 256 | col = cOffset; 257 | for (int j = 0; j < sDiag; j++) { 258 | array[j] = mImg.get(row, col); 259 | weightsArr[j] = mWeights[row][col]; 260 | row += pRow; 261 | col += pCol; 262 | } 263 | //futuresList.add(eservice.submit(createTask(array, weightsArr))); 264 | Callable l2Potts = createTask(array, weightsArr); 265 | futuresList.add(eservice.submit(l2Potts)); 266 | } 267 | } 268 | 269 | for(int cOffset = 0; cOffset < pCol ; cOffset++ ) { 270 | for(int rOffset = cOffset; rOffset < mRow; rOffset++ ) { 271 | //int cOffset = 0; 272 | sDiag = Math.min( (mCol - cOffset - 1)/pCol, (mRow - rOffset-1)/pRow ) + 1; 273 | array = new PLVector[sDiag]; 274 | weightsArr = new double[sDiag]; 275 | row = rOffset; 276 | col = cOffset; 277 | for (int j = 0; j < sDiag; j++) { 278 | array[j] = mImg.get(row, col); 279 | weightsArr[j] = mWeights[row][col]; 280 | row += pRow; 281 | col += pCol; 282 | } 283 | //futuresList.add(eservice.submit(createTask(array, weightsArr))); 284 | Callable l2Potts = createTask(array, weightsArr); 285 | futuresList.add(eservice.submit(l2Potts)); 286 | } 287 | } 288 | 289 | for(Future future:futuresList) { 290 | try { 291 | future.get(); 292 | } 293 | catch (InterruptedException e) {e.printStackTrace();} 294 | catch (ExecutionException e) {e.printStackTrace();} 295 | } 296 | } 297 | 298 | public Callable createTask(PLVector[] array, double[] weightsArr) { 299 | //if (mNonNegative) { 300 | // return new L2PottsNonNeg(array, weightsArr, mGamma); 301 | //} else { 302 | return new L2Potts(array, weightsArr, mGamma); 303 | //} 304 | } 305 | 306 | } 307 | -------------------------------------------------------------------------------- /Java/src/pottslab/PLVector.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package pottslab; 5 | 6 | /** 7 | * Data structure for a vector with some frequently used methods 8 | * 9 | * @author Martin Storath 10 | * 11 | */ 12 | public class PLVector { 13 | 14 | private double[] mData; 15 | 16 | public PLVector(double data) { 17 | mData = new double[1]; 18 | mData[0] = data; 19 | } 20 | 21 | public PLVector(double[] data) { 22 | mData = data; 23 | } 24 | 25 | public double get(int idx) { 26 | return mData[idx]; 27 | } 28 | 29 | public double[] get() { 30 | return mData; 31 | } 32 | 33 | public void set(double[] data) { 34 | mData = data; 35 | } 36 | 37 | public void set(int k, double d) { 38 | mData[k] = d; 39 | } 40 | 41 | public double norm() { 42 | return Math.sqrt(this.normQuad()); 43 | } 44 | 45 | public void normalize() { 46 | double norm = this.norm(); 47 | if (norm == 0) { 48 | mData = new double[mData.length]; 49 | } else { 50 | for (int i=0; i < mData.length; i++) { 51 | mData[i] /= norm; 52 | } 53 | } 54 | } 55 | 56 | public double normQuad() { 57 | double norm = 0; 58 | for (int i=0; i < mData.length; i++) { 59 | norm += Math.pow(mData[i], 2); 60 | } 61 | return norm; 62 | } 63 | 64 | 65 | public double sum() { 66 | double sum = 0; 67 | for (int i=0; i < mData.length; i++) { 68 | sum += mData[i]; 69 | } 70 | return sum; 71 | } 72 | 73 | public int length() { 74 | return mData.length; 75 | } 76 | 77 | public PLVector mult(double x) { 78 | double[] data = new double[length()]; 79 | for (int i=0; i < mData.length; i++) { 80 | data[i] = mData[i] * x; 81 | } 82 | return new PLVector(data); 83 | } 84 | 85 | public PLVector multAssign(double x) { 86 | for (int i=0; i < mData.length; i++) { 87 | mData[i] *= x; 88 | } 89 | return this; 90 | } 91 | 92 | public PLVector divAssign(double x) { 93 | for (int i=0; i < mData.length; i++) { 94 | mData[i] /= x; 95 | } 96 | return this; 97 | } 98 | 99 | public PLVector pow(double p) { 100 | double[] data = new double[length()]; 101 | for (int i=0; i < mData.length; i++) { 102 | data[i] = Math.pow(mData[i], p); 103 | } 104 | return new PLVector(data); 105 | } 106 | 107 | public PLVector powAssign(double p) { 108 | for (int i=0; i < mData.length; i++) { 109 | mData[i] = Math.pow(mData[i], p); 110 | } 111 | return this; 112 | } 113 | 114 | public PLVector plus(PLVector x) { 115 | assert x.length() == this.length(); 116 | double[] data = new double[length()]; 117 | for (int i=0; i < mData.length; i++) { 118 | data[i] = mData[i] + x.get(i); 119 | } 120 | return new PLVector(data); 121 | } 122 | 123 | public PLVector plusAssign(PLVector x) { 124 | assert x.length() == this.length(); 125 | for (int i=0; i < mData.length; i++) { 126 | mData[i] += x.get(i); 127 | } 128 | return this; 129 | } 130 | 131 | public PLVector minus(PLVector x) { 132 | assert x.length() == this.length(); 133 | double[] data = new double[length()]; 134 | for (int i=0; i < mData.length; i++) { 135 | data[i] = mData[i] - x.get(i); 136 | } 137 | return new PLVector(data); 138 | } 139 | 140 | public PLVector minusAssign(PLVector x) { 141 | assert x.length() == this.length(); 142 | for (int i=0; i < mData.length; i++) { 143 | mData[i] -= x.get(i); 144 | } 145 | return this; 146 | } 147 | 148 | /** 149 | * Deep copy 150 | * @return 151 | */ 152 | public PLVector copy() { 153 | double datac[] = new double[this.length()]; 154 | for (int i=0; i < mData.length; i++) { 155 | datac[i] = mData[i]; 156 | } 157 | return new PLVector(datac); 158 | } 159 | 160 | public static PLVector zeros(int length) { 161 | return new PLVector(new double[length]); 162 | } 163 | 164 | public static PLVector[] array1DToVector1D(double[] arr) { 165 | int m = arr.length; 166 | PLVector[] vec = new PLVector[m]; 167 | for (int i = 0; i < m; i++) { 168 | vec[i] = new PLVector(arr[i]); 169 | } 170 | return vec; 171 | } 172 | 173 | public static PLVector[] array2DToVector1D(double[][] arr) { 174 | int m = arr.length; 175 | PLVector[] vec = new PLVector[m]; 176 | for (int i = 0; i < m; i++) { 177 | vec[i] = new PLVector(arr[i]); 178 | } 179 | return vec; 180 | } 181 | 182 | public static PLVector[][] array3DToVector2D(double[][][] arr) { 183 | int m = arr.length; 184 | int n = arr[0].length; 185 | PLVector[][] vec = new PLVector[m][n]; 186 | for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) { 187 | vec[i][j] = new PLVector(arr[i][j]); 188 | } 189 | return vec; 190 | } 191 | 192 | public static PLVector[][] array2DToVector2D(double[][] arr) { 193 | int m = arr.length; 194 | int n = arr[0].length; 195 | PLVector[][] vec = new PLVector[m][n]; 196 | for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) { 197 | vec[i][j] = new PLVector(arr[i][j]); 198 | } 199 | return vec; 200 | } 201 | 202 | 203 | public static double[] vector1DToArray1D(PLVector[] vec) { 204 | int m = vec.length; 205 | double[] arr = new double[m]; 206 | for (int i = 0; i < m; i++) { 207 | arr[i]= vec[i].get(0); 208 | } 209 | return arr; 210 | } 211 | 212 | public static double[][] vector1DToArray2D(PLVector[] vec) { 213 | int m = vec.length; 214 | int l = vec[0].length(); 215 | double[][] arr = new double[m][l]; 216 | for (int i = 0; i < m; i++) for (int k = 0; k < l; k++) { 217 | arr[i][k] = vec[i].get(k); 218 | } 219 | return arr; 220 | } 221 | 222 | public static double[][][] vector2DToArray3D(PLVector[][] vec) { 223 | int m = vec.length; 224 | int n = vec[0].length; 225 | int l = vec[0][0].length(); 226 | double[][][] arr = new double[m][n][l]; 227 | for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) for (int k = 0; k < l; k++) { 228 | arr[i][j][k] = vec[i][j].get(k); 229 | } 230 | return arr; 231 | } 232 | 233 | 234 | public static double[][] vector2DToArray2D(PLVector[][] vec) { 235 | int m = vec.length; 236 | int n = vec[0].length; 237 | double[][] arr = new double[m][n]; 238 | for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) { 239 | arr[i][j] = vec[i][j].get(0); 240 | } 241 | return arr; 242 | } 243 | 244 | public String toString() { 245 | String str = "("; 246 | int l = this.length(); 247 | for (int i=0; i < l; i++) { 248 | str += mData[i]; 249 | if (i == l-1) { 250 | str += ")"; 251 | } else { 252 | str += ", "; 253 | } 254 | } 255 | return str; 256 | } 257 | 258 | } 259 | -------------------------------------------------------------------------------- /Java/src/pottslab/RunMe.java: -------------------------------------------------------------------------------- 1 | package pottslab; 2 | 3 | import javax.imageio.ImageIO; 4 | import java.awt.*; 5 | import java.awt.image.BufferedImage; 6 | import java.io.File; 7 | import java.io.IOException; 8 | 9 | public class RunMe { 10 | public static void main(String[] args) throws IOException { 11 | if(args.length < 3) { 12 | System.err.println("pottslab: Multilabel image segmentation based on the Potts model (aka piecewise constant Mumford-Shah model)."); 13 | System.err.println("Algorithm: M. Storath, A. Weinmann. \"Fast partitioning of vector-valued images\" SIAM Journal on Imaging Sciences, 2014"); 14 | System.err.println("USAGE: java -jar pottslab.jar "); 15 | System.exit(1); 16 | } 17 | 18 | PLImage img = PLImage.fromBufferedImage(ImageIO.read(new File(args[0]))); 19 | 20 | double gamma = Double.valueOf(args[2]); 21 | double[][]weights = new double[img.mRow][img.mCol]; 22 | for (int y = 0; y < weights.length; y++) { 23 | for (int x = 0; x < weights[y].length; x++) { 24 | weights[y][x] = 1.0; 25 | } 26 | } 27 | double muInit = gamma * 1e-2; 28 | double muStep = 2; 29 | double stopTol = 1e-10; 30 | boolean verbose = true; 31 | boolean multiThreaded = true; 32 | boolean useADMM = true; 33 | double[] omega = new double[]{Math.sqrt(2.0)-1.0, 1.0-Math.sqrt(2.0)/2.0}; 34 | PLImage img2 = JavaTools.minL2PottsADMM8(img, gamma, weights, muInit, muStep, stopTol, verbose, multiThreaded, useADMM, omega); 35 | 36 | ImageIO.write(img2.toBufferedImage(), "png", new File(args[1])); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Martin Storath, Andreas Weinmann 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 | -------------------------------------------------------------------------------- /Plugins/PottsSegmentationJ_.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/Plugins/PottsSegmentationJ_.jar -------------------------------------------------------------------------------- /Potts/PottsCore/findBestPartition.m: -------------------------------------------------------------------------------- 1 | function [partition, devMatrix] = findBestPartition( f, gamma, dataFidelityNorm, weights ) 2 | %findBestPartition Finds the optimal Potts partition 3 | 4 | % written by M. Storath 5 | % $Date: 2012/10/29 01:19:08 $ $Revision: 0.1 $ 6 | 7 | %-------------------------------------------------------------------------- 8 | %%Initialization 9 | 10 | % n: size of data f 11 | n = numel(f); 12 | % create gamma array 13 | if isscalar(gamma) 14 | gamma = gamma * ones(n, 1); 15 | else 16 | gamma = [gamma(1); gamma(:)]; 17 | end 18 | % B: Bellmann values (indices start at 1!) 19 | B = zeros(n + 1, 1); 20 | B(1) = -gamma(1); 21 | % partition: stores the optimal partition 22 | partition = zeros(n, 1); 23 | 24 | % deviations are only stored if requested, since it needs O(n^2) 25 | % memory 26 | storeDeviations = nargout > 1; 27 | if storeDeviations 28 | devMatrix = zeros(n); 29 | end 30 | 31 | % check input signal 32 | if any(~isfinite(f)) 33 | error('Input vector must consist of finite values.') 34 | end 35 | 36 | %-------------------------------------------------------------------------- 37 | %%Pre-computations 38 | 39 | switch dataFidelityNorm 40 | case 'L1' % fast \ell^1 computation needs an IndexedLinkedList 41 | % create an IndexedLinkedList (java object) 42 | % if IndexedLinkedList is not in the classpath 43 | if exist('weights', 'var') 44 | list = javaObject('pottslab.IndexedLinkedHistogram', weights); 45 | else 46 | list = javaObject('pottslab.IndexedLinkedHistogramUnweighted', n); 47 | 48 | end 49 | 50 | 51 | case 'L2' % \ell^2 computation gets faster by precomputing the moments 52 | if exist('weights', 'var') 53 | error('Weighted Potts implemented for L1 data term only') 54 | end 55 | m = [0; cumsum(real(f(:)) ) ]; % first moments 56 | s = [0; cumsum(real(f(:)).^2 ) ]; % second moments 57 | if ~isreal(f) 58 | mc = [0; cumsum(imag(f(:)) ) ]; % first moments 59 | sc = [0; cumsum(imag(f(:)).^2 ) ]; % second moments 60 | end 61 | end 62 | 63 | %-------------------------------------------------------------------------- 64 | %%The main loop 65 | 66 | %rb: right bound 67 | for rb = 1:n 68 | switch dataFidelityNorm 69 | case 'L1' % fast L1 penalty computation, O(n^2) time 70 | % insert element r to list (sorted) 71 | % -needs O(n) time 72 | % -list is a Java object! 73 | list.insertSorted(f(rb)); 74 | % compute the L1-distances d^*_[lb,rb] for l = 1:rb; 75 | % -needs O(n) time 76 | devL = list.computeDeviations(); 77 | 78 | case 'L1naive' % naive L1 penalty computation, very slow, O(n^3 * log n) time 79 | devL = zeros(rb,1); 80 | for lb = 1:rb 81 | % build signal 82 | arr = f(lb:rb); 83 | w = weights(lb:rb); 84 | % compute median 85 | m = medianw(arr,w); 86 | devL(lb) = sum(abs(arr - m) .* w); 87 | end 88 | 89 | case 'L2' 90 | % compute the L2-deviation d^*_[lb,rb] for lb = 1:rb (vectorized) 91 | lb = (1:(rb))'; 92 | devL = s(rb+1) - s(lb) - (m(rb+1) - m(lb)).^2 ./ (rb - lb + 1); 93 | % in case of complex data 94 | if ~isreal(f) 95 | devLc = sc(rb+1) - sc(lb) - (mc(rb+1) - mc(lb)).^2 ./ (rb - lb + 1); 96 | devL = devL.^2 + devLc.^2; 97 | end 98 | end 99 | % due to round-off errors, deviations might be negative. This is 100 | % corrected here. 101 | devL( devL < 0 ) = 0; 102 | devL( rb ) = 0; 103 | %bv: best value at right bound r 104 | %blb: best left bound at right bound rb 105 | [bv, blb] = min( B(1:rb) + gamma(1:rb) + devL ); 106 | partition( rb ) = blb-1; 107 | B( rb+1 ) = bv; 108 | 109 | % store the deviations, if requested 110 | if storeDeviations 111 | devMatrix(1:rb,rb) = devL; 112 | end 113 | end 114 | 115 | end 116 | 117 | -------------------------------------------------------------------------------- /Potts/PottsCore/iPottsADMM.m: -------------------------------------------------------------------------------- 1 | function [u, dataError, nJumps, energy] = iPottsADMM(f, gamma, A, p, varargin) 2 | %iPottsADMM Computes a minimizer of the inverse Potts functional 3 | % 4 | % The function minimizes the inverse Potts functional 5 | % 6 | % \gamma || D u ||_0 + || A u - f ||_p^p 7 | % 8 | % by an ADMM splitting approach 9 | % 10 | % Syntax: 11 | % [u, dataError, nJumps, energy] = iPottsADMM(f, gamma, A, p) 12 | % 13 | % See also: minL1iPotts, minL2iPotts, minL1iSpars, minL2iSpars 14 | 15 | % written by M. Storath 16 | % $Date: 2012/10/29 01:19:08 $ $Revision: 0.1 $ 17 | 18 | 19 | 20 | % parse options 21 | ip = inputParser; 22 | %addParamValue(ip, 'muInit', gamma/(2*norm(v(:), 2)^2)); 23 | addParamValue(ip, 'muInit', gamma*1e-6); 24 | addParamValue(ip, 'muStep', 1.05); 25 | addParamValue(ip, 'dispStatus', 1); 26 | addParamValue(ip, 'tol', 1e-6); 27 | addParamValue(ip, 'iMax', 1); 28 | addParamValue(ip, 'v', A' * f); 29 | parse(ip, varargin{:}); 30 | par = ip.Results; 31 | 32 | 33 | % init variables 34 | lambda = 0; 35 | v = par.v; 36 | w = zeros(size(v)); 37 | u = Inf; 38 | 39 | % precomputations for solving linear equations 40 | if p == 2 41 | linopts.mat = A' * A; 42 | elseif p ==1 43 | linopts.mat = A * A'; 44 | if isnumeric(A) 45 | m = size(linopts.mat, 1); 46 | linopts.L = spconvmatrix([-1, 2, -1], m); 47 | end 48 | else 49 | error('p must be equal to 1 or 2.') 50 | end 51 | % option for iterative linear solver 52 | linopts.maxit = 500; 53 | 54 | % init mu 55 | mu = par.muInit; 56 | 57 | % counts total number of iterations 58 | iter = 0; 59 | 60 | %%------------------------------------------------------------------------- 61 | %the main loop 62 | while sum(abs(u(:) - v(:)).^2) > par.tol 63 | 64 | % ADMM steps 65 | for i = 1:par.iMax 66 | % solve L^2 Potts problem 67 | u = minL2Potts( v - lambda/mu, 2*gamma/mu ); 68 | nJumps = countJumps(u); 69 | 70 | % prepare right hand side (substitution v = u - w + lambda) 71 | b = A * (u + lambda/mu) - f; 72 | 73 | % switch between L^1 and L^2 data fitting 74 | if p == 2 75 | % initial guess 76 | linopts.init = w; 77 | % solve L^2-Tikhonov problem 78 | [w, linflag] = minL2Tikhonov(b, mu/2, A, linopts); 79 | else 80 | % solve L^1-Tikhonov problem 81 | [w, linflag] = minL1Tikhonov(b, mu/2, A, linopts); 82 | end 83 | 84 | % if an eventual linear equation solver did not converge 85 | if linflag ~= 0 86 | warning('Linear equation solver did not converge. Results may be inaccurate.') 87 | end 88 | 89 | % resubstitute 90 | v = u - w + lambda/mu; 91 | 92 | % update multiplier 93 | lambda = lambda + mu * (u - v); 94 | end 95 | 96 | % increase coupling 97 | mu = mu * par.muStep; 98 | 99 | % update counter 100 | iter = iter+1; 101 | 102 | % show output 103 | switch par.dispStatus 104 | case 2 105 | if mod(iter, 10) == 0 106 | % output 107 | subplot(2,2,1) 108 | showdbl(real(u)); 109 | title('u') 110 | subplot(2,2,2) 111 | showdbl(real(v)); 112 | title('v') 113 | subplot(2,2,3) 114 | showdbl(real(u-v)); 115 | title('u-v') 116 | subplot(2,2,4) 117 | showdbl(real(lambda)); 118 | title('lambda') 119 | drawnow; 120 | 121 | % compute energy values 122 | err = A * u - f; 123 | dataError = sum(abs(err(:)).^p); 124 | energy = gamma * nJumps + dataError; 125 | disp(['Potts-Energy: ' num2str(energy)]); 126 | end 127 | case 1 128 | % show status 129 | if mod(iter, 5) == 0 130 | fprintf('*'); 131 | end 132 | if mod(iter, 200) == 0 133 | fprintf('\n'); 134 | end 135 | end 136 | end 137 | 138 | % final output 139 | err = A * u - f; 140 | dataError = sum(abs(err(:)).^p); 141 | energy = gamma * nJumps + dataError; 142 | 143 | if par.dispStatus > 0 144 | fprintf(' Done. \n'); 145 | end 146 | 147 | end 148 | -------------------------------------------------------------------------------- /Potts/PottsCore/reconstructionFromPartition.m: -------------------------------------------------------------------------------- 1 | function [u, nJumps] = reconstructionFromPartition( f, partition, dataFidelityNorm, weights ) 2 | %reconstructionFromPartition Reconstructs the minimizer from the optimal 3 | %partition 4 | 5 | % written by M. Storath 6 | % $Date: 2012/10/29 01:19:08 $ $Revision: 0.1 $ 7 | 8 | u = zeros(size(f)); % preallocation 9 | % start at right bound rb = n 10 | if isvector(f) 11 | rb = numel(f); 12 | else 13 | rb = size(f, 1); 14 | end 15 | %init nJumps 16 | nJumps = -1; 17 | %check for weights 18 | weighted = exist('weights', 'var'); 19 | 20 | while rb > 0 21 | % partition(rb) stores corresponding optimal left bound lb 22 | lb = partition(rb); 23 | interval = (lb+1) : rb; 24 | switch dataFidelityNorm 25 | case 'L1' 26 | % best approximation on partition is the median 27 | if weighted 28 | % best approximation on partition is the weighted median 29 | muLR = medianw(f(interval), weights(interval)); 30 | else 31 | muLR = median(f(interval)); 32 | end 33 | 34 | case 'L2' 35 | if weighted 36 | % best approximation on partition is the weighted mean value 37 | muLR = sum(f(interval).*weights(interval)) / sum(weights(interval)); 38 | else 39 | % best approximation on partition is the mean value 40 | muLR = mean(f(interval)); 41 | end 42 | 43 | case 'L2v' 44 | % best approximation on partition is the mean value 45 | muLR = mean(f(interval, :),1); 46 | end 47 | % set values of current interval to optimal value 48 | u(interval, :) = ones(numel(interval), 1) * muLR; 49 | % continue with next right bound 50 | rb = lb; 51 | % update nJumps 52 | nJumps = nJumps + 1; 53 | end 54 | u = double(u); 55 | end 56 | 57 | -------------------------------------------------------------------------------- /Potts/minL1Potts.m: -------------------------------------------------------------------------------- 1 | function [u, dataError, nJumps, energy] = minL1Potts( f, gamma, varargin ) 2 | %minL1Potts Minimizes the (non-inverse) L^1-Potts problem 3 | % 4 | %Description 5 | % Minimizes the L^1 Potts functional 6 | % \gamma \| D u \|_0 + \| u - f \|_1 7 | % 8 | % The computations are performed in O(n^2) time 9 | % and O(n) space complexity. 10 | % 11 | %Syntax 12 | % pottsEstimator = minL1Potts( f, gamma ) 13 | % pottsEstimator = minL1Potts( f, gamma, samples ) 14 | % 15 | % samples: sampling points x_i of data f_i, in case of non-equidistant sampling 16 | % 17 | % 18 | %Reference 19 | % A. Weinmann, M. Storath, L. Demaret 20 | % "The $L^1$-Potts functional for robust jump-sparse reconstruction", 21 | % arXiv:1207.4642, http://arxiv.org/abs/1207.4642 22 | % 23 | % See also: minL2Potts, minL1iPotts 24 | 25 | % written by M. Storath 26 | % $Date: 2012/10/29 01:19:08 $ $Revision: 0.1 $ 27 | 28 | %-------------------------------------------------------------------------- 29 | %%check for real data 30 | if not(isreal(f)) 31 | error('Data must be real-valued.') 32 | end 33 | 34 | ip = inputParser; 35 | addParamValue(ip, 'samples', []); 36 | addParamValue(ip, 'weights', []); 37 | parse(ip, varargin{:}); 38 | par = ip.Results; 39 | 40 | if not(isempty(par.samples)) && not(isempty(par.weights)) 41 | error('Cannot define samples and weights at the same time.') 42 | end 43 | 44 | %-------------------------------------------------------------------------- 45 | %%main computation 46 | if isempty(par.samples) && isempty(par.weights) 47 | partition = findBestPartition(f, gamma, 'L1'); 48 | [u, nJumps] = reconstructionFromPartition(f, partition, 'L1'); 49 | dataError = sum( abs(u - f) ); 50 | else 51 | % convert samples to weights 52 | if isempty(par.weights) 53 | if numel(par.samples) ~= numel(f) 54 | error('Data vector and sample vector must be of equal length'); 55 | end 56 | par.weights = samplesToWeights(par.samples); 57 | end 58 | partition = findBestPartition(f, gamma, 'L1', par.weights); 59 | [u, nJumps] = reconstructionFromPartition(f, partition, 'L1', par.weights); 60 | dataError = sum( abs(u(:) - f(:)) .* par.weights(:) ); 61 | end 62 | 63 | energy = gamma * nJumps + dataError; 64 | 65 | %-------------------------------------------------------------------------- 66 | %%show the result, if requested 67 | if nargout == 0 68 | if ~exist('samples', 'var') 69 | samples = linspace(0,1,numel(f)); 70 | end 71 | plot(samples, f, '.', 'MarkerSize', 10); 72 | hold on 73 | stairs(samples, u, 'r', 'LineWidth', 2); 74 | hold off 75 | legend({'Signal', 'L^1-Potts estimate'}); 76 | grid on; 77 | end 78 | 79 | end 80 | 81 | -------------------------------------------------------------------------------- /Potts/minL1iPotts.m: -------------------------------------------------------------------------------- 1 | function [u, dataError, nJumps, energy] = minL1iPotts( f, gamma, A, varargin ) 2 | %minL1iPotts Minimizes the inverse Potts problem 3 | % 4 | %Description 5 | % Minimizes the inverse L^1-Potts functional 6 | % \gamma \| D u \|_0 + \| A u - f \|_1 7 | % by an ADMM splitting approach 8 | % 9 | %Reference 10 | % M. Storath, A. Weinmann, L. Demaret, 11 | % Jump-sparse and sparse recovery using Potts functionals, 12 | % IEEE Transactions on Signal Processing, 2014 13 | % 14 | % See also: minL2iPotts, iPottsADMM 15 | 16 | % written by M. Storath 17 | % $Date: 2013-01-05 17:25:45 +0100 (Sat, 05 Jan 2013) $ $Revision: 63 $ 18 | 19 | [u, dataError, nJumps, energy] = iPottsADMM(f, gamma, A, 1, varargin{:}); 20 | 21 | end 22 | 23 | -------------------------------------------------------------------------------- /Potts/minL2Potts.m: -------------------------------------------------------------------------------- 1 | function u = minL2Potts(f, gamma, weights) 2 | %MINL2POTTS Minimizes the classical L^2-Potts problem 3 | % 4 | %Description 5 | % Computes an exact minimizer of the L^2-Potts functional 6 | % 7 | % \gamma \| D u \|_0 + \| u - f \|_2^2 8 | % 9 | % in O(n^2) time. 10 | % 11 | %Reference: 12 | % F. Friedrich, A. Kempe, V. Liebscher, G. Winkler, 13 | % Complexity penalized M-estimation: Fast computation 14 | % Journal of Computational and Graphical Statistics, 2008 15 | % 16 | % See also: minL2iPotts, minL1Potts, minL1iPotts 17 | 18 | % written by M. Storath 19 | % $Date: 2013-10-04 13:43:18 +0200 (Fr, 04 Okt 2013) $ $Revision: 80 $ 20 | 21 | % weighted and vector valued version exists only in Java 22 | if ~exist('weights', 'var') 23 | weights = ones(size(f)); 24 | end 25 | 26 | siz = size(f); 27 | 28 | complexData = ~isreal(f); 29 | % convert complex data to vector 30 | if complexData 31 | f = [real(f(:)), imag(f(:))]; 32 | end 33 | 34 | u = pottslab.JavaTools.minL2Potts( f, gamma, weights); 35 | 36 | % convert from vector to complex 37 | if complexData 38 | u = u(:,1) + 1i * u(:,2); 39 | end 40 | 41 | % reshape u to size of f 42 | u = reshape(u, siz); 43 | 44 | %-------------------------------------------------------------------------- 45 | %%show the result, if requested 46 | if nargout == 0 47 | plot(f, '.', 'MarkerSize', 10); 48 | hold on 49 | stairs(real(u), 'r', 'LineWidth', 2); 50 | hold off 51 | legend({'Signal', 'L^2-Potts estimate'}); 52 | grid on; 53 | end 54 | 55 | end 56 | -------------------------------------------------------------------------------- /Potts/minL2iPotts.m: -------------------------------------------------------------------------------- 1 | function [u, dataError, nJumps, energy] = minL2iPotts( f, gamma, A, varargin ) 2 | %minL2iPotts Minimizes the inverse Potts problem 3 | % 4 | %Description 5 | % Minimizes the inverse L^2-Potts problem 6 | % \gamma \| D u \|_0 + \| A u - f \|_2^2 7 | % by ADMM 8 | % 9 | %Reference 10 | % M. Storath, A. Weinmann, L. Demaret, 11 | % Jump-sparse and sparse recovery using Potts functionals, 12 | % IEEE Transactions on Signal Processing, 2014 13 | % 14 | % See also: minL2Potts, minL1iPotts, iPottsADMM 15 | 16 | % written by M. Storath 17 | % $Revision: 5.26.4.17 $ $Date: 2012/03/01 02:21:56$ 18 | 19 | [u, dataError, nJumps, energy] = iPottsADMM(f, gamma, A, 2, varargin{:}); 20 | 21 | end 22 | 23 | -------------------------------------------------------------------------------- /Potts2D/Core/iPotts2DADMM.m: -------------------------------------------------------------------------------- 1 | function out = iPotts2DADMM(f, gamma, A, dataterm, varargin) 2 | %iPotts2DADMM Minimization strategy fot the inverse 2D Potts problem 3 | 4 | % written by M. Storath 5 | % $Date: 2014-09-12 12:35:45 +0200 (Fr, 12 Sep 2014) $ $Revision: 117 $ 6 | 7 | % parse options 8 | 9 | backproj = A'*f; 10 | [m,n,~] = size(backproj); 11 | sseq = @(k) k^2.01 * 1e-7; 12 | 13 | % parse options 14 | ip = inputParser; 15 | addParamValue(ip, 'muSeq', sseq ); % by default quadratic + eps progression 16 | addParamValue(ip, 'nuSeq', @(k) 0 ); 17 | addParamValue(ip, 'tol', 1e-3); 18 | addParamValue(ip, 'isotropic', 2); % knight moves neighborhood by default 19 | addParamValue(ip, 'multiThreading', true); 20 | addParamValue(ip, 'verbose', false); 21 | addParamValue(ip, 'weights', ones(m,n)); 22 | addParamValue(ip, 'maxIter', 50000); 23 | addParamValue(ip, 'LSEmaxIter', 1000); 24 | addParamValue(ip, 'LSEtol', []); 25 | addParamValue(ip, 'groundTruth', []); 26 | 27 | parse(ip, varargin{:}); 28 | par = ip.Results; 29 | 30 | assert(par.tol > 0, 'Stopping tolerance must be > 0.'); 31 | assert((dataterm == 2), 'Currently only L2 data terms supported.'); 32 | 33 | % counts total number of iterations 34 | nIter = 0; 35 | 36 | % options for iterative linear solver 37 | LSEtol.maxit = par.LSEmaxIter; 38 | LSEtol.tol = par.LSEtol; 39 | 40 | % initialize variables 41 | 42 | switch par.isotropic 43 | case 0 44 | % anisotropic finite differences 45 | nhood = { [1,0] }; 46 | omega = { 1 }; 47 | case 1 48 | % finite differences with diagonals 49 | nhood = { [1,0], [1,1] }; 50 | omega = { sqrt(2) - 1,... 51 | 1 - sqrt(2)/2 }; 52 | case 2 53 | % finite differences with knight moves 54 | nhood = { [1,0], [1,1], [2,1], [1,2] }; 55 | omega = { sqrt(5) - 2,... 56 | sqrt(5) - 3/2 * sqrt(2),... 57 | 0.5 * (1 + sqrt(2) - sqrt(5)),... 58 | 0.5 * (1 + sqrt(2) - sqrt(5)) }; 59 | 60 | otherwise 61 | error('Value of isotropic must be 0, 1, or 2') 62 | end 63 | 64 | S = numel(nhood) * 2; 65 | 66 | u = cell(S, 1); 67 | lam = cell(S, 1); 68 | rho = cell(S, S); 69 | for s = 1:S 70 | u{s} = zeros(size(backproj)); 71 | lam{s} = zeros(size(backproj)); 72 | for t = s+1:S 73 | rho{s,t} = zeros(size(backproj)); 74 | end 75 | end 76 | v = zeros(size(backproj)); 77 | 78 | 79 | % init datastructures -> Java 80 | proc1 = javaObject('pottslab.PLProcessor'); 81 | proc1.setMultiThreaded(par.multiThreading); 82 | bufferImg1 = javaObject('pottslab.PLImage', u{1}); 83 | proc1.set(bufferImg1, par.weights); 84 | 85 | proc2 = javaObject('pottslab.PLProcessor'); 86 | proc2.setMultiThreaded(par.multiThreading); 87 | bufferImg2 = javaObject('pottslab.PLImage', rot90(u{2})); 88 | proc2.set(bufferImg2, par.weights); 89 | 90 | for k=1:par.maxIter 91 | 92 | % set coupling parameter 93 | mu = par.muSeq(k); 94 | nu = par.nuSeq(k); 95 | 96 | q = zeros(size(backproj)); 97 | for a = 1:S/2 98 | % apply univariate Potts to nhood directions 99 | s = 2*a -1; 100 | % prepare data 101 | aux = 0; 102 | for r = 1:s-1 103 | aux = aux + nu * u{r} + rho{r,s}; 104 | end 105 | for t = s+1:S 106 | aux = aux + nu * u{t} - rho{s,t}; 107 | end 108 | bufferImg1.set( (mu * v + lam{s} + aux) / (mu + nu * (S - 1)) ); 109 | proc1.setGamma(2 * gamma * omega{a} / (mu + nu * (S - 1))); 110 | proc1.set(bufferImg1, par.weights); 111 | proc1.applyToDirection(nhood{a}); 112 | 113 | u{s} = plimage2double(bufferImg1); 114 | 115 | % set data for Tikhonov problem 116 | q = q + u{s} - lam{s}/mu; 117 | 118 | % apply same to orthogonal directions 119 | s = 2*a; 120 | % prepare data 121 | aux = 0; 122 | for r = 1:s-1 123 | aux = aux + nu * u{r} + rho{r,s}; 124 | end 125 | for t = s+1:S 126 | aux = aux + nu * u{t} - rho{s,t}; 127 | end 128 | bufferImg2.set( rot90( (mu * v + lam{s} + aux) / (mu + nu * (S - 1)) )); 129 | proc2.setGamma(2 * gamma * omega{a} / (mu + nu * (S - 1))); 130 | proc2.set(bufferImg2, rot90(par.weights)); 131 | proc2.applyToDirection(nhood{a}); 132 | u{s} = rot90( plimage2double(bufferImg2), -1 ); 133 | % set data for Tikhonov problem 134 | q = q + u{s} - lam{s}/mu; 135 | end 136 | 137 | % offset for Tikhonov problem 138 | LSEtol.u0 = q/S; 139 | v = minL2Tikhonov(f, mu/2 * S, A, LSEtol); 140 | 141 | % init h for next Tikhonov iteration ("warm start") 142 | LSEtol.init = v; 143 | 144 | % update Lagrange multipliers 145 | for s = 1:S 146 | lam{s} = lam{s} + mu * (v - u{s}); 147 | for t = (s+1):S 148 | rho{s,t} = rho{s,t} + nu * (u{s} - u{t}); 149 | end 150 | end 151 | 152 | % 153 | nIter = nIter + 1; 154 | uScale = (norm(u{1}(:), 2) + norm(u{2}(:), 2)); 155 | relError = norm(u{1}(:) - u{2}(:),2)/ uScale; 156 | %relError = norm(u{end}(:) - u{end-1}(:),2)/norm(u{end}(:) + u{end-1}(:), 2); 157 | 158 | if ((relError < par.tol) || (nIter > par.maxIter) || (uScale == 0)) && (nIter > 1) 159 | break; 160 | end 161 | 162 | 163 | if par.verbose && (mod(nIter, par.verbose) ==0) 164 | T = S/2 + 1; 165 | out = 0; 166 | for s = 1:S 167 | out = out + u{s}; 168 | end 169 | out = out/S; 170 | subplot(2,T, T) 171 | imshow(f, []) 172 | title('Data') 173 | subplot(2, T, 2*T) 174 | imshow(out); 175 | title('Average of all u') 176 | 177 | for s= 1:S/2 178 | subplot(2, T, s) 179 | imshow(u{s}); 180 | title(sprintf('u %i', s) ) 181 | subplot(2, T, T + s ) 182 | imshow(u{s + S/2}); 183 | title(sprintf('u %i', s + S/2) ) 184 | end 185 | 186 | colormap gray; 187 | drawnow; 188 | fprintf('Mu: %f, ', mu); 189 | fprintf('Rel. error: %f', relError); 190 | if ~isempty(par.groundTruth) 191 | fprintf(', PSNR: %f', plpsnr(par.groundTruth, out)); 192 | end 193 | fprintf('\n'); 194 | end 195 | end 196 | 197 | out = zeros(size(backproj)); 198 | for s = 1:S 199 | out = out + u{s}; 200 | end 201 | out = out/S; 202 | 203 | end 204 | -------------------------------------------------------------------------------- /Potts2D/minL2Potts2DADMM.m: -------------------------------------------------------------------------------- 1 | function u = minL2Potts2DADMM(f, gamma, varargin) 2 | %minL2Potts2DADMM A ADMM splitting strategy for the two-dimensional vector valued L^2-Potts 3 | %problem 4 | % 5 | %Description: 6 | % Minimizes the (2D) Potts problem 7 | % 8 | % \gamma \| u \|_0 + \| A u - f \|_p^p -> min 9 | % 10 | % using ADMM splitting an dynamic programming 11 | % 12 | %Reference: 13 | % M. Storath, A. Weinmann 14 | % Fast partitioning of vector-valued images", 15 | % SIAM Journal on Imaging Sciences, 2014 16 | 17 | % written by M. Storath 18 | % $Date: 2014-06-30 11:26:34 +0200 (Mo, 30. Jun 2014) $ $Revision: 99 $ 19 | 20 | [m,n,~] = size(f); 21 | 22 | % parse options 23 | ip = inputParser; 24 | addParamValue(ip, 'muInit', gamma*1e-2); 25 | addParamValue(ip, 'muStep', 2); 26 | addParamValue(ip, 'tol', 1e-10); 27 | addParamValue(ip, 'isotropic', true); 28 | addParamValue(ip, 'verbose', false); 29 | addParamValue(ip, 'weights', ones(m,n)); 30 | addParamValue(ip, 'multiThreading', true); 31 | addParamValue(ip, 'quantization', true); 32 | addParamValue(ip, 'useADMM', true); 33 | parse(ip, varargin{:}); 34 | par = ip.Results; 35 | 36 | % check args 37 | assert(par.muStep > 1, 'Variable muStep must be > 1.'); 38 | assert(all(par.weights(:) >= 0), 'Weights must be >= 0.'); 39 | assert(par.tol > 0, 'Stopping tolerance must be > 0.'); 40 | assert(par.muInit > 0, 'muInit must be > 0.'); 41 | 42 | % cast data to PLImage 43 | plf = pottslab.PLImage(f); 44 | 45 | % main program (calls Java routines) 46 | if par.isotropic 47 | % near-isotropic discretization 48 | omega(1) = sqrt(2.0) - 1.0; 49 | omega(2) = 1.0 - sqrt(2.0)/2.0; 50 | % alternative neighborhood weights 51 | %omega(1) = (2 * sqrt(2.0) - 1.0)/3; 52 | %omega(2) = (2 - sqrt(2.0))/3; 53 | plu = pottslab.JavaTools.minL2PottsADMM8(plf, gamma, par.weights, par.muInit, par.muStep, par.tol, par.verbose, par.multiThreading, par.useADMM, omega); 54 | 55 | else 56 | % anisotropic discretization 57 | plu = pottslab.JavaTools.minL2PottsADMM4(plf, gamma, par.weights, par.muInit, par.muStep, par.tol, par.verbose, par.multiThreading, par.useADMM); 58 | end 59 | 60 | % reshape the 1D array given by .toDouble() 61 | u = reshape( plu.toDouble(), size(f) ); 62 | 63 | % to remove small remaining variations in result (algorithm works with floats) 64 | if par.quantization 65 | u = round(u * 255)/255; 66 | end 67 | 68 | end 69 | -------------------------------------------------------------------------------- /Potts2D/minL2iPotts2DADMM.m: -------------------------------------------------------------------------------- 1 | function u = minL2iPotts2DADMM(f, gamma, A, varargin) 2 | 3 | u = iPotts2DADMM(f, gamma, A, 2, varargin{:}); 4 | 5 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pottslab 2 | 3 | Pottslab is a Matlab/Java toolbox for the reconstruction of 4 | jump-sparse signals and images using the Potts model (also known as "piecewise constant Mumford-Shah model" or "l0 gradient model"). 5 | Applications include denoising of piecewise constant signals, step detection and 6 | segmentation of multichannel image. 7 | 8 | -- See also the Pick of the Week on [![View Pottslab - Multilabel segmentation of vectorial data on File Exchange](https://www.mathworks.com/matlabcentral/images/matlab-file-exchange.svg)](https://de.mathworks.com/matlabcentral/fileexchange/62641-pottslab-multilabel-segmentation-of-vectorial-data) -- 9 | 10 | 11 | 12 | ## Application examples 13 | 14 | ### Segmentation of vector-valued images 15 | 16 | 17 | - Supports segmentation of vector-valued images (e.g. multispectral images, feature images) 18 | - Linear complexity in number of color channels 19 | - Label-free: No label discretization required 20 | 21 | ![Vector-valued segmentation](/Docs/titleImage.png) 22 | 23 | Left: A natural image; Right: Result using Potts model 24 | 25 | ![Vector-valued segmentation](/Docs/texture.png) 26 | 27 | Texture segmentation using highdimensional curvelet-based feature vectors 28 | 29 | Used as segmentation method in 30 | - A. Breger et al., [Supervised learning and dimension reduction techniques for quantification of retinal fluid in optical coherence tomography images,](https://www.nature.com/articles/eye201761) Eye (2017). 31 | 32 | ### Joint image reconstruction and segmentation 33 | 34 | 35 | - Applicable to many imaging operators, e.g. convolution, Radon transform, MRI, PET, MPI: only implementation of proximal mapping reuqired 36 | - Supports vector-valued data 37 | - Label-free: Labels need NOT be chosen a-priori 38 | 39 | 40 | ![Phantom](/Docs/radon/phantom.png) 41 | ![Phantom](/Docs/radon/recFBPRamLak.png) 42 | ![Phantom](/Docs/radon/recPotts.png) 43 | 44 | Left: Shepp-Logan phantom; Center: Filtered backprojection from 7 angular projections; Right: Joint reconstruction and segmentation using the Potts model from 7 angular projections 45 | 46 | 51 | 52 | ### Denoising of jump-sparse/piecewise-constant signals, or step detection/changepoint detection 53 | 54 | 55 | - L1 Potts model is robust to noise and to moderately blurred data 56 | - Fast and exact solver for L1 Potts model 57 | - Approximative strategies for severely blurred data 58 | 59 | 60 | ![Phantom](/Docs/potts1d.png) 61 | 62 | Top: Noisy signal; Bottom: Minimizer of Potts functional (ground truth in red) 63 | 64 | Used as step detection algorithm in 65 | 66 | * A. Nord et al., [Catch bond drives stator mechanosensitivity in the bacterial flagellar motor](http://www.pnas.org/content/early/2017/11/27/1716002114.full), Proceedings of the National Academy of Sciences, 2017 67 | * A. Szorkovszky et al., [Assortative interactions revealed by sorting of animal groups](https://www.sciencedirect.com/science/article/pii/S0003347218301799), Animal Behaviour, 2018 68 | 69 | ## Usage Instructions 70 | ### Standalone usage from command line (only image plain image segmentation supported) 71 | - Call "java -jar pottslab-standalone.jar input output.png gamma" where gamma is a positive real number, e.g. 0.1 (thanks to fxtentacle) 72 | 73 | ### Installation for Matlab (all features usable) 74 | #### Quickstart: 75 | - Run the script "installPottslab.m", it should set all necessary paths 76 | - For best performance, increase Java heap space in the Matlab preferences (MATLAB - General - Java heap memory) 77 | - Run a demo from the Demos folder 78 | 79 | #### Troubleshooting: 80 | * Problem: OutOfMemoryException 81 | * Solution: Increase Java heap space in the Matlab preferences (MATLAB - General - Java heap memory) 82 | 83 | * Problem: Undefined variable "pottslab" or class "pottslab.JavaTools.minL2Potts" 84 | * Solution: 85 | - Run setPLJavaPath.m 86 | - Maybe you need to install Java 1.7 (see e.g. http://undocumentedmatlab.com/blog/using-java-7-in-matlab-r2013a-and-earlier) 87 | 88 | ## Plugins for Image Analysis GUIs 89 | Parts of Pottslab can be used without Matlab as pure Java plugins 90 | - [Icy plugin](http://icy.bioimageanalysis.org/plugin/potts-segmentation/) - an interactive image segmentation plugin based on Pottslab (written by Vasileios Angelopoulos) 91 | - [ImageJ plugin](Plugins/PottsSegmentationJ_.jar) - an ImageJ frontend for Pottslab (written by Michael Kaul) 92 | 93 | ## References 94 | - M. Storath, A. Weinmann, J. Frikel, M. Unser. 95 | "Joint image reconstruction and segmentation using the Potts model" 96 | Inverse Problems, 2015 97 | - A. Weinmann, M. Storath. "Iterative Potts and Blake-Zisserman minimization for the recovery of functions with discontinuities from indirect measurements." Proceedings of The Royal Society A, 471(2176), 2015 98 | - A. Weinmann, M. Storath, L. Demaret. 99 | "The L1-Potts functional for robust jump-sparse reconstruction" 100 | SIAM Journal on Numerical Analysis, 2015 101 | - M. Storath, A. Weinmann. 102 | "Fast partitioning of vector-valued images" 103 | SIAM Journal on Imaging Sciences, 2014 104 | - M. Storath, A. Weinmann, L. Demaret. 105 | "Jump-sparse and sparse recovery using Potts functionals" 106 | IEEE Transactions on Signal Processing, 2014 107 | 108 | 109 | ## See also 110 | - [PALMS Image Segmentation for piecewise affine linear estimation](https://github.com/lu-kie/PALMS_ImagePartitioning) 111 | - [Higher order Mumford-Shah models for piecewise smooth signals](https://github.com/lu-kie/HOMS_SignalProcessing) 112 | - [Smoothing splines for discontinuous signals](https://github.com/mstorath/CSSD) 113 | - [Mumford-Shah regularization for piecewise smooth images](https://github.com/mstorath/MumfordShah2D) 114 | - [L1 TV for robust piecewise constant signals](https://github.com/mstorath/L1TV) 115 | 116 | 117 | -------------------------------------------------------------------------------- /Sparsity/SparsityCore/iSparsADMM.m: -------------------------------------------------------------------------------- 1 | function [u, dataError, nSpikes, energy] = iSparsADMM(f, gamma, A, p, varargin) 2 | %iSparsPottsADMM Minimizes the sparsity problem 3 | % 4 | %Description: 5 | % Minimizes the sparsity problem 6 | % 7 | % \gamma \| u \|_0 + \| A u - f \|_p^p -> min 8 | % 9 | % using a direct ADMM method 10 | % 11 | %Reference 12 | % M. Storath, A. Weinmann, L. Demaret, 13 | % Jump-sparse and sparse recovery using Potts functionals, 14 | % IEEE Transactions on Signal Processing, 2014 15 | % 16 | 17 | % written by M. Storath 18 | % $Date: 2014-06-30 13:19:43 +0200 (Mo, 30. Jun 2014) $ $Revision: 102 $ 19 | 20 | % precomputations for solving linear equations 21 | if p == 2 22 | linopts.mat = A' * A; 23 | elseif p ==1 24 | linopts.mat = A * A'; 25 | m = size(linopts.mat, 1); 26 | linopts.L = spconvmatrix([-1, 2, -1], m); 27 | else 28 | error('p must be equal to 1 or 2.') 29 | end 30 | % option for iterative linear solver 31 | linopts.maxit = 500; 32 | 33 | % init variables 34 | v = A' * f; 35 | lambda = 0; 36 | w = zeros(size(v)); 37 | u = Inf; 38 | 39 | % parse options 40 | ip = inputParser; 41 | addParamValue(ip, 'muInit', gamma*1e-6); 42 | addParamValue(ip, 'muStep', 1.05); 43 | addParamValue(ip, 'dispStatus', 1); 44 | addParamValue(ip, 'tol', 1e-6); 45 | addParamValue(ip, 'iMax', 1); 46 | parse(ip, varargin{:}); 47 | par = ip.Results; 48 | 49 | % init 50 | mu = par.muInit; 51 | 52 | % counts total number of iterations 53 | iter = 0; 54 | 55 | %%------------------------------------------------------------------------- 56 | %the main loop 57 | while sum(abs(u(:) - v(:)).^2) > par.tol 58 | 59 | % ADMM steps 60 | for i = 1:par.iMax 61 | % solve L^2 Potts problem 62 | [u, ~, nSpikes] = minL2Spars( v - lambda/mu, 2*gamma/mu ); 63 | 64 | % prepare right hand side (substitution v = u - w + lambda) 65 | b = A * (u + lambda/mu) - f; 66 | 67 | % switch between L^1 and L^2 data fitting 68 | if p == 2 69 | % initial guess (iterative solvers only) 70 | linopts.init = w; 71 | % solve L^2-Tikhonov problem 72 | [w, linflag] = minL2Tikhonov(b, mu/2, A, linopts); 73 | else 74 | % solve L^1-Tikhonov problem 75 | [w, linflag] = minL1Tikhonov(b, mu/2, A, linopts); 76 | %w = ssn_l1(A, b, mu/2); 77 | end 78 | 79 | % if an eventual linear equation solver did not converge 80 | if linflag ~= 0 81 | warning('Linear equation solver did not converge. Results may be inaccurate.') 82 | end 83 | 84 | % resubstitute 85 | v = u - w + lambda/mu; 86 | 87 | % update multiplier 88 | lambda = lambda + mu*(u - v); 89 | end 90 | 91 | % increase coupling 92 | mu = mu * par.muStep; 93 | 94 | % update counter 95 | iter = iter+1; 96 | 97 | % show output 98 | if par.dispStatus == 2 99 | if mod(iter, 10) == 0 100 | % output 101 | subplot(2,2,1) 102 | showdbl(real(u)); 103 | title('u') 104 | subplot(2,2,2) 105 | showdbl(real(v)); 106 | title('v') 107 | subplot(2,2,3) 108 | showdbl(real(u-v)); 109 | title('u-v') 110 | subplot(2,2,4) 111 | showdbl(real(lambda)); 112 | title('lambda') 113 | drawnow; 114 | 115 | % compute energy values 116 | err = A * u - f; 117 | dataError = sum(abs(err(:)).^p); 118 | energy = gamma * nSpikes + dataError; 119 | disp(['Potts-Energy: ' num2str(energy)]); 120 | end 121 | elseif par.dispStatus == 1 122 | % show status 123 | if mod(iter, 5) == 0 124 | fprintf('*'); 125 | end 126 | if mod(iter, 200) == 0 127 | fprintf('\n'); 128 | end 129 | end 130 | end 131 | 132 | err = A * u - f; 133 | dataError = sum(abs(err(:)).^p); 134 | energy = gamma * nSpikes + dataError; 135 | 136 | fprintf(' Done. \n'); 137 | 138 | end 139 | -------------------------------------------------------------------------------- /Sparsity/SparsityCore/iSparsByPottsADMM.m: -------------------------------------------------------------------------------- 1 | function [u, dataError, nSpikes, energy] = iSparsByPottsADMM(f, gamma, A, p, opts) 2 | %iSparsPottsADMM Minimizes the sparsity problem using an Potts ADMM method 3 | % 4 | %Description: 5 | % Minimizes the sparsity functional 6 | % 7 | % \gamma \| u \|_0 + \| A u - f \|_p^p 8 | % 9 | % using the inverse Potts functional (see iPottsADMM) 10 | 11 | % written by M. Storath 12 | % $Date: 2013-04-05 12:22:32 +0200 (Fr, 05. Apr 2013) $ $Revision: 73 $ 13 | 14 | % create differentiation matrix 15 | if isa(A, 'linop') 16 | D = linop( @(x) diff(x), @(x) conv(x, [-1 1], 'full') ); 17 | elseif isa(A, 'convop') 18 | d(numel(f),1) = -1; d(1) = 1; 19 | D = convop(fft(d)); 20 | else 21 | D = spdiffmatrix(size(A, 2)+1); 22 | end 23 | 24 | % create substitution matrix 25 | B = A * D; 26 | 27 | % parse parameters 28 | if ~exist('opts', 'var') 29 | % standard parameters 30 | opts.muInit = gamma * 1e-6; 31 | %opts.muInit = 2 * gamma / norm(B' * f, 2)^2 ; 32 | opts.muStep = 1.05; 33 | opts.dispStatus = 1; 34 | opts.tol = 1e-6; 35 | opts.iMax = 1; 36 | end 37 | 38 | % call Potts problem with matrix A * D 39 | v = iPottsADMM(f, gamma, B, p, opts); 40 | 41 | % resubstitution 42 | u = real(D * v); 43 | 44 | % compute error 45 | res = A * u - f; 46 | dataError = sum(res(:).^p); 47 | 48 | % count number of spikes 49 | nSpikes = sum(u(:) ~= 0); 50 | 51 | % total energy 52 | energy = gamma * nSpikes + dataError; 53 | 54 | end 55 | -------------------------------------------------------------------------------- /Sparsity/SparsityCore/minSpars.m: -------------------------------------------------------------------------------- 1 | function [u, dataError, nSpikes, energy] = minSpars( f, gamma, p ) 2 | %MINSPARS Solves the sparsity problem 3 | % 4 | % \| u \|_0 + \| u - f \|_p^p -> min 5 | % 6 | % using thresholding 7 | 8 | u = (abs(f).^p >= gamma) .* f; 9 | dataError = sum(u(:) - f(:)).^p; 10 | nSpikes = sum(u(:) ~= 0); 11 | energy = gamma * nSpikes + dataError; 12 | 13 | end 14 | 15 | -------------------------------------------------------------------------------- /Sparsity/minL1Spars.m: -------------------------------------------------------------------------------- 1 | function u = minL1Spars( f, gamma ) 2 | %MINL1SPARS Solves the sparsity problem 3 | % 4 | % argmin \| u \|_0 + \| u - f \|_1 -> min 5 | % 6 | % using thresholding 7 | 8 | u = minSpars(f, gamma, p); 9 | 10 | end 11 | 12 | -------------------------------------------------------------------------------- /Sparsity/minL1iSpars.m: -------------------------------------------------------------------------------- 1 | function [u, dataError, nSpikes, energy] = minL1iSpars( f, gamma, A, varargin ) 2 | %minL1SparsInv Minimizes the sparsity problem 3 | % 4 | %Description 5 | % Minimizes the L^1-sparsity problem 6 | % 7 | % \gamma \| u \|_0 + \| A u - f \|_1 -> min 8 | % 9 | % using the inverse Potts functional 10 | % 11 | % 12 | %Reference 13 | % M. Storath, A. Weinmann, L. Demaret, 14 | % Jump-sparse and sparse recovery using Potts functionals, 15 | % IEEE Transactions on Signal Processing, 2014 16 | % 17 | % See also: minL2iSpars, minL1iSpars, iSparsByPottsADMM 18 | 19 | % written by M. Storath 20 | % $Date: 2012/10/29 01:19:08 $ $Revision: 0.1 $ 21 | 22 | [u, dataError, nSpikes, energy] = iSparsByPottsADMM( f, gamma, A, 1, varargin{:} ); 23 | 24 | end 25 | 26 | -------------------------------------------------------------------------------- /Sparsity/minL2Spars.m: -------------------------------------------------------------------------------- 1 | function [u, dataError, nSpikes, energy] = minL2Spars( f, gamma) 2 | %MINL2SPARS Solves the sparsity problem 3 | % 4 | % argmin \| u \|_0 + \| u - f \|_2^2 5 | % 6 | % using thresholding 7 | 8 | [u, dataError, nSpikes, energy] = minSpars(f, gamma, 2); 9 | 10 | end 11 | 12 | -------------------------------------------------------------------------------- /Sparsity/minL2iSpars.m: -------------------------------------------------------------------------------- 1 | function [u, dataError, nSpikes, energy] = minL2iSpars( f, gamma, A, method, varargin ) 2 | %minL2iSpars Minimizes the sparsity problem 3 | % 4 | %Description 5 | % Minimizes the L^2 sparsity problem 6 | % \gamma \| u \|_0 + \| A u - f \|_2^2 -> min 7 | % 8 | % using inverse Potts functionals (see iPottsADMM) 9 | % 10 | %Reference 11 | % M. Storath, A. Weinmann, L. Demaret, 12 | % Jump-sparse and sparse recovery using Potts functionals, 13 | % IEEE Transactions on Signal Processing, 2014 14 | % 15 | % See also: minL1iSpars, iPottsADMM 16 | 17 | % written by M. Storath 18 | % $Date: 2012/10/29 01:19:08 $ $Revision: 0.1 $ 19 | 20 | if not(exist('method', 'var')) 21 | method = 'PottsADMM'; 22 | end 23 | 24 | 25 | switch method 26 | case 'PottsADMM' 27 | [u, dataError, nSpikes, energy] = iSparsByPottsADMM( f, gamma, A, 2, varargin{:} ); 28 | case 'ADMM' 29 | [u, dataError, nSpikes, energy] = iSparsADMM( f, gamma, A, 2, varargin{:} ); 30 | otherwise 31 | error('This method does not exist.') 32 | end 33 | 34 | 35 | end 36 | 37 | -------------------------------------------------------------------------------- /Tikhonov/minL1Tikhonov.m: -------------------------------------------------------------------------------- 1 | function [u, flag] = minL1Tikhonov(f, gamma, A, opts) 2 | %minL1Tikhonov Tikhonov regularization with L^1 data term 3 | % 4 | % Solves the optimization problem 5 | % \gamma \| u \|_2^2 + \| A u - f\|_1 -> min 6 | % by a semi-smooth Newton method 7 | % 8 | % u = minL1Tikhonov(f, gamma, A) 9 | % 10 | % 11 | % Reference: 12 | % Clason, Jin, Kunisch 13 | % A semismooth Newton method for L^1 data fitting with automatic choice of regularization parameters and noise calibration 14 | % SIAM Journal on Scientific Computing, 2010 15 | % 16 | 17 | % written by M. Storath 18 | % $Date: 2012/10/29 01:19:08 $ $Revision: 0.1 $ 19 | 20 | %%------------------------------------------------------------------------- 21 | % init 22 | m = numel(f); 23 | maxit = []; 24 | init = []; 25 | flag = 0; 26 | if exist('opts', 'var') 27 | if isfield(opts, 'mat') 28 | AAT = opts.mat; 29 | end 30 | if isfield(opts, 'maxit') 31 | maxit = opts.maxit; 32 | end 33 | if isfield(opts, 'init') 34 | init = opts.init; 35 | end 36 | if isfield(opts, 'L') 37 | L = opts.L; 38 | end 39 | end 40 | if ~exist('AAT', 'var') 41 | AAT = A * A'; 42 | end 43 | if ~isa(A, 'linop') && ~exist('L', 'var') 44 | L = spconvmatrix([-1, 2, -1], m); 45 | end 46 | 47 | % diagonal idx 48 | diagidx = eye(m) == 1; 49 | 50 | % standard parameters (see Clason, Jin, Kunisch 2010) 51 | iMax = 20; %(iMax higher than standard) 52 | c = 1e9; 53 | beta = 1; 54 | q = 0.2; 55 | 56 | % init variables 57 | p = zeros(m,1); 58 | pos = p; 59 | neg = p; 60 | alpha = 2 * gamma; 61 | 62 | %deactivate warnings temporaily 63 | warning off; 64 | 65 | %%------------------------------------------------------------------------- 66 | % main program 67 | while beta > 1e-16 68 | pOld = p; 69 | 70 | % precompute matrices 71 | if ~isa(A, 'linop') 72 | M = (1/alpha) * AAT + beta * L; 73 | diagM = M(diagidx); 74 | end 75 | 76 | % newton step 77 | for k = 1:iMax 78 | % compute active sets 79 | posNew = p > +1; 80 | negNew = p < -1; 81 | comb = posNew | negNew; 82 | 83 | % solve linear equation 84 | 85 | b = f(:) + c * (posNew - negNew); 86 | if isa(A, 'linop') 87 | M = @(x) reshape(AAT * (x / alpha), m, 1) + beta * conv(x(:), [-1; 2; -1], 'same') + c * comb .* x; 88 | [p, flag] = cgs(M, b, [], maxit, [], [], init); 89 | else 90 | M(diagidx) = diagM + c * comb; 91 | p = M \ b; 92 | end 93 | 94 | % if active sets did not change, break 95 | if ~(any(posNew - pos) || any(negNew - neg)) 96 | break; 97 | end 98 | 99 | % set new active sets 100 | pos = posNew; 101 | neg = negNew; 102 | end 103 | 104 | if (k == iMax) && any(abs(p)>2) 105 | p = pOld; 106 | break; 107 | end 108 | 109 | % decrease beta 110 | beta = beta * q; 111 | end 112 | 113 | % restore primal variable 114 | u = A' * (p / alpha); 115 | 116 | % reactivate warnings 117 | warning on; -------------------------------------------------------------------------------- /Tikhonov/minL2Tikhonov.m: -------------------------------------------------------------------------------- 1 | function [u, flag] = minL2Tikhonov(f, gamma, A, varargin) 2 | %MINL2TIKHONOV Solves the L^2 Tikhonov problem 3 | % 4 | % Computes a minimizer of the L^2 Tikhonov functional 5 | % 6 | % \| A u - f \|_2^2 + \gamma \| u - u0 \|_2^2 7 | % 8 | % using the normal equation 9 | % 10 | % See also: minL1Tikhonov, minL2iPotts, minL2iSpars, iPottsADMM 11 | 12 | % written by M. Storath 13 | % $Date: 2012/10/29 01:19:08 $ $Revision: 0.1 $ 14 | 15 | % init 16 | flag = 0; % flag for iterative solver 17 | 18 | % parse input 19 | ip = inputParser; 20 | addParamValue(ip, 'mat', A'*A); % A'*A 21 | addParamValue(ip, 'maxit', []); % max iterations 22 | addParamValue(ip, 'init', []); % initial guess 23 | addParamValue(ip, 'u0', []); % offset 24 | addParamValue(ip, 'tol', []); % stop tolerance 25 | addParamValue(ip, 'verbose', false); % show output? 26 | parse(ip, varargin{:}); 27 | par = ip.Results; 28 | 29 | % if u0 is not given 30 | if par.u0 == 0 31 | par.u0 = []; 32 | end 33 | 34 | % right hand side 35 | if isempty(par.u0) 36 | b = A' * f; 37 | else 38 | b = A' * (f - A * par.u0); 39 | if ~isempty(par.init) 40 | par.init = par.init - par.u0; % update initial guess 41 | end 42 | end 43 | 44 | % switch between ordinary matrix and linear operator 45 | if isa(A, 'radonop') && A.useFBP 46 | if isempty(par.u0) 47 | w = minL2TikhonovFBP(f, gamma, A); 48 | else 49 | w = minL2TikhonovFBP(f - A * par.u0, gamma, A); 50 | end 51 | 52 | elseif isa(A, 'linop') % A is a linear operator (function handle of type 'linop') 53 | % set up system of normal equations 54 | bCol = b(:); 55 | M = @(x) reshape(A.normalOp(reshape(x, size(b))) + gamma * reshape(x, size(b)), size(bCol)); 56 | 57 | if ~isempty(A.lseSolver) 58 | solverHandle = str2func(A.lseSolver); 59 | [w,flag,relres,iter] = feval(solverHandle, M, bCol, par.tol, par.maxit, [], [], par.init(:)); 60 | 61 | else 62 | if A.posdef 63 | % if M is positive definite we can use cgs 64 | method = 'CG'; 65 | [w,flag,relres,iter] = pcg(M, bCol, par.tol, par.maxit, [], [], par.init(:)); 66 | else 67 | % M is symmetric, but not necessarily positive definite, therefore 68 | % minimization using minres 69 | method = 'MINRES'; 70 | [w,flag,relres,iter] = minres(M, bCol, par.tol, par.maxit, [], [], par.init(:)); 71 | end 72 | end 73 | 74 | % reshaping 75 | w = reshape(w, size(b)); 76 | 77 | % show output 78 | if par.verbose 79 | fprintf('Number of %s iterations %i; Rel. residual %f; Flag %i \n', method, iter, relres, flag); 80 | 81 | end 82 | 83 | elseif isa(A, 'convop') % operator of convolution type, solve using fft 84 | wHat = fftn(b) ./ (abs(A).^2 + gamma) ; 85 | w = ifftn(wHat); 86 | else % ordinary matrix (only for 1D) 87 | w = (A' * A + speye(size(par.mat)) * gamma) \ b; 88 | end 89 | 90 | % resubstitue 91 | if isempty(par.u0) 92 | u = w; 93 | else 94 | u = w + par.u0; 95 | end 96 | 97 | end 98 | 99 | -------------------------------------------------------------------------------- /Tikhonov/minL2TikhonovFBP.m: -------------------------------------------------------------------------------- 1 | function u = minL2TikhonovFBP(f, gamma, R, varargin) 2 | %minL2TikhonovFBP Solves the L^2 Tikhonov problem for R being the Radon transform 3 | % 4 | % Computes a minimizer of the L^2 Tikhonov functional 5 | % 6 | % \| R u - f \|_2^2 + \gamma \| u \|_2^2 7 | % 8 | % using a filtered backprojection formula 9 | % 10 | % See also: minL1Tikhonov, minL2iPotts, minL2iSpars, iPottsADMM 11 | 12 | % written by M. Storath 13 | % $Date: 2014-06-30 11:26:34 +0200 (Mo, 30. Jun 2014) $ $Revision: 99 $ 14 | 15 | 16 | 17 | 18 | d = 1; 19 | 20 | % Modified from iradon.m 21 | %--------------------------------------------------------------------- 22 | len = size(f,1); 23 | order = max(64,2^nextpow2(2*len)); 24 | n = 0:(order/2); % 'order' is always even. 25 | filtImpResp = zeros(1,(order/2)+1); % 'filtImpResp' is the bandlimited ramp's impulse response (values for even n are 0) 26 | filtImpResp(1) = 1/4; % Set the DC term 27 | filtImpResp(2:2:end) = -1./((pi*n(2:2:end)).^2); % Set the values for odd n 28 | filtImpResp = [filtImpResp filtImpResp(end-1:-1:2)]; 29 | filt = 2*real(fft(filtImpResp)); 30 | filt = filt(1:(order/2)+1); 31 | 32 | filtMod = filt./(1+gamma * filt); % Tikhonov reg. 33 | w = 2*pi*(0:size(filt,2)-1)/order; % frequency axis up to Nyquist 34 | filtMod(w>pi*d) = 0; 35 | filt = [filtMod' ; filtMod(end-1:-1:2)']; % Symmetry of the filter 36 | %---------------------------------------------------------------------- 37 | 38 | f(length(filt),1)=0; % Zero pad projections 39 | 40 | 41 | f = fft(f); % p holds fft of projections 42 | 43 | for i = 1:size(f,2) 44 | f(:,i) = f(:,i).*filt; % frequency domain filtering 45 | end 46 | 47 | f = real(ifft(f)); % p is the filtered projections 48 | f(len+1:end,:) = []; % Truncate the filtered projections 49 | 50 | 51 | 52 | % backprojection 53 | u = R' * f; 54 | 55 | 56 | end 57 | 58 | -------------------------------------------------------------------------------- /installPottslab.m: -------------------------------------------------------------------------------- 1 | % try to show license 2 | try 3 | disp('----------') 4 | lic = importdata('License.txt'); 5 | for i = 1:numel(lic) 6 | disp(lic{i}); 7 | end 8 | disp('----------') 9 | catch e 10 | end 11 | 12 | %%sets all necessary paths 13 | % set path 14 | disp('Setting Matlab path...'); 15 | folder = fileparts(which(mfilename)); 16 | addpath(... 17 | fullfile(folder, ''),... 18 | fullfile(folder, 'Auxiliary'),... 19 | fullfile(folder, 'Auxiliary/Operators'),... 20 | fullfile(folder, 'Data'),... 21 | fullfile(folder, 'Demos'),... 22 | fullfile(folder, 'Demos', '1D'),... 23 | fullfile(folder, 'Demos', '2D'),... 24 | fullfile(folder, 'Java', 'bin', 'pottslab'),... 25 | fullfile(folder, 'Potts'),... 26 | fullfile(folder, 'Potts', 'PottsCore'),... 27 | fullfile(folder, 'Potts2D'),... 28 | fullfile(folder, 'Potts2D', 'Core'),... 29 | fullfile(folder, 'Sparsity'),... 30 | fullfile(folder, 'Sparsity', 'SparsityCore'),... 31 | fullfile(folder, 'Tikhonov')... 32 | ); 33 | 34 | % set java path 35 | disp('Setting Java path...'); 36 | setPLJavaPath(true); 37 | 38 | % save pathdef 39 | savepath; 40 | -------------------------------------------------------------------------------- /pottslab-standalone.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mstorath/Pottslab/d4692b95a619a238cd2d7f777ac566463ad5965a/pottslab-standalone.jar --------------------------------------------------------------------------------