├── ChangeLog ├── Contents.m ├── LICENSE ├── README.md ├── adjustCorr1s.m ├── adjustCorr2s.m ├── checkerboard.pgm ├── example1.m ├── example2.m ├── expand.m ├── metal.pgm ├── modacor22.m ├── modkurt.m ├── modskew.m ├── nuts.pgm ├── reptil_skin.pgm ├── sawtooth.pgm ├── shrink.m ├── snr.m ├── text.pgm ├── textureAnalysis.m └── textureSynthesis.m /ChangeLog: -------------------------------------------------------------------------------- 1 | Log of changes made to textureSynth code 2 | (important changes marked with **) 3 | ----------------------------------------------------------------------- 4 | 2001-03-28 Eero (mobile) 5 | 6 | * Released Version 1.0 -------------------------------------------------------------------------------- /Contents.m: -------------------------------------------------------------------------------- 1 | % Tools for analysis and synthesis of texture images. 2 | % Version 1.0 March 2001. 3 | % 4 | % Created: Javier Portilla and Eero Simoncelli 5 | % javier@decsai.ugr.es / eero.simoncelli@nyu.edu 6 | % 7 | % See Readme.txt file for a brief description. 8 | % See ChangeLog file for latest modifications. 9 | % Type "help " for documentation on individual commands. 10 | % ----------------------------------------------------------------- 11 | % Demonstrations: 12 | % example1 - synthesis of "random text" 13 | % example2 - synthesis mixing real and synthetic data 14 | % 15 | % Primary entry points: 16 | % textureAnalysis - Extract a set of parameters for a texture image 17 | % textureSynthesis - Synthesize a new texture from a set of parameters. 18 | % 19 | % Utility functions: 20 | % snr - compute signal-to-noise ratio in dB 21 | % expand - resample at higher resolution (in Fourier domain) 22 | % shrink - resample at lower resolution (in Fourier domain) 23 | % 24 | % Functions that perform projection parameter constraint surfaces: 25 | % modacor22 - modify autocorrelation of an image 26 | % modkurt - modify kurtosis (4th moment divided by squared variance) 27 | % modskew - modify skewness (3rd moment divided by variance^1.5) 28 | % adjustCorr1s - modify cross-correlation 29 | % adjustCorr2s - modify cross-correlation, with some variables held fixed. 30 | % 31 | % Example texture images: 32 | % text.pgm (scanned by us) 33 | % nuts.pgm (from VisTex texture data base) 34 | % metal.pgm (VisTex) 35 | % reptil_skin.pgm (Brodatz) 36 | % checkerboard.pgm (artificial) 37 | % sawtooth.pgm (artificial) 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 LabForComputationalVision 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # textureSynth 2 | 3 | This package contains MatLab code for analyzing and synthesizing digital 4 | image of visual texture. The algorithm is described in the references 5 | given at the bottom of this document. Further information, as well the 6 | most recent versions of the code, are available at 7 | 8 | http://www.cns.nyu.edu/~lcv/texture/ 9 | 10 | Incremental changes to the code are documented in the `ChangeLog` file. 11 | 12 | Written by Javier Portilla and Eero Simoncelli, 1999-2000. 13 | 14 | Comments/Suggestions/Bugs to: 15 | - `javier@decsai.ugr.es` 16 | - `eero.simoncelli@nyu.edu` 17 | 18 | > [!TIP] 19 | > 20 | > An actively-maintained GPU-compatible python port of this model (using 21 | > pytorch) can be found in the 22 | > [plenoptic](https://github.com/LabForComputationalVision/plenoptic) package. 23 | > Note that the `plenoptic` port is not exactly identical: it makes use of 24 | > pytorch's built-in optimization, rather than the custom optimization found 25 | > here, and it returns only the statistics described in the paper (this 26 | > implementation includes some redundant statistics, see [plenoptic 27 | > docs](https://plenoptic.readthedocs.io/en/latest/tutorials/models/Metamer-Portilla-Simoncelli.html) 28 | > for more details). The results of texture synthesis are still qualitatively 29 | > similar. 30 | 31 | ## INSTALLATION 32 | 33 | 1) download and unpack the code. You can put the code anywhere on your 34 | system, but we'll assume it's in a directory named `textureSynth`. 35 | 36 | 2) download and unpack the [matlabPyrTools 37 | package](https://github.com/LabForComputationalVision/matlabPyrTools) This 38 | is a collection of tools for multi-scale decomposition of images. You can 39 | put the code anywhere on your system, but we'll assume it's in a directory 40 | named `matlabPyrTools`. Please use version 1.4 or newer of the 41 | matlabPyrTools. 42 | 43 | 3) Run matlab, and put the matlabPyrTools directory in your path: > 44 | path('matlabPyrTools', path); 45 | 46 | 4) The matlabPyrTools distribution includes a MEX subdirectory 47 | containing binary executables, precompiled for various platforms 48 | (SunOS,Solaris, Linux,Windows). You may need to recompile these on 49 | your platform. In addition, you should either move the relavent 50 | files from the MEX subdirectory into the main directory, OR create a 51 | link/alias to them, OR place the MEX subdirectory in your matlab 52 | path. 53 | 54 | ## USING THE SOFTWARE 55 | 56 | - To see a demonstration, start Matlab, change directories (using 57 | `cd`) into the textureSynth directory, and execute `example1`. 58 | 59 | - If you want to learn how to use the texture analysis and synthesis 60 | functions, take a look at example1.m and example2.m (these include 61 | many explanatory comments). 62 | 63 | - For a listing of matlab function included in this package, execute `help 64 | textureSynth` 65 | 66 | - For details on any of the functions, execute `help ` 67 | 68 | ## REFERENCES 69 | 70 | J Portilla and E P Simoncelli. A Parametric Texture Model based on Joint 71 | Statistics of Complex Wavelet Coefficients. Int'l Journal of Computer 72 | Vision. 40(1):49-71, October, 2000. 73 | http://www.cns.nyu.edu/~eero/ABSTRACTS/portilla99-abstract.html 74 | 75 | J Portilla and E P Simoncelli Texture Modeling and Synthesis using Joint 76 | Statistics of Complex Wavelet Coefficients. IEEE Workshop on Statistical 77 | and Computational Theories of Vision, Fort Collins, CO, 22 June 1999. 78 | http://www.cns.nyu.edu/~eero/ABSTRACTS/portilla99a-abstract.html 79 | 80 | J Portilla and E P Simoncelli. Texture Representation and Synthesis 81 | Using Correlation of Complex Wavelet Coefficient Magnitudes. Technical 82 | Report #54, Consejo Superior de Investigaciones Cientificas (CSIC), 83 | Madrid. 29 March 1999. 84 | http://www.cns.nyu.edu/~eero/ABSTRACTS/portilla98-abstract.html 85 | 86 | E P Simoncelli and J Portilla. Texture Characterization via Joint 87 | Statistics of Wavelet Coefficient Magnitudes. In 5th IEEE Int'l Conf on 88 | Image Processing. Chicago, IL. Oct 4-7, 1998. 89 | http://www.cns.nyu.edu/~eero/ABSTRACTS/simoncelli98b-abstract.html 90 | -------------------------------------------------------------------------------- /adjustCorr1s.m: -------------------------------------------------------------------------------- 1 | % [newX, snr1, M] = adjustCorr1s(X, Cx, MODE, p) 2 | % 3 | % Linearly adjust variables in X to have correlation Cx. 4 | % Rows of X and newX are samples of a (random) row-vector, such that: 5 | % 1: newX = X * M 6 | % 2: newX' * newX = Cx 7 | % 8 | % MODE is optional: 9 | % 0 => choose randomly from the space of linear solutions 10 | % 1 => simplest soln 11 | % 2 => minimize angle change (DEFAULT) 12 | % 3 => SVD minimal vector change soln 13 | % 14 | % p is optional: 15 | % Imposes an intermediate value of correlation between the current one 16 | % C and Cx: 17 | % Cx' = (1-p)*C + p*Cx; 18 | % DEFAULT is p=1. 19 | 20 | % EPS, 11/23/97. 21 | 22 | function [newX, snr1, M] = adjustCorr1s(X,Co,mode,p) 23 | 24 | if (exist('mode') ~= 1) 25 | mode = 2; 26 | end 27 | 28 | if (exist('p') ~= 1) 29 | p = 1; 30 | end 31 | 32 | C = innerProd(X) / size(X,1); 33 | [E, D] = eig(C); 34 | D = diag(D); 35 | [junk,Ind] = sort(D); 36 | D = diag(sqrt(D(Ind(size(Ind,1):-1:1)))); 37 | E = E(:,Ind(size(Ind,1):-1:1)); 38 | 39 | Co0 = Co; 40 | Co = (1-p)*C + p*Co; 41 | 42 | [Eo,Do] = eig(Co); 43 | Do = diag(Do); 44 | [junk,Ind] = sort(Do); 45 | Do = diag(sqrt(Do(Ind(size(Ind,1):-1:1)))); 46 | Eo = Eo(:,Ind(size(Ind,1):-1:1)); 47 | 48 | if (mode == 0) 49 | Orth = orth(rand(size(C))); 50 | elseif (mode == 1) % eye 51 | Orth = eye(size(C)); 52 | elseif (mode == 2) % simple 53 | Orth = E' * Eo; 54 | else % SVD 55 | [U,S,V] = svd(D * E' * Eo * inv(Do)); 56 | Orth = U * V'; 57 | end 58 | 59 | M = E * inv(D) * Orth * Do * Eo'; 60 | 61 | newX = X * M; 62 | 63 | snr1=10*log10(sum(sum(Co0.^2))/sum(sum((Co0-C).^2))); 64 | -------------------------------------------------------------------------------- /adjustCorr2s.m: -------------------------------------------------------------------------------- 1 | % [newX, snr1, snr2, Mx, My] = adjustCorr2s(X, Cx, Y, Cxy, MODE, p) 2 | % 3 | % Linearly adjust variables in X to have correlation Cx, and cross-correlation Cxy. 4 | % Rows of X, Y, and newX are samples of (random) row-vectors, such that: 5 | % 1: newX = X * Mx + Y * My 6 | % 2: newX' * newX = Cx 7 | % 3: newX' * Y = Cxy 8 | % 9 | % MODE is optional: 10 | % 0 => choose randomly from the space of linear solutions 11 | % 1 => simplest soln 12 | % 2 => minimize angle change 13 | % 3 => Simple rotational (DEFAULT) 14 | % 4 => SVD minimal vector change soln 15 | % 16 | % p is optional: 17 | % Imposes an intermediate value of correlation between the current ones 18 | % Bx and Bxy and the specified Cx and Cxy: 19 | % Cx' = (1-p)*Bx + p*Cx; 20 | % Cxy' = (1-p)*Bxy + p*Cxy; 21 | % DEFAULT is p=1. 22 | 23 | 24 | % EPS, 11/25/97 25 | 26 | function [newX,snr1,snr2,Mx,My] = adjustCorr2s(X, Cx, Y, Cxy, mode, p) 27 | 28 | Warn = 0; % Set to 1 if you want to display warning messages 29 | if (exist('mode') ~= 1) 30 | mode = 3; 31 | end 32 | if (exist('p') ~= 1) 33 | p = 1; 34 | end 35 | 36 | Bx = innerProd(X) / size(X,1); 37 | Bxy = (X' * Y) / size(X,1); 38 | By = innerProd(Y) / size(X,1); 39 | iBy = inv(By); 40 | 41 | Current = Bx - (Bxy * iBy * Bxy'); 42 | Cx0 = Cx; 43 | Cx = (1-p)*Bx + p*Cx; 44 | Cxy0 = Cxy; 45 | Cxy = (1-p)*Bxy + p*Cxy; 46 | Desired = Cx - (Cxy * iBy * Cxy'); 47 | 48 | [E, D] = eig(Current); 49 | D = diag(D); 50 | if any(D < 0) & Warn 51 | ind = find(D<0); 52 | fprintf(1,'Warning: negative current eigenvalues: %d\n',D(ind)'); 53 | end 54 | [junk,Ind] = sort(D); 55 | D = diag(sqrt(D(Ind(size(Ind,1):-1:1)))); 56 | E = E(:,Ind(size(Ind,1):-1:1)); 57 | 58 | [Eo,Do] = eig(Desired); 59 | Do = diag(Do); 60 | if any(Do < 0) & Warn 61 | ind = find(Do<0); 62 | fprintf(1,'Warning: negative desired eigenvalues: %d\n',Do(ind)'); 63 | end 64 | [junk,Ind] = sort(Do); 65 | Do = diag(sqrt(Do(Ind(size(Ind,1):-1:1)))); 66 | Eo = Eo(:,Ind(size(Ind,1):-1:1)); 67 | 68 | if (mode == 0) 69 | Orth = orth(rand(size(D))); 70 | elseif (mode == 1) % eye 71 | Orth = eye(size(D)); 72 | elseif (mode == 2) % simple 73 | A = [ eye(size(Cx)); -iBy*Bxy' ]; 74 | Ao = [ eye(size(Cx)); -iBy*Cxy' ]; 75 | [U,S,V] = svd(E' * pinv(A) * Ao * Eo); 76 | Orth = U * V'; 77 | elseif (mode == 3) 78 | Orth = E' * Eo; 79 | else % SVD 80 | A = [ eye(size(Cx)); -iBy*Bxy' ]; 81 | Ao = [ eye(size(Cx)); -iBy*Cxy' ]; 82 | [U,S,V] = svd(D * E' * pinv(A) * Ao * Eo * inv(Do)); 83 | Orth = U * V'; 84 | end 85 | 86 | Mx = E * inv(D) * Orth * Do * Eo'; 87 | My = iBy * (Cxy' - Bxy' * Mx); 88 | newX = X * Mx + Y * My; 89 | 90 | if Cx0~=Bx, 91 | snr1=10*log10(sum(sum(Cx0.^2))/sum(sum((Cx0-Bx).^2))); 92 | else 93 | snr1 = Inf; 94 | end 95 | if Cxy0~=Bxy, 96 | snr2=10*log10(sum(sum(Cxy0.^2))/sum(sum((Cxy0-Bxy).^2))); 97 | else 98 | snr2 = Inf; 99 | end 100 | -------------------------------------------------------------------------------- /checkerboard.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LabForComputationalVision/textureSynth/65d6b59c88f746acbc4959895721a59cc4eff7a6/checkerboard.pgm -------------------------------------------------------------------------------- /example1.m: -------------------------------------------------------------------------------- 1 | % Example 1: Synthesis of a "text" texture image, using 2 | % Portilla-Simoncelli texture analysis/synthesis code, based on 3 | % alternate projections onto statistical constraints in a complex 4 | % overcomplete wavelet representation. 5 | % 6 | % See Readme.txt, and headers of textureAnalysis.m and 7 | % textureSynthesis.m for more details. 8 | % 9 | % Javier Portilla (javier@decsai.ugr.es). March, 2001 10 | 11 | close all 12 | 13 | im0 = pgmRead('text.pgm'); % im0 is a double float matrix! 14 | 15 | Nsc = 4; % Number of scales 16 | Nor = 4; % Number of orientations 17 | Na = 9; % Spatial neighborhood is Na x Na coefficients 18 | % It must be an odd number! 19 | 20 | params = textureAnalysis(im0, Nsc, Nor, Na); 21 | 22 | Niter = 25; % Number of iterations of synthesis loop 23 | Nsx = 192; % Size of synthetic image is Nsy x Nsx 24 | Nsy = 128; % WARNING: Both dimensions must be multiple of 2^(Nsc+2) 25 | 26 | res = textureSynthesis(params, [Nsy Nsx], Niter); 27 | 28 | close all 29 | figure(1) 30 | showIm(im0, 'auto', 1, 'Original texture'); 31 | figure(2) 32 | showIm(res, 'auto', 1, 'Synthesized texture'); 33 | 34 | % Can you read the NEW text? ;-) 35 | -------------------------------------------------------------------------------- /example2.m: -------------------------------------------------------------------------------- 1 | % Example 2: Seamless blending of real and synthetic texture in an 2 | % image, using Portilla-Simoncelli texture analysis/synthesis code, 3 | % based on alternate projections onto statistical constraints in a 4 | % complex overcomplete wavelet representation. 5 | % 6 | % See Readme.txt, and headers of textureAnalysis.m and 7 | % textureSynthesis.m for more details. 8 | % 9 | % Javier Portilla (javier@decsai.ugr.es). March, 2001 10 | 11 | close all 12 | 13 | Nsc = 4; % Number of scales 14 | Nor = 4; % Number of orientations 15 | Na = 5; % Spatial neighborhood is Na x Na coefficients 16 | % It must be an odd number! 17 | 18 | im0 = pgmRead('nuts.pgm'); % Warning: im0 is a double float matrix! 19 | 20 | params = textureAnalysis(im0, Nsc, Nor, Na); 21 | 22 | Niter = 25; % Number of iterations of synthesis loop 23 | Nsx = 192; % Size of synthetic image is Nsy x Nsx 24 | Nsy = 192; % Warning: both dimensions must be multiple of 2^(Nsc+2) 25 | 26 | % Use a mask and the original image to synthesize an image with the 27 | % left side synthetic and the right side real data. 28 | % The effective mask is M = (mask>0), its smoothness is for avoiding 29 | % border effects. 30 | ramp = meshgrid(1:Nsx/4,1:Nsy)*4/Nsy; 31 | mask = [zeros(Nsy,Nsx/2) ramp ramp(:,Nsx/4:-1:1)]; 32 | mask = 1/2*(1-cos(pi*mask)); 33 | 34 | showIm(double(mask>0), 'auto', 'auto', 'Mask'); 35 | display('Press any key to continue');pause 36 | 37 | imKeep = zeros(Nsx*Nsy,2); 38 | imKeep(:,1) = reshape(mask, [Nsy*Nsx,1]); 39 | imKeep(:,2) = reshape(im0(1:Nsy,1:Nsx), [Nsy*Nsx,1]); % Original 40 | 41 | res = textureSynthesis(params, [Nsy Nsx], Niter,[],imKeep); 42 | 43 | close all 44 | figure(1);showIm(double(mask>0), 'auto', 'auto', 'Mask'); 45 | figure(2);showIm(im0, 'auto', 'auto', 'Original Texture'); 46 | figure(3);showIm(res, 'auto', 'auto', 'Blended Original and Synthetic Texture'); 47 | -------------------------------------------------------------------------------- /expand.m: -------------------------------------------------------------------------------- 1 | function te = expand(t,f) 2 | 3 | % Expand spatially an image t in a factor f 4 | % in X and in Y. 5 | % t may be complex. 6 | % It fills in with zeros in the Fourier domain. 7 | % te = expand(t, f) 8 | % See also: shrink.m 9 | % JPM, May 95, Instituto de Optica, CSIC, Madrid. 10 | 11 | [my mx]=size(t); 12 | my=f*my; 13 | mx=f*mx; 14 | Te=zeros(my,mx); 15 | T=f^2*fftshift(fft2(t)); 16 | y1=my/2+2-my/(2*f); 17 | y2=my/2+my/(2*f); 18 | x1=mx/2+2-mx/(2*f); 19 | x2=mx/2+mx/(2*f); 20 | Te(y1:y2,x1:x2)=T(2:my/f,2:mx/f); 21 | Te(y1-1,x1:x2)=T(1,2:mx/f)/2; 22 | Te(y2+1,x1:x2)=((T(1,mx/f:-1:2)/2)').'; 23 | Te(y1:y2,x1-1)=T(2:my/f,1)/2; 24 | Te(y1:y2,x2+1)=((T(my/f:-1:2,1)/2)').'; 25 | esq=T(1,1)/4; 26 | Te(y1-1,x1-1)=esq; 27 | Te(y1-1,x2+1)=esq; 28 | Te(y2+1,x1-1)=esq; 29 | Te(y2+1,x2+1)=esq; 30 | Te=fftshift(Te); 31 | te=ifft2(Te); 32 | if all(imag(t)==0), 33 | te = real(te); 34 | end 35 | -------------------------------------------------------------------------------- /metal.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LabForComputationalVision/textureSynth/65d6b59c88f746acbc4959895721a59cc4eff7a6/metal.pgm -------------------------------------------------------------------------------- /modacor22.m: -------------------------------------------------------------------------------- 1 | function [Y,snrV,Chf]=modacor22(X,Cy,p); 2 | 3 | % It imposes the desired autocorrelation in the given (central) samples (Cy) to 4 | % an image X, convolving it with an even filter of size(Cy), in such a way 5 | % that the image containts change as less as possible, in a LSE sense. 6 | % [Y,snr,Chf]=modacor22(X,Cy,p); 7 | % Chf: Fourier transform of the filter that forces the autocorrelation 8 | % p [OPTIONAL]: mixing proportion between Cx and Cy 9 | % it imposes (1-p)*Cx + p*Cy, 10 | % being Cx the actual autocorrelation. 11 | % DEFAULT: p = 1; 12 | 13 | % JPM, 10/97, working with EPS, NYU 14 | 15 | Warn = 0; % Set to 1 if you want to see warning messages 16 | if (exist('p') ~= 1) 17 | p = 1; 18 | end 19 | 20 | % Compute the autocorrelation function of the original image 21 | 22 | [Ny,Nx]=size(X); 23 | Nc=size(Cy,1); % Normally Nc< Nx) & Warn 25 | warning('Autocorrelation neighborhood too large for image: reducing'); 26 | Nc = 2*floor(Nx/4)-1; 27 | first = (size(Cy,1)-Nc)/2; 28 | Cy = Cy(first+1:first+Nc, first+1:first+Nc); 29 | end 30 | 31 | Xf=fft2(X); 32 | Xf2=abs(Xf).^2; 33 | Cx=fftshift(real(ifft2(Xf2)))/(2-isreal(X)); 34 | Cy=Cy*prod(size(X)); % Unnormalize the previously normalized correlation 35 | 36 | cy=Ny/2+1; 37 | cx=Nx/2+1; 38 | Lc=(Nc-1)/2; 39 | Cy0 = Cy; 40 | Cy = p*Cy + (1-p)*Cx(cy-Lc:cy+Lc,cx-Lc:cx+Lc); 41 | 42 | % Compare the actual correlation with the desired one 43 | %imStats(Cx(cy-Lc:cy+Lc,cx-Lc:cx+Lc),Cy) 44 | snrV=10*log10(sum(sum(Cy0.^2))/sum(sum((Cy0-Cx(cy-Lc:cy+Lc,cx-Lc:cx+Lc)).^2))); 45 | 46 | % Take just the part that has influence on the samples of Cy (Cy=conv(Cx,Ch)) 47 | Cx=Cx(cy-2*Lc:cy+2*Lc,cx-2*Lc:cx+2*Lc); 48 | 49 | % Build the matrix that performs the convolution Cy1=Tcx*Ch1 50 | 51 | Ncx=4*Lc+1; 52 | M=(Nc^2+1)/2; 53 | Tcx=zeros(M); 54 | 55 | for i=Lc+1:2*Lc, 56 | for j=Lc+1:3*Lc+1, 57 | nm=(i-Lc-1)*(2*Lc+1)+j-Lc; 58 | ccx=Cx(i-Lc:i+Lc,j-Lc:j+Lc); 59 | ccxi=ccx(2*Lc+1:-1:1,2*Lc+1:-1:1); 60 | ccx=ccx+ccxi; 61 | ccx(Lc+1,Lc+1)=ccx(Lc+1,Lc+1)/2; 62 | ccx=vectify(ccx'); 63 | Tcx(nm,:)=ccx(1:M)'; 64 | end 65 | end 66 | i=2*Lc+1; 67 | for j=Lc+1:2*Lc+1, 68 | nm=(i-Lc-1)*(2*Lc+1)+j-Lc; 69 | ccx=Cx(i-Lc:i+Lc,j-Lc:j+Lc); 70 | ccxi=ccx(2*Lc+1:-1:1,2*Lc+1:-1:1); 71 | ccx=ccx+ccxi; 72 | ccx(Lc+1,Lc+1)=ccx(Lc+1,Lc+1)/2; 73 | ccx=vectify(ccx'); 74 | Tcx(nm,:)=ccx(1:M)'; 75 | end 76 | 77 | % Rearrange Cy indices and solve the equation 78 | 79 | Cy1=vectify(Cy'); 80 | Cy1=Cy1(1:M); 81 | 82 | Ch1=inv(Tcx)*Cy1; 83 | 84 | % Rearrange Ch1 85 | 86 | Ch1=[Ch1;Ch1(length(Cy1)-1:-1:1)]; 87 | Ch=reshape(Ch1,Nc,Nc)'; 88 | 89 | % Compute H from Ch (H is zero-phase) through the DFT 90 | 91 | %s=2^(ceil(log(Nc)/log(2))+1); 92 | %H=sqrt(abs(fft2(Ch,s,s))); 93 | %h=fftshift(real(ifft2(H))); 94 | %h=h(s/2+1-Lc:s/2+1+Lc,s/2+1-Lc:s/2+1+Lc); 95 | %%plot(Ch);drawnow 96 | %h=recphase(Ch); 97 | 98 | % Compute Y as conv(X,H) in the Fourier domain 99 | 100 | %%Y=real(ifft2(Xf.*H)); 101 | %Y=real(ifft2(Xf.*sqrt(abs(fft2(Ch,Ny,Nx))))); 102 | aux=zeros(Ny,Nx); 103 | aux(cy-Lc:cy+Lc,cx-Lc:cx+Lc)=Ch; 104 | Ch=fftshift(aux); 105 | Chf=real(fft2(Ch)); 106 | %Chf=fft2(Ch,Ny,Nx); 107 | %figure(7);plot(Chf);drawnow; 108 | Yf=Xf.*sqrt(abs(Chf)); 109 | Y=ifft2(Yf); 110 | %Y=cconv2(X,h); 111 | 112 | % Checks the fidelity of the imposition 113 | 114 | %Cy2=fftshift(real(ifft2(Xf2.*abs(Chf))))/(2-isreal(X)); 115 | %Cy2=Cy2(cy-Lc:cy+Lc,cx-Lc:cx+Lc); 116 | %imStats(Cy,Cy2) 117 | %imStats(X,Y) 118 | 119 | %Yf2=abs(Yf).^2; 120 | %Cy3=fftshift(real(ifft2(Yf2)))/2; 121 | 122 | %Cy3=Cy3(cy-Lc:cy+Lc,cx-Lc:cx+Lc); 123 | %snr(Cy,Cy-Cy3) 124 | %imStats(Cy,Cy3) 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /modkurt.m: -------------------------------------------------------------------------------- 1 | function [chm, snrk] = modkurt(ch,k,p); 2 | 3 | % Modify the kurtosis in one step, by moving in gradient direction until 4 | % reaching the desired kurtosis value. 5 | % It does not affect the mean nor the variance, but it affects the skewness. 6 | % This operation is not an orthogonal projection, but the projection angle is 7 | % near pi/2 when k is close to the original kurtosis, which is a realistic assumption 8 | % when doing iterative projections in a pyramid, for example (small corrections 9 | % to the channels' statistics). 10 | % 11 | % [chm, snrk] = modkurt(ch,k,p); 12 | % ch: channel 13 | % k: desired kurtosis (k=M4/M2^2) 14 | % p [OPTIONAL]: mixing proportion between k0 and k 15 | % it imposes (1-p)*k0 + p*k, 16 | % being k0 the current kurtosis. 17 | % DEFAULT: p = 1; 18 | 19 | 20 | % Javier Portilla, Oct.12/97, NYU 21 | 22 | Warn = 0; % Set to 1 if you want to see warning messages 23 | if ~exist('p'), 24 | p = 1; 25 | end 26 | 27 | me=mean2(ch); 28 | ch=ch-me; 29 | 30 | % Compute the moments 31 | 32 | m=zeros(12,1); 33 | for n=2:12, 34 | m(n)=mean2(ch.^n); 35 | end 36 | 37 | % The original kurtosis 38 | 39 | k0=m(4)/m(2)^2; 40 | snrk = snr(k, k-k0); 41 | if snrk > 60, 42 | chm = ch+me; 43 | return 44 | end 45 | k = k0*(1-p) + k*p; 46 | 47 | % Some auxiliar variables 48 | 49 | a=m(4)/m(2); 50 | 51 | % Coeficients of the numerator (A*lam^4+B*lam^3+C*lam^2+D*lam+E) 52 | 53 | A=m(12)-4*a*m(10)-4*m(3)*m(9)+6*a^2*m(8)+12*a*m(3)*m(7)+6*m(3)^2*m(6)-... 54 | 4*a^3*m(6)-12*a^2*m(3)*m(5)+a^4*m(4)-12*a*m(3)^2*m(4)+... 55 | 4*a^3*m(3)^2+6*a^2*m(3)^2*m(2)-3*m(3)^4; 56 | B=4*(m(10)-3*a*m(8)-3*m(3)*m(7)+3*a^2*m(6)+6*a*m(3)*m(5)+3*m(3)^2*m(4)-... 57 | a^3*m(4)-3*a^2*m(3)^2-3*m(4)*m(3)^2); 58 | C=6*(m(8)-2*a*m(6)-2*m(3)*m(5)+a^2*m(4)+2*a*m(3)^2+m(3)^2*m(2)); 59 | D=4*(m(6)-a^2*m(2)-m(3)^2); 60 | E=m(4); 61 | 62 | % Define the coefficients of the denominator (F*lam^2+G)^2 63 | 64 | F=D/4; 65 | G=m(2); 66 | 67 | % test 68 | test = 0; 69 | 70 | if test, 71 | 72 | grd = ch.^3 - a*ch - m(3); 73 | lam = -0.001:0.00001:0.001; 74 | k = (A*lam.^4+B*lam.^3+C*lam.^2+D*lam+E)./... 75 | (F*lam.^2 + G).^2; 76 | for lam = -0.001:0.00001:0.001, 77 | n = lam*100000+101; 78 | chp = ch + lam*grd; 79 | k2(n) = mean2(chp.^4)/mean2(chp.^2)^2; 80 | %k2(n) = mean2(chp.^4); 81 | end 82 | lam = -0.001:0.00001:0.001; 83 | snr(k2, k-k2) 84 | 85 | end % test 86 | 87 | % Now I compute its derivative with respect to lambda 88 | % (only the roots of derivative = 0 ) 89 | 90 | d(1) = B*F; 91 | d(2) = 2*C*F - 4*A*G; 92 | d(3) = 4*F*D -3*B*G - D*F; 93 | d(4) = 4*F*E - 2*C*G; 94 | d(5) = -D*G; 95 | 96 | mMlambda = roots(d); 97 | 98 | tg = imag(mMlambda)./real(mMlambda); 99 | mMlambda = mMlambda(find(abs(tg)<1e-6)); 100 | lNeg = mMlambda(find(mMlambda<0)); 101 | if length(lNeg)==0, 102 | lNeg = -1/eps; 103 | end 104 | lPos = mMlambda(find(mMlambda>=0)); 105 | if length(lPos)==0, 106 | lPos = 1/eps; 107 | end 108 | lmi = max(lNeg); 109 | lma = min(lPos); 110 | 111 | lam = [lmi lma]; 112 | mMnewKt = polyval([A B C D E],lam)./(polyval([F 0 G],lam)).^2; 113 | kmin = min(mMnewKt); 114 | kmax = max(mMnewKt); 115 | 116 | % Given a desired kurtosis, solves for lambda 117 | 118 | if k<=kmin & Warn, 119 | lam = lmi; 120 | warning('Saturating (down) kurtosis!'); 121 | kmin 122 | elseif k>=kmax & Warn, 123 | lam = lma; 124 | warning('Saturating (up) kurtosis!'); 125 | kmax 126 | else 127 | 128 | % Coeficients of the algebraic equation 129 | 130 | c0 = E - k*G^2; 131 | c1 = D; 132 | c2 = C - 2*k*F*G; 133 | c3 = B; 134 | c4 = A - k*F^2; 135 | 136 | % Solves the equation 137 | 138 | r=roots([c4 c3 c2 c1 c0]); 139 | 140 | % Chose the real solution with minimum absolute value with the rigth sign 141 | 142 | tg = imag(r)./real(r); 143 | %lambda = real(r(find(abs(tg)<1e-6))); 144 | lambda = real(r(find(abs(tg)==0))); 145 | if length(lambda)>0, 146 | lam = lambda(find(abs(lambda)==min(abs(lambda)))); 147 | lam = lam(1); 148 | else 149 | lam = 0; 150 | end 151 | 152 | end % if ... else 153 | 154 | 155 | % Modify the channel 156 | 157 | chm=ch+lam*(ch.^3-a*ch-m(3)); % adjust the kurtosis 158 | chm=chm*sqrt(m(2)/mean2(chm.^2)); % adjust the variance 159 | chm=chm+me; % adjust the mean 160 | 161 | % Check the result 162 | %k2=mean2((chm-me).^4)/(mean2((chm-me).^2))^2; 163 | %SNR=snr(k,k-k2) 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | -------------------------------------------------------------------------------- /modskew.m: -------------------------------------------------------------------------------- 1 | function [chm, snrk] = modskew(ch,sk,p); 2 | 3 | % Adjust the sample skewness of a vector/matrix, using gradient projection, 4 | % without affecting its sample mean and variance. 5 | % 6 | % This operation is not an orthogonal projection, but the projection angle is 7 | % near pi/2 when sk is close to the original skewness, which is a realistic 8 | % assumption when doing iterative projections in a pyramid, for example 9 | % (small corrections to the channels' statistics). 10 | % 11 | % [xm, snrk] = modskew(x,sk,p); 12 | % sk: new skweness 13 | % p [OPTIONAL]: mixing proportion between sk0 and sk 14 | % it imposes (1-p)*sk0 + p*sk, 15 | % being sk0 the current skewness. 16 | % DEFAULT: p = 1; 17 | 18 | % 19 | % JPM. 2/98, IODV, CSIC 20 | % 4/00, CNS, NYU 21 | 22 | Warn = 0; % Set to 1 if you want to see warning messages 23 | if ~exist('p'), 24 | p = 1; 25 | end 26 | 27 | N=prod(size(ch)); % number of samples 28 | me=mean2(ch); 29 | ch=ch-me; 30 | 31 | for n=2:6, 32 | m(n)=mean2(ch.^n); 33 | end 34 | 35 | sd=sqrt(m(2)); % standard deviation 36 | s=m(3)/sd^3; % original skewness 37 | snrk = snr(sk, sk-s); 38 | sk = s*(1-p) + sk*p; 39 | 40 | % Define the coefficients of the numerator (A*lam^3+B*lam^2+C*lam+D) 41 | 42 | A=m(6)-3*sd*s*m(5)+3*sd^2*(s^2-1)*m(4)+sd^6*(2+3*s^2-s^4); 43 | B=3*(m(5)-2*sd*s*m(4)+sd^5*s^3); 44 | C=3*(m(4)-sd^4*(1+s^2)); 45 | D=s*sd^3; 46 | 47 | a(7)=A^2; 48 | a(6)=2*A*B; 49 | a(5)=B^2+2*A*C; 50 | a(4)=2*(A*D+B*C); 51 | a(3)=C^2+2*B*D; 52 | a(2)=2*C*D; 53 | a(1)=D^2; 54 | 55 | % Define the coefficients of the denominator (A2+B2*lam^2) 56 | 57 | A2=sd^2; 58 | B2=m(4)-(1+s^2)*sd^4; 59 | 60 | b=zeros(1,7); 61 | b(7)=B2^3; 62 | b(5)=3*A2*B2^2; 63 | b(3)=3*A2^2*B2; 64 | b(1)=A2^3; 65 | 66 | 67 | if 0, % test 68 | 69 | lam = -2:0.02:2; 70 | S = (A*lam.^3+B*lam.^2+C*lam+D)./... 71 | sqrt(b(7)*lam.^6 + b(5)*lam.^4 + b(3)*lam.^2 + b(1)); 72 | % grd = ch.^2 - m(2) - sd * s * ch; 73 | % for lam = -1:0.01:1, 74 | % n = lam*100+101; 75 | % chp = ch + lam*grd; 76 | % S2(n) = mean2(chp.^3)/abs(mean2(chp.^2))^(1.5); 77 | % end 78 | lam = -2:0.02:2; 79 | figure(1);plot(lam,S);grid;drawnow 80 | % snr(S2, S-S2) 81 | 82 | end % test 83 | 84 | % Now I compute its derivative with respect to lambda 85 | 86 | d(8) = B*b(7); 87 | d(7) = 2*C*b(7) - A*b(5); 88 | d(6) = 3*D*b(7); 89 | d(5) = C*b(5) - 2*A*b(3); 90 | d(4) = 2*D*b(5) - B*b(3); 91 | d(3) = -3*A*b(1); 92 | d(2) = D*b(3) - 2*B*b(1); 93 | d(1) = -C*b(1); 94 | 95 | d = d(8:-1:1); 96 | mMlambda = roots(d); 97 | 98 | tg = imag(mMlambda)./real(mMlambda); 99 | mMlambda = real(mMlambda(find(abs(tg)<1e-6))); 100 | lNeg = mMlambda(find(mMlambda<0)); 101 | if length(lNeg)==0, 102 | lNeg = -1/eps; 103 | end 104 | lPos = mMlambda(find(mMlambda>=0)); 105 | if length(lPos)==0, 106 | lPos = 1/eps; 107 | end 108 | lmi = max(lNeg); 109 | lma = min(lPos); 110 | 111 | lam = [lmi lma]; 112 | mMnewSt = polyval([A B C D],lam)./(polyval(b(7:-1:1),lam)).^0.5; 113 | skmin = min(mMnewSt); 114 | skmax = max(mMnewSt); 115 | 116 | 117 | % Given a desired skewness, solves for lambda 118 | 119 | if sk<=skmin & Warn, 120 | lam = lmi; 121 | warning('Saturating (down) skewness!'); 122 | skmin 123 | elseif sk>=skmax & Warn, 124 | lam = lma; 125 | warning('Saturating (up) skewness!'); 126 | skmax 127 | else 128 | 129 | 130 | % The equation is sum(c.*lam.^(0:6))=0 131 | 132 | c=a-b*sk^2; 133 | 134 | c=c(7:-1:1); 135 | 136 | r=roots(c); 137 | 138 | % Chose the real solution with minimum absolute value with the rigth sign 139 | lam=-Inf; 140 | co=0; 141 | for n=1:6, 142 | tg = imag(r(n))/real(r(n)); 143 | if (abs(tg)<1e-6)&(sign(real(r(n)))==sign(sk-s)), 144 | co=co+1; 145 | lam(co)=real(r(n)); 146 | end 147 | end 148 | if min(abs(lam))==Inf & Warn, 149 | display('Warning: Skew adjustment skipped!'); 150 | lam=0; 151 | end 152 | 153 | p=[A B C D]; 154 | 155 | if length(lam)>1, 156 | foo=sign(polyval(p,lam)); 157 | if any(foo==0), 158 | lam = lam(find(foo==0)); 159 | else 160 | lam = lam(find(foo==sign(sk))); % rejects the symmetric solution 161 | end 162 | if length(lam)>0, 163 | lam=lam(find(abs(lam)==min(abs(lam)))); % the smallest that fix the skew 164 | lam=lam(1); 165 | else 166 | lam = 0; 167 | end 168 | end 169 | end % if else 170 | 171 | % Modify the channel 172 | chm=ch+lam*(ch.^2-sd^2-sd*s*ch); % adjust the skewness 173 | chm=chm*sqrt(m(2)/mean2(chm.^2)); % adjust the variance 174 | chm=chm+me; % adjust the mean 175 | % (These don't affect the skewness) 176 | % Check the result 177 | %mem=mean2(chm); 178 | %sk2=mean2((chm-mem).^3)/mean2((chm-mem).^2).^(3/2); 179 | %sk - sk2 180 | %SNR=snr(sk,sk-sk2) 181 | 182 | 183 | 184 | -------------------------------------------------------------------------------- /nuts.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LabForComputationalVision/textureSynth/65d6b59c88f746acbc4959895721a59cc4eff7a6/nuts.pgm -------------------------------------------------------------------------------- /reptil_skin.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LabForComputationalVision/textureSynth/65d6b59c88f746acbc4959895721a59cc4eff7a6/reptil_skin.pgm -------------------------------------------------------------------------------- /sawtooth.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LabForComputationalVision/textureSynth/65d6b59c88f746acbc4959895721a59cc4eff7a6/sawtooth.pgm -------------------------------------------------------------------------------- /shrink.m: -------------------------------------------------------------------------------- 1 | function ts=shrink(t,f) 2 | 3 | % It shrinks an image in a factor f 4 | % in each dimension. 5 | % ts = shrink(t,f) 6 | % ts may also be complex. 7 | % See also: expand.m, blurDn.m 8 | % JPM, April 95, Instituto de Optica, CSIC, Madrid. 9 | 10 | [my,mx]=size(t); 11 | T=fftshift(fft2(t))/f^2; 12 | Ts=zeros(my/f,mx/f); 13 | y1=my/2+2-my/(2*f); 14 | y2=my/2+my/(2*f); 15 | x1=mx/2+2-mx/(2*f); 16 | x2=mx/2+mx/(2*f); 17 | Ts(2:my/f,2:mx/f)=T(y1:y2,x1:x2); 18 | Ts(1,2:mx/f)=(T(y1-1,x1:x2)+T(y2+1,x1:x2))/2; 19 | Ts(2:my/f,1)=(T(y1:y2,x1-1)+T(y1:y2,x2+1))/2; 20 | Ts(1,1)=(T(y1-1,x1-1)+T(y1-1,x2+1)+T(y2+1,x1-1)+T(y2+1,x2+1))/4; 21 | Ts=fftshift(Ts); 22 | ts=ifft2(Ts); 23 | if all(imag(t)==0), 24 | ts = real(ts); 25 | end 26 | -------------------------------------------------------------------------------- /snr.m: -------------------------------------------------------------------------------- 1 | function X=SNR(s,n); 2 | 3 | % Compute the signal-to-noise ratio in dB 4 | % X=SNR(signal,noise); 5 | % (it does not subtract the means). 6 | 7 | es=sum(sum(abs(s).^2)); 8 | en=sum(sum(abs(n).^2)); 9 | X=10*log10(es/en); 10 | 11 | -------------------------------------------------------------------------------- /text.pgm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LabForComputationalVision/textureSynth/65d6b59c88f746acbc4959895721a59cc4eff7a6/text.pgm -------------------------------------------------------------------------------- /textureAnalysis.m: -------------------------------------------------------------------------------- 1 | function [params] = textureAnalysis(im0, Nsc, Nor, Na) 2 | 3 | % Analyze texture for application of Portilla-Simoncelli model/algorithm. 4 | % 5 | % [params] = textureAnalysis(im0, Nsc, Nor, Na); 6 | % im0: original image 7 | % Nsc: number of scales 8 | % Nor: number of orientations 9 | % Na: spatial neighborhood considered (Na x Na) 10 | % 11 | % Example: Nsc=4; Nor=4; Na=7; 12 | % 13 | % See also textureSynthesis. 14 | 15 | % Javier Portilla and Eero Simoncelli. 16 | % Work described in: 17 | % "A Parametric Texture Model based on Joint Statistics of Complex Wavelet Coefficients". 18 | % J Portilla and E P Simoncelli. Int'l Journal of Computer Vision, 19 | % vol.40(1), pp. 49-71, Dec 2000. 20 | % 21 | % Please refer to this publication if you use the program for research or 22 | % for technical applications. Thank you. 23 | % 24 | % Copyright, Center for Neural Science, New York University, January 2001. 25 | % All rights reserved. 26 | 27 | 28 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 29 | 30 | Warn = 0; % Set to 1 if you want to see warning messages 31 | 32 | %% Check required args are passed 33 | if (nargin < 4) 34 | error('Function called with too few input arguments'); 35 | end 36 | 37 | %% 1D interpolation filter, for scale cross-correlations: 38 | interp = [-1/16 0 9/16 1 9/16 0 -1/16]/sqrt(2); 39 | 40 | if ( mod(Na,2) == 0 ) 41 | error('Na is not an odd integer'); 42 | end 43 | 44 | %% If the spatial neighborhood Na is too big for the lower scales, 45 | %% "modacor22.m" will make it as big as the spatial support at 46 | %% each scale: 47 | 48 | [Ny,Nx] = size(im0); 49 | nth = log2(min(Ny,Nx)/Na); 50 | if nth 1e-6, 117 | skew0p(Nsc+1) = mean2(im.^3)/vari^1.5; 118 | kurt0p(Nsc+1) = mean2(im.^4)/vari^2; 119 | else 120 | skew0p(Nsc+1) = 0; 121 | kurt0p(Nsc+1) = 3; 122 | end 123 | 124 | %% Compute central autoCorr of each Mag band, and the autoCorr of the 125 | %% combined (non-oriented) band. 126 | ace = NaN * ones(Na,Na,Nsc,Nor); 127 | for nsc = Nsc:-1:1, 128 | for nor = 1:Nor, 129 | nband = (nsc-1)*Nor+nor+1; 130 | ch = pyrBand(apyr0,pind0,nband); 131 | [Nly, Nlx] = size(ch); 132 | Sch = min(Nlx, Nly); 133 | le = min(Sch/2-1,la); 134 | cx = Nlx/2+1; %Assumes Nlx even 135 | cy = Nly/2+1; 136 | ac = fftshift(real(ifft2(abs(fft2(ch)).^2)))/prod(size(ch)); 137 | ac = ac(cy-le:cy+le,cx-le:cx+le); 138 | ace(la-le+1:la+le+1,la-le+1:la+le+1,nsc,nor) = ac; 139 | end 140 | 141 | %% Combine ori bands 142 | 143 | bandNums = [1:Nor] + (nsc-1)*Nor+1; %ori bands only 144 | ind1 = pyrBandIndices(pind0, bandNums(1)); 145 | indN = pyrBandIndices(pind0, bandNums(Nor)); 146 | bandInds = [ind1(1):indN(length(indN))]; 147 | %% Make fake pyramid, containing dummy hi, ori, lo 148 | fakePind = [pind0(bandNums(1),:);pind0(bandNums(1):bandNums(Nor)+1,:)]; 149 | fakePyr = [zeros(prod(fakePind(1,:)),1);... 150 | rpyr0(bandInds); zeros(prod(fakePind(size(fakePind,1),:)),1);]; 151 | ch = reconSFpyr(fakePyr, fakePind, [1]); % recon ori bands only 152 | im = real(expand(im,2))/4; 153 | im = im + ch; 154 | ac = fftshift(real(ifft2(abs(fft2(im)).^2)))/prod(size(ch)); 155 | ac = ac(cy-le:cy+le,cx-le:cx+le); 156 | acr(la-le+1:la+le+1,la-le+1:la+le+1,nsc) = ac; 157 | vari = ac(le+1,le+1); 158 | if vari/var0 > 1e-6, 159 | skew0p(nsc) = mean2(im.^3)/vari^1.5; 160 | kurt0p(nsc) = mean2(im.^4)/vari^2; 161 | else 162 | skew0p(nsc) = 0; 163 | kurt0p(nsc) = 3; 164 | end 165 | end 166 | 167 | %% Compute the cross-correlation matrices of the coefficient magnitudes 168 | %% pyramid at the different levels and orientations 169 | 170 | C0 = zeros(Nor,Nor,Nsc+1); 171 | Cx0 = zeros(Nor,Nor,Nsc); 172 | 173 | Cr0 = zeros(2*Nor,2*Nor,Nsc+1); 174 | Crx0 = zeros(2*Nor,2*Nor,Nsc); 175 | 176 | for nsc = 1:Nsc, 177 | firstBnum = (nsc-1)*Nor+2; 178 | cousinSz = prod(pind0(firstBnum,:)); 179 | ind = pyrBandIndices(pind0,firstBnum); 180 | cousinInd = ind(1) + [0:Nor*cousinSz-1]; 181 | 182 | if (nsc 0) 210 | Cx0(1:nc,1:np,nsc) = (cousins'*parents)/cousinSz; 211 | if (nsc==Nsc) 212 | C0(1:np,1:np,Nsc+1) = innerProd(parents)/(cousinSz/4); 213 | end 214 | end 215 | 216 | cousins = reshape(real(pyr0(cousinInd)), [cousinSz Nor]); 217 | nrc = size(cousins,2); nrp = size(rparents,2); 218 | Cr0(1:nrc,1:nrc,nsc) = innerProd(cousins)/cousinSz; 219 | if (nrp > 0) 220 | Crx0(1:nrc,1:nrp,nsc) = (cousins'*rparents)/cousinSz; 221 | if (nsc==Nsc) 222 | Cr0(1:nrp,1:nrp,Nsc+1) = innerProd(rparents)/(cousinSz/4); 223 | end 224 | end 225 | end 226 | 227 | %% Calculate the mean, range and variance of the LF and HF residuals' energy. 228 | 229 | channel = pyr0(pyrBandIndices(pind0,1)); 230 | vHPR0 = mean2(channel.^2); 231 | 232 | statsLPim = [skew0p kurt0p]; 233 | 234 | params = struct('pixelStats', statg0, ... 235 | 'pixelLPStats', statsLPim, ... 236 | 'autoCorrReal', acr, ... 237 | 'autoCorrMag', ace, ... 238 | 'magMeans', magMeans0, ... 239 | 'cousinMagCorr', C0, ... 240 | 'parentMagCorr', Cx0, ... 241 | 'cousinRealCorr', Cr0, ... 242 | 'parentRealCorr', Crx0, ... 243 | 'varianceHPR', vHPR0); 244 | 245 | 246 | -------------------------------------------------------------------------------- /textureSynthesis.m: -------------------------------------------------------------------------------- 1 | function [im,snrP,imS] = textureSynthesis(params, im0, Niter, cmask, imask) 2 | 3 | % [res,snrP,imS] = textureSynthesis(params, initialIm, Niter, cmask, imask) 4 | % 5 | % Synthesize texture applying Portilla-Simoncelli model/algorithm. 6 | % 7 | % params: structure containing texture parameters (as returned by textureAnalysis). 8 | % 9 | % im0: initial image, OR a vector (Ydim, Xdim, [SEED]) containing 10 | % dimensions of desired image and an optional seed for the random 11 | % number generator. If dimensions are passed, initial image is 12 | % Gaussian white noise. 13 | % 14 | % Niter (optional): Number of iterations. Default = 50. 15 | % 16 | % cmask (optional): binary column vector (4x1) indicating which sets of 17 | % constraints we want to apply in the synthesis. The four sets are: 18 | % 1) Marginal statistics (mean, var, skew, kurt, range) 19 | % 2) Correlation of subbands (space, orientation, scale) 20 | % 3) Correlation of magnitude responses (sp, or, sc) 21 | % 4) Relative local phase 22 | % 23 | % imask (optional): imsizex2 matrix. First column is a mask, second 24 | % column contains the image values to be imposed. If only one column is 25 | % provided, it assumes it corresponds to the image values, and it uses 26 | % a raised cosine square for the mask. 27 | % snrP (optional): Set of adjustment values (in dB) of the parameters. 28 | % imS (optional): Sequence of synthetic images, from niter = 1 to 2^n, being 29 | % n = floor(log2(Niter)). 30 | 31 | % Javier Portilla and Eero Simoncelli. 32 | % Work described in: 33 | % "A Parametric Texture Model based on Joint Statistics of Complex Wavelet Coefficients". 34 | % J Portilla and E P Simoncelli. Int'l Journal of Computer Vision, 35 | % vol.40(1), pp. 49-71, Dec 2000. 36 | % 37 | % Please refer to this publication if you use the program for research or 38 | % for technical applications. Thank you. 39 | % 40 | % Copyright, Center for Neural Science, New York University, January 2001. 41 | % All rights reserved. 42 | 43 | Warn = 0; % Set to 1 if you want to see warning messages 44 | 45 | %% Check required args are passed: 46 | if (nargin < 2) 47 | error('Function called with too few input arguments'); 48 | end 49 | 50 | if ( ~exist('Niter') | isempty(Niter) ) 51 | Niter = 50; 52 | end 53 | 54 | if (exist('cmask') & ~isempty(cmask) ) 55 | cmask = (cmask > 0.5); % indices of ones in mask 56 | else 57 | cmask = ones(4,1); 58 | end 59 | 60 | %% Extract parameters 61 | statg0 = params.pixelStats; 62 | mean0 = statg0(1); var0 = statg0(2); 63 | skew0 = statg0(3); kurt0 = statg0(4); 64 | mn0 = statg0(5); mx0 = statg0(6); 65 | statsLPim = params.pixelLPStats; 66 | skew0p = statsLPim(:,1); 67 | kurt0p = statsLPim(:,2); 68 | vHPR0 = params.varianceHPR; 69 | acr0 = params.autoCorrReal; 70 | ace0 = params.autoCorrMag; 71 | magMeans0 = params.magMeans; 72 | C0 = params.cousinMagCorr; 73 | Cx0 = params.parentMagCorr; 74 | Crx0 = params.parentRealCorr; 75 | 76 | %% Extract {Nsc, Nor, Na} from params 77 | tmp = size(params.autoCorrMag); 78 | Na = tmp(1); Nsc = tmp(3); 79 | Nor = tmp(length(tmp))*(length(tmp)==4) + (length(tmp)<4); 80 | la = (Na-1)/2; 81 | 82 | %% If im0 is a vector of length 2, create Gaussian white noise image of this 83 | %% size, with desired pixel mean and variance. If vector length is 84 | %% 3, use the 3rd element to seed the random number generator. 85 | if ( length(im0) <= 3 ) 86 | if ( length(im0) == 3) 87 | randn('state', im0(3)); % Reset Seed 88 | im0 = im0(1:2); 89 | end 90 | im = mean0 + sqrt(var0)*randn(im0); 91 | else 92 | im = im0; 93 | end 94 | 95 | %% If the spatial neighborhood Na is too big for the lower scales, 96 | %% "modacor22.m" will make it as big as the spatial support at 97 | %% each scale: 98 | [Ny,Nx] = size(im); 99 | nth = log2(min(Ny,Nx)/Na); 100 | if nth 1e-4, 168 | [im, snr2(niter,Nsc+1)] = ... 169 | modacor22(im, acr0(la-le+1:la+le+1,la-le+1:la+le+1,Nsc+1),p); 170 | else 171 | im = im*sqrt(vari/var2(im)); 172 | end 173 | if (var2(imag(ch))/var2(real(ch)) > 1e-6) 174 | fprintf(1,'Discarding non-trivial imaginary part, lowPass autoCorr!'); 175 | end 176 | im = real(im); 177 | end % cmask(2) 178 | if cmask(1), 179 | if vari/var0 > 1e-4, 180 | [im,snr7(niter,2*(Nsc+1)-1)] = modskew(im,skew0p(Nsc+1),p); % Adjusts skewness 181 | [im,snr7(niter,2*(Nsc+1))] = modkurt(im,kurt0p(Nsc+1),p); % Adjusts kurtosis 182 | end 183 | end % cmask(2) 184 | 185 | %% Subtract mean of magnitude 186 | if cmask(3), 187 | magMeans = zeros(size(pind,1), 1); 188 | for nband = 1:size(pind,1) 189 | indices = pyrBandIndices(pind,nband); 190 | magMeans(nband) = mean2(apyr(indices)); 191 | apyr(indices) = apyr(indices) - magMeans(nband); 192 | end 193 | end % cmask(3) 194 | 195 | %% Coarse-to-fine loop: 196 | for nsc = Nsc:-1:1 197 | 198 | firstBnum = (nsc-1)*Nor+2; 199 | cousinSz = prod(pind(firstBnum,:)); 200 | ind = pyrBandIndices(pind,firstBnum); 201 | cousinInd = ind(1) + [0:Nor*cousinSz-1]; 202 | 203 | %% Interpolate parents 204 | if (cmask(3) | cmask(4)), 205 | if (nsc 1e-6) 237 | fprintf(1,'Non-trivial imaginary part, mag crossCorr, lev=%d!\n',nsc); 238 | else 239 | cousins = real(cousins); 240 | ind = cousinInd; 241 | apyr(ind) = vectify(cousins); 242 | end 243 | 244 | %% Adjust autoCorr of mag responses 245 | nband = (nsc-1)*Nor+2; 246 | Sch = min(pind(nband,:)/2); 247 | nz = sum(sum(~isnan(ace0(:,:,nsc,1)))); 248 | lz = (sqrt(nz)-1)/2; 249 | le = min(Sch/2-1,lz); 250 | for nor = 1:Nor, 251 | nband = (nsc-1)*Nor+nor+1; 252 | ch = pyrBand(apyr,pind,nband); 253 | [ch, snr1(niter,nband-1)] = modacor22(ch,... 254 | ace0(la-le+1:la+le+1,la-le+1:la+le+1,nsc,nor), p); 255 | ch = real(ch); 256 | ind = pyrBandIndices(pind,nband); 257 | apyr(ind) = ch; 258 | %% Impose magnitude: 259 | mag = apyr(ind) + magMeans0(nband); 260 | mag = mag .* (mag>0); 261 | pyr(ind) = pyr(ind) .* (mag./(abs(pyr(ind))+(abs(pyr(ind)) 1e-6) 281 | fprintf(1,'Non-trivial imaginary part, real crossCorr, lev=%d!\n',nsc); 282 | else 283 | %%% NOTE: THIS SETS REAL PART ONLY - signal is now NONANALYTIC! 284 | pyr(cousinInd) = vectify(cousins(1:Nor*cousinSz)); 285 | end 286 | 287 | %% Re-create analytic subbands 288 | dims = pind(firstBnum,:); 289 | ctr = ceil((dims+0.5)/2); 290 | ang = mkAngle(dims, 0, ctr); 291 | ang(ctr(1),ctr(2)) = -pi/2; 292 | for nor = 1:Nor, 293 | nband = (nsc-1)*Nor+nor+1; 294 | ind = pyrBandIndices(pind,nband); 295 | ch = pyrBand(pyr, pind, nband); 296 | ang0 = pi*(nor-1)/Nor; 297 | xang = mod(ang-ang0+pi, 2*pi) - pi; 298 | amask = 2*(abs(xang) < pi/2) + (abs(xang) == pi/2); 299 | amask(ctr(1),ctr(2)) = 1; 300 | amask(:,1) = 1; 301 | amask(1,:) = 1; 302 | amask = fftshift(amask); 303 | ch = ifft2(amask.*fft2(ch)); % "Analytic" version 304 | pyr(ind) = ch; 305 | end 306 | 307 | %% Combine ori bands 308 | bandNums = [1:Nor] + (nsc-1)*Nor+1; %ori bands only 309 | ind1 = pyrBandIndices(pind, bandNums(1)); 310 | indN = pyrBandIndices(pind, bandNums(Nor)); 311 | bandInds = [ind1(1):indN(length(indN))]; 312 | %% Make fake pyramid, containing dummy hi, ori, lo 313 | fakePind = pind([bandNums(1), bandNums, bandNums(Nor)+1],:); 314 | fakePyr = [zeros(prod(fakePind(1,:)),1);... 315 | real(pyr(bandInds)); zeros(prod(fakePind(size(fakePind,1),:)),1)]; 316 | ch = reconSFpyr(fakePyr, fakePind, [1]); % recon ori bands only 317 | im = real(expand(im,2))/4; 318 | im = im + ch; 319 | vari = acr0(la+1:la+1,la+1:la+1,nsc); 320 | if cmask(2), 321 | if vari/var0 > 1e-4, 322 | [im, snr2(niter,nsc)] = ... 323 | modacor22(im, acr0(la-le+1:la+le+1,la-le+1:la+le+1,nsc), p); 324 | else 325 | im = im*sqrt(vari/var2(im)); 326 | end 327 | end % cmask(2) 328 | im = real(im); 329 | 330 | if cmask(1), 331 | %% Fix marginal stats 332 | if vari/var0 > 1e-4, 333 | [im,snr7(niter,2*nsc-1)] = modskew(im,skew0p(nsc),p); % Adjusts skewness 334 | [im,snr7(niter,2*nsc)] = modkurt(im,kurt0p(nsc),p); % Adjusts kurtosis 335 | end 336 | end % cmask(1) 337 | 338 | end %END Coarse-to-fine loop 339 | 340 | %% Adjust variance in HP, if higher than desired 341 | if (cmask(2)|cmask(3)|cmask(4)), 342 | ind = pyrBandIndices(pind,1); 343 | ch = pyr(ind); 344 | vHPR = mean2(ch.^2); 345 | if vHPR > vHPR0, 346 | ch = ch * sqrt(vHPR0/vHPR); 347 | pyr(ind) = ch; 348 | end 349 | end % cmask 350 | im = im + reconSFpyr(real(pyr), pind, [0]); %recon hi only 351 | 352 | %% Pixel statistics 353 | means = mean2(im); 354 | vars = var2(im, means); 355 | snr7(niter,2*(Nsc+1)+1) = snr(var0,var0-vars); 356 | im = im-means; % Adjusts mean and variance 357 | [mns mxs] = range2(im + mean0); 358 | snr7(niter,2*(Nsc+1)+2) = snr(mx0-mn0,sqrt((mx0-mxs)^2+(mn0-mns)^2)); 359 | if cmask(1), 360 | im = im*sqrt(((1-p)*vars + p*var0)/vars); 361 | end % cmaks(1) 362 | im = im+mean0; 363 | if cmask(1), 364 | [im, snr7(niter,2*(Nsc+1)+3)] = modskew(im,skew0,p); % Adjusts skewness (keep mean and variance) 365 | [im, snr7(niter,2*(Nsc+1)+4)] = modkurt(im,kurt0,p); % Adjusts kurtosis (keep mean and variance, 366 | % but not skewness) 367 | im = max(min(im,(1-p)*max(max(im))+p*mx0),... 368 | (1-p)*min(min(im))+p*mn0); % Adjusts range (affects everything) 369 | else 370 | snr7(niter,2*(Nsc+1)+3) = snr(skew0,skew0-skew2(im)); 371 | snr7(niter,2*(Nsc+1)+4) = snr(kurt0,kurt0-kurt2(im)); 372 | end % cmask(1) 373 | 374 | %% Force pixels specified by image mask 375 | if (exist('imask') & ~isempty(imask) ) 376 | im = mask.*reshape(imask(:,2 - (size(imask,2)==1)),size(im)) + ... 377 | (1-mask).*im; 378 | end 379 | 380 | snr6(niter,1) = snr(im-mean0,im-prev_im); 381 | 382 | if floor(log2(niter))==log2(niter), 383 | nq = nq + 1; 384 | imS(:,:,nq) = im; 385 | end 386 | 387 | tmp = prev_im; 388 | prev_im=im; 389 | 390 | figure(imf); 391 | subplot(1,2,1); 392 | showIm(im-tmp,'auto',1); title('Change'); 393 | subplot(1,2,2); 394 | showIm(im,'auto',1); title(sprintf('iteration %d/%d',niter,Niter)); 395 | drawnow 396 | 397 | % accelerator 398 | alpha = 0.8; 399 | im = im + alpha*(im - tmp); 400 | 401 | commented = 1; % set it to 0 for displaying convergence of parameters in SNR (dB) 402 | if ~commented, 403 | 404 | % The graphs that appear reflect 405 | % the relative distance of each parameter or group 406 | % of parametersi, to the original's, in decibels. 407 | % Note, however, that when the original parameters 408 | % are close to zero, this measurement is meaningless. 409 | % This is why in some cases it seems that some of 410 | % the parameters do not converge at all. 411 | 412 | figure(snrf); 413 | if cmask(1) 414 | subplot(171); plot(snr7); title('Mrgl stats'); 415 | end 416 | if cmask(2), 417 | subplot(172); plot(snr2); title('Raw auto'); 418 | end 419 | if cmask(3), 420 | subplot(173); plot(snr1); title('Mag auto'); 421 | subplot(174); plot(snr3); title('Mag ori'); 422 | subplot(175); plot(snr4); title('Mag scale'); 423 | end 424 | if (Nrp > 0) & cmask(4), 425 | subplot(176); plot(snr4r); title('Phs scale'); 426 | end 427 | subplot(177); plot(snr6); title('Im change'); 428 | drawnow 429 | 430 | end % if ~commented 431 | 432 | end %END MAIN LOOP 433 | 434 | im = prev_im; 435 | 436 | snrP = [snr7 snr2 snr1 snr3 snr4 snr4r snr6]; 437 | --------------------------------------------------------------------------------