function [GLCM_stats] = GLCM_Features1(glcmin,pairs) % % GLCM_Features1 helps to calculate the features from the different GLCMs % that are input to the function. The GLCMs are stored in a i x j x n % matrix, where n is the number of GLCMs calculated usually due to the % different orientation and displacements used in the algorithm. Usually % the values i and j are equal to 'NumLevels' parameter of the GLCM % computing function graycomatrix(). Note that matlab quantization values % belong to the set {1,..., NumLevels} and not from {0,...,(NumLevels-1)} % as provided in some references % http://www.mathworks.com/access/helpdesk/help/toolbox/images/graycomatrix % .html % % Although there is a function graycoprops() in Matlab Image Processing % Toolbox that computes four parameters Contrast, Correlation, Energy, % and Homogeneity. The paper by Haralick suggests a few more parameters % that are also computed here. The code is not fully vectorized and hence % is not an efficient implementation but it is easy to add new features % based on the GLCM using this code. Takes care of 3 dimensional glcms % (multiple glcms in a single 3D array) % % If you find that the values obtained are different from what you expect % or if you think there is a different formula that needs to be used % from the ones used in this code please let me know. % A few questions which I have are listed in the link % http://www.mathworks.com/matlabcentral/newsreader/view_thread/239608 % % I plan to submit a vectorized version of the code later and provide % updates based on replies to the above link and this initial code. % % Features computed % Autocorrelation: [2] (out.autoc) % Contrast: matlab/[1,2] (out.contr) % Correlation: matlab (out.corrm) % Correlation: [1,2] (out.corrp) % Cluster Prominence: [2] (out.cprom) % Cluster Shade: [2] (out.cshad) % Dissimilarity: [2] (out.dissi) % Energy: matlab / [1,2] (out.energ) % Entropy: [2] (out.entro) % Homogeneity: matlab (out.homom) % Homogeneity: [2] (out.homop) % Maximum probability: [2] (out.maxpr) % Sum of sqaures: Variance [1] (out.sosvh) % Sum average [1] (out.savgh) % Sum variance [1] (out.svarh) % Sum entropy [1] (out.senth) % Difference variance [1] (out.dvarh) % Difference entropy [1] (out.denth) % Information measure of correlation1 [1] (out.inf1h) % Informaiton measure of correlation2 [1] (out.inf2h) % Inverse difference (INV) is homom [3] (out.homom) % Inverse difference normalized (INN) [3] (out.indnc) % Inverse difference moment normalized [3] (out.idmnc) % % The maximal correlation coefficient was not calculated due to % computational instability % http://murphylab.web.cmu.edu/publications/boland/boland_node26.html % % Formulae from MATLAB site (some look different from % the paper by Haralick but are equivalent and give same results) % Example formulae: % Contrast = sum_i(sum_j( (i-j)^2 * p(i,j) ) ) (same in matlab/paper) % Correlation = sum_i( sum_j( (i - u_i)(j - u_j)p(i,j)/(s_i.s_j) ) ) (m) % Correlation = sum_i( sum_j( ((ij)p(i,j) - u_x.u_y) / (s_x.s_y) ) ) (p[2]) % Energy = sum_i( sum_j( p(i,j)^2 ) ) (same in matlab/paper) % Homogeneity = sum_i( sum_j( p(i,j) / (1 + |i-j|) ) ) (as in matlab) % Homogeneity = sum_i( sum_j( p(i,j) / (1 + (i-j)^2) ) ) (as in paper) % % Where: % u_i = u_x = sum_i( sum_j( i.p(i,j) ) ) (in paper [2]) % u_j = u_y = sum_i( sum_j( j.p(i,j) ) ) (in paper [2]) % s_i = s_x = sum_i( sum_j( (i - u_x)^2.p(i,j) ) ) (in paper [2]) % s_j = s_y = sum_i( sum_j( (j - u_y)^2.p(i,j) ) ) (in paper [2]) % % % Normalize the glcm: % Compute the sum of all the values in each glcm in the array and divide % each element by it sum % % Haralick uses 'Symmetric' = true in computing the glcm % There is no Symmetric flag in the Matlab version I use hence % I add the diagonally opposite pairs to obtain the Haralick glcm % Here it is assumed that the diagonally opposite orientations are paired % one after the other in the matrix % If the above assumption is true with respect to the input glcm then % setting the flag 'pairs' to 1 will compute the final glcms that would result % by setting 'Symmetric' to true. If your glcm is computed using the % Matlab version with 'Symmetric' flag you can set the flag 'pairs' to 0 % % References: % 1. R. M. Haralick, K. Shanmugam, and I. Dinstein, Textural Features of % Image Classification, IEEE Transactions on Systems, Man and Cybernetics, % vol. SMC-3, no. 6, Nov. 1973 % 2. L. Soh and C. Tsatsoulis, Texture Analysis of SAR Sea Ice Imagery % Using Gray Level Co-Occurrence Matrices, IEEE Transactions on Geoscience % and Remote Sensing, vol. 37, no. 2, March 1999. % 3. D A. Clausi, An analysis of co-occurrence texture statistics as a % function of grey level quantization, Can. J. Remote Sensing, vol. 28, no. % 1, pp. 45-62, 2002 % 4. http://murphylab.web.cmu.edu/publications/boland/boland_node26.html % % % Example: % % Usage is similar to graycoprops() but needs extra parameter 'pairs' apart % from the GLCM as input % I = imread('circuit.tif'); % GLCM2 = graycomatrix(I,'Offset',[2 0;0 2]); % stats = GLCM_features1(GLCM2,0) % The output is a structure containing all the parameters for the different % GLCMs % % [Avinash Uppuluri: avinash_uv@yahoo.com: Last modified: 11/20/08] % If 'pairs' not entered: set pairs to 0 if ((nargin > 2) || (nargin == 0)) error('Too many or too few input arguments. Enter GLCM and pairs.'); elseif ( (nargin == 2) ) if ((size(glcmin,1) <= 1) || (size(glcmin,2) <= 1)) error('The GLCM should be a 2-D or 3-D matrix.'); elseif ( size(glcmin,1) ~= size(glcmin,2) ) error('Each GLCM should be square with NumLevels rows and NumLevels cols'); end elseif (nargin == 1) % only GLCM is entered pairs = 0; % default is numbers and input 1 for percentage if ((size(glcmin,1) <= 1) || (size(glcmin,2) <= 1)) error('The GLCM should be a 2-D or 3-D matrix.'); elseif ( size(glcmin,1) ~= size(glcmin,2) ) error('Each GLCM should be square with NumLevels rows and NumLevels cols'); end end format long e if (pairs == 1) newn = 1; for nglcm = 1:2:size(glcmin,3) glcm(:,:,newn) = glcmin(:,:,nglcm) + glcmin(:,:,nglcm+1); newn = newn + 1; end elseif (pairs == 0) glcm = glcmin; end size_glcm_1 = size(glcm,1); size_glcm_2 = size(glcm,2); size_glcm_3 = size(glcm,3); % checked out.autoc = zeros(1,size_glcm_3); % Autocorrelation: [2] out.contr = zeros(1,size_glcm_3); % Contrast: matlab/[1,2] out.corrm = zeros(1,size_glcm_3); % Correlation: matlab out.corrp = zeros(1,size_glcm_3); % Correlation: [1,2] out.cprom = zeros(1,size_glcm_3); % Cluster Prominence: [2] out.cshad = zeros(1,size_glcm_3); % Cluster Shade: [2] out.dissi = zeros(1,size_glcm_3); % Dissimilarity: [2] out.energ = zeros(1,size_glcm_3); % Energy: matlab / [1,2] out.entro = zeros(1,size_glcm_3); % Entropy: [2] out.homom = zeros(1,size_glcm_3); % Homogeneity: matlab out.homop = zeros(1,size_glcm_3); % Homogeneity: [2] out.maxpr = zeros(1,size_glcm_3); % Maximum probability: [2] out.sosvh = zeros(1,size_glcm_3); % Sum of sqaures: Variance [1] out.savgh = zeros(1,size_glcm_3); % Sum average [1] out.svarh = zeros(1,size_glcm_3); % Sum variance [1] out.senth = zeros(1,size_glcm_3); % Sum entropy [1] out.dvarh = zeros(1,size_glcm_3); % Difference variance [4] %out.dvarh2 = zeros(1,size_glcm_3); % Difference variance [1] out.denth = zeros(1,size_glcm_3); % Difference entropy [1] out.inf1h = zeros(1,size_glcm_3); % Information measure of correlation1 [1] out.inf2h = zeros(1,size_glcm_3); % Informaiton measure of correlation2 [1] %out.mxcch = zeros(1,size_glcm_3);% maximal correlation coefficient [1] %out.invdc = zeros(1,size_glcm_3);% Inverse difference (INV) is homom [3] out.indnc = zeros(1,size_glcm_3); % Inverse difference normalized (INN) [3] out.idmnc = zeros(1,size_glcm_3); % Inverse difference moment normalized [3] % correlation with alternate definition of u and s %out.corrm2 = zeros(1,size_glcm_3); % Correlation: matlab %out.corrp2 = zeros(1,size_glcm_3); % Correlation: [1,2] glcm_sum = zeros(size_glcm_3,1); glcm_mean = zeros(size_glcm_3,1); glcm_var = zeros(size_glcm_3,1); % http://www.fp.ucalgary.ca/mhallbey/glcm_mean.htm confuses the range of % i and j used in calculating the means and standard deviations. % As of now I am not sure if the range of i and j should be [1:Ng] or % [0:Ng-1]. I am working on obtaining the values of mean and std that get % the values of correlation that are provided by matlab. u_x = zeros(size_glcm_3,1); u_y = zeros(size_glcm_3,1); s_x = zeros(size_glcm_3,1); s_y = zeros(size_glcm_3,1); % % alternate values of u and s % u_x2 = zeros(size_glcm_3,1); % u_y2 = zeros(size_glcm_3,1); % s_x2 = zeros(size_glcm_3,1); % s_y2 = zeros(size_glcm_3,1); % checked p_x p_y p_xplusy p_xminusy p_x = zeros(size_glcm_1,size_glcm_3); % Ng x #glcms[1] p_y = zeros(size_glcm_2,size_glcm_3); % Ng x #glcms[1] p_xplusy = zeros((size_glcm_1*2 - 1),size_glcm_3); %[1] p_xminusy = zeros((size_glcm_1),size_glcm_3); %[1] % checked hxy hxy1 hxy2 hx hy hxy = zeros(size_glcm_3,1); hxy1 = zeros(size_glcm_3,1); hx = zeros(size_glcm_3,1); hy = zeros(size_glcm_3,1); hxy2 = zeros(size_glcm_3,1); %Q = zeros(size(glcm)); for k = 1:size_glcm_3 % number glcms glcm_sum(k) = sum(sum(glcm(:,:,k))); glcm(:,:,k) = glcm(:,:,k)./glcm_sum(k); % Normalize each glcm glcm_mean(k) = mean2(glcm(:,:,k)); % compute mean after norm glcm_var(k) = (std2(glcm(:,:,k)))^2; for i = 1:size_glcm_1 for j = 1:size_glcm_2 out.contr(k) = out.contr(k) + (abs(i - j))^2.*glcm(i,j,k); out.dissi(k) = out.dissi(k) + (abs(i - j)*glcm(i,j,k)); out.energ(k) = out.energ(k) + (glcm(i,j,k).^2); out.entro(k) = out.entro(k) - (glcm(i,j,k)*log(glcm(i,j,k) + eps)); out.homom(k) = out.homom(k) + (glcm(i,j,k)/( 1 + abs(i-j) )); out.homop(k) = out.homop(k) + (glcm(i,j,k)/( 1 + (i - j)^2)); % [1] explains sum of squares variance with a mean value; % the exact definition for mean has not been provided in % the reference: I use the mean of the entire normalized glcm out.sosvh(k) = out.sosvh(k) + glcm(i,j,k)*((i - glcm_mean(k))^2); %out.invdc(k) = out.homom(k); out.indnc(k) = out.indnc(k) + (glcm(i,j,k)/( 1 + (abs(i-j)/size_glcm_1) )); out.idmnc(k) = out.idmnc(k) + (glcm(i,j,k)/( 1 + ((i - j)/size_glcm_1)^2)); u_x(k) = u_x(k) + (i)*glcm(i,j,k); % changed 10/26/08 u_y(k) = u_y(k) + (j)*glcm(i,j,k); % changed 10/26/08 % code requires that Nx = Ny % the values of the grey levels range from 1 to (Ng) end end out.maxpr(k) = max(max(glcm(:,:,k))); end % glcms have been normalized: % The contrast has been computed for each glcm in the 3D matrix % (tested) gives similar results to the matlab function for k = 1:size_glcm_3 for i = 1:size_glcm_1 for j = 1:size_glcm_2 p_x(i,k) = p_x(i,k) + glcm(i,j,k); p_y(i,k) = p_y(i,k) + glcm(j,i,k); % taking i for j and j for i if (ismember((i + j),[2:2*size_glcm_1])) p_xplusy((i+j)-1,k) = p_xplusy((i+j)-1,k) + glcm(i,j,k); end if (ismember(abs(i-j),[0:(size_glcm_1-1)])) p_xminusy((abs(i-j))+1,k) = p_xminusy((abs(i-j))+1,k) +... glcm(i,j,k); end end end % % consider u_x and u_y and s_x and s_y as means and standard deviations % % of p_x and p_y % u_x2(k) = mean(p_x(:,k)); % u_y2(k) = mean(p_y(:,k)); % s_x2(k) = std(p_x(:,k)); % s_y2(k) = std(p_y(:,k)); end % marginal probabilities are now available [1] % p_xminusy has +1 in index for matlab (no 0 index) % computing sum average, sum variance and sum entropy: for k = 1:(size_glcm_3) for i = 1:(2*(size_glcm_1)-1) out.savgh(k) = out.savgh(k) + (i+1)*p_xplusy(i,k); % the summation for savgh is for i from 2 to 2*Ng hence (i+1) out.senth(k) = out.senth(k) - (p_xplusy(i,k)*log(p_xplusy(i,k) + eps)); end end % compute sum variance with the help of sum entropy for k = 1:(size_glcm_3) for i = 1:(2*(size_glcm_1)-1) out.svarh(k) = out.svarh(k) + (((i+1) - out.senth(k))^2)*p_xplusy(i,k); % the summation for savgh is for i from 2 to 2*Ng hence (i+1) end end % compute difference variance, difference entropy, for k = 1:size_glcm_3 % out.dvarh2(k) = var(p_xminusy(:,k)); % but using the formula in % http://murphylab.web.cmu.edu/publications/boland/boland_node26.html % we have for dvarh for i = 0:(size_glcm_1-1) out.denth(k) = out.denth(k) - (p_xminusy(i+1,k)*log(p_xminusy(i+1,k) + eps)); out.dvarh(k) = out.dvarh(k) + (i^2)*p_xminusy(i+1,k); end end % compute information measure of correlation(1,2) [1] for k = 1:size_glcm_3 hxy(k) = out.entro(k); for i = 1:size_glcm_1 for j = 1:size_glcm_2 hxy1(k) = hxy1(k) - (glcm(i,j,k)*log(p_x(i,k)*p_y(j,k) + eps)); hxy2(k) = hxy2(k) - (p_x(i,k)*p_y(j,k)*log(p_x(i,k)*p_y(j,k) + eps)); % for Qind = 1:(size_glcm_1) % Q(i,j,k) = Q(i,j,k) +... % ( glcm(i,Qind,k)*glcm(j,Qind,k) / (p_x(i,k)*p_y(Qind,k)) ); % end end hx(k) = hx(k) - (p_x(i,k)*log(p_x(i,k) + eps)); hy(k) = hy(k) - (p_y(i,k)*log(p_y(i,k) + eps)); end out.inf1h(k) = ( hxy(k) - hxy1(k) ) / ( max([hx(k),hy(k)]) ); out.inf2h(k) = ( 1 - exp( -2*( hxy2(k) - hxy(k) ) ) )^0.5; % eig_Q(k,:) = eig(Q(:,:,k)); % sort_eig(k,:)= sort(eig_Q(k,:),'descend'); % out.mxcch(k) = sort_eig(k,2)^0.5; % The maximal correlation coefficient was not calculated due to % computational instability % http://murphylab.web.cmu.edu/publications/boland/boland_node26.html end corm = zeros(size_glcm_3,1); corp = zeros(size_glcm_3,1); % using http://www.fp.ucalgary.ca/mhallbey/glcm_variance.htm for s_x s_y for k = 1:size_glcm_3 for i = 1:size_glcm_1 for j = 1:size_glcm_2 s_x(k) = s_x(k) + (((i) - u_x(k))^2)*glcm(i,j,k); s_y(k) = s_y(k) + (((j) - u_y(k))^2)*glcm(i,j,k); corp(k) = corp(k) + ((i)*(j)*glcm(i,j,k)); corm(k) = corm(k) + (((i) - u_x(k))*((j) - u_y(k))*glcm(i,j,k)); out.cprom(k) = out.cprom(k) + (((i + j - u_x(k) - u_y(k))^4)*... glcm(i,j,k)); out.cshad(k) = out.cshad(k) + (((i + j - u_x(k) - u_y(k))^3)*... glcm(i,j,k)); end end % using http://www.fp.ucalgary.ca/mhallbey/glcm_variance.htm for s_x % s_y : This solves the difference in value of correlation and might be % the right value of standard deviations required % According to this website there is a typo in [2] which provides % values of variance instead of the standard deviation hence a square % root is required as done below: s_x(k) = s_x(k) ^ 0.5; s_y(k) = s_y(k) ^ 0.5; out.autoc(k) = corp(k); out.corrp(k) = (corp(k) - u_x(k)*u_y(k))/(s_x(k)*s_y(k)); out.corrm(k) = corm(k) / (s_x(k)*s_y(k)); % % alternate values of u and s % out.corrp2(k) = (corp(k) - u_x2(k)*u_y2(k))/(s_x2(k)*s_y2(k)); % out.corrm2(k) = corm(k) / (s_x2(k)*s_y2(k)); aut=mean([out.autoc]); cont=mean([out.contr]); corm=mean([out.corrm]); corp=mean([out.corrp]); cpro=mean([out.cprom]); cshd=mean([out.cshad]); dissim=mean([out.dissi]); energy=mean([out.energ]); entrop=mean([out.entro]); homo=mean([out.homom]); homopr=mean([out.homop]); maxp=mean([out.maxpr]); sovh=mean([out.sosvh]); sagh=mean([out.savgh]); svah=mean([out.svarh]); sent=mean([out.senth]); dvah=mean([out.dvarh]); denh=mean([out.denth]); infh1=mean([out.inf1h]); infh2=mean([out.inf2h]); indn=mean([out.indnc]); idmc=mean([out.idmnc]); GLCM_stats=[aut cont corm corp cpro cshd dissim energy entrop homo homopr maxp sovh sagh svah sent dvah denh infh1 infh2 indn idmc]; end % Here the formula in the paper out.corrp and the formula in matlab % out.corrm are equivalent as confirmed by the similar results obtained % % The papers have a slightly different formular for Contrast % % I have tested here to find this formula in the papers provides the % % same results as the formula provided by the matlab function for % % Contrast (Hence this part has been commented) % out.contrp = zeros(size_glcm_3,1); % contp = 0; % Ng = size_glcm_1; % for k = 1:size_glcm_3 % for n = 0:(Ng-1) % for i = 1:Ng % for j = 1:Ng % if (abs(i-j) == n) % contp = contp + glcm(i,j,k); % end % end % end % out.contrp(k) = out.contrp(k) + n^2*contp; % contp = 0; % end % % end % GLCM Features (Soh, 1999; Haralick, 1973; Clausi 2002) % f1. Uniformity / Energy / Angular Second Moment (done) % f2. Entropy (done) % f3. Dissimilarity (done) % f4. Contrast / Inertia (done) % f5. Inverse difference % f6. correlation % f7. Homogeneity / Inverse difference moment % f8. Autocorrelation % f9. Cluster Shade % f10. Cluster Prominence % f11. Maximum probability % f12. Sum of Squares % f13. Sum Average % f14. Sum Variance % f15. Sum Entropy % f16. Difference variance % f17. Difference entropy % f18. Information measures of correlation (1) % f19. Information measures of correlation (2) % f20. Maximal correlation coefficient % f21. Inverse difference normalized (INN) % f22. Inverse difference moment normalized (IDN)