[797475]: / QIF_extraction / Matlab_Source / lna01_4.m.backup

Download this file

932 lines (771 with data), 41.8 kB

function[metrics_3d, metrics_2d] = lna(binary_ana, grayscale_ana, segment_num, kernel, gray_seg_thresh, fast_only)

   
   if (exist('fast_only', 'var') == 0)
       printf('fast_only undefined');
       fast_only = 0;
   endif

   tic ;
   disp('Entering lna01_4') ;
   lna_version = 1.4 ;

   % Version 1.2 incorporates the gradient margin metric and the lacunarity
   % calculation. The only known missing  metric is "correlation."
   %
   % Version 1.3 incorporates 5 new texture metrics from:
   %    Moses Amadasun and Robert T. King, "Textural Features Corresponding to
   %    Textural Properties," IEEE Transactions on Systems, Man, and
   %    Cybernetics, Vol. 19, No. 5, September/October 1989.
   %
   % Version 1.4 adds a new statistics, the median HU inside the nodule to the
   %    2D and 3D calculations.

   % Should I also input a path to the data and a path to the results as well
   % as the filenames themselves??????????????

   % bq_full.m (Calculates the 3-D distance to surface metric that takes so
   % long.)
   %
   % Calculate metrics for some lung nodules in support of an ICTS grant
   % proposal due on Monday, November 16, 2009.
   %
   % Metrics have been proposed for 3-D image volumes and for 2-D image
   % slices.

   % Read in the binary-thresholded 3-D image with the nodule only, which is
   % unsigned 8-bit.

   % Also read in the grayscale 3-D image which contains the nodule. It is
   % signed 16-bit.
   %
   % Software has been "re-purposed" to analyze bones rather than lung
   % nodules. DGP 2010-04-29
   %
   % This version includes the variable names and descriptions devised by
   % David Gierada and David Politte for use with the REDCAP database.

   disp(' ') ;
   disp(' ') ;
   disp(' ') ;

   filename_root = strrep(binary_ana, '_bin', '') ;
   diary_name = [filename_root '.diary'] ;
   diary(diary_name) ;

   header_name = [binary_ana '.hdr'] ;
   header = analyze75info(header_name) ;
   dy_mm = double(header.PixelDimensions(1)) ;
   dx_mm = double(header.PixelDimensions(2)) ;
   dz_mm = double(header.PixelDimensions(3)) ;
   image_bin_name = [binary_ana '.img'] ;
   a = int8(analyze75read(image_bin_name)) ;
   temp = size(a) ;
   d3_long_pix = temp(2) ;
   d3_trans_pix = temp(1) ;
   d3_z_pix = temp(3) ;
   s_3d_orig = double(a) ;
   disp_string = ['# rows = '    num2str(d3_trans_pix) ...
               '   # columns = ' num2str(d3_long_pix) ...
               '   # planes = '  num2str(d3_z_pix)] ;
   disp(disp_string) ;
   clear a temp disp_string ;

   image_gray_name = [grayscale_ana '.img'] ;
   g = analyze75read(image_gray_name) ;
   g_3d = double(g) ;
   clear g ;

   if (~isempty(find(mod(s_3d_orig, 1) ~= 0, 1)))
      disp('WARNING: Segmentation image is not binary!') ;
      exit ;
   end
   s_3d = s_3d_orig / max(max(max(s_3d_orig))) ;
   if (~isempty(find(mod(s_3d, 1) ~= 0, 1)))
      disp('WARNING: Segmentation image is not binary!') ;
      exit ;
   end
   clear s_3d_orig ;

   % Ensure that the nodule isn't on a border plane. Some of the
   % algorithms below require 2 "guard" planes on all sides.

   s_3d(1:2,       :,         :        ) = 0 ;
   s_3d(end-1:end, :,         :        ) = 0 ;
   s_3d(:,         1:2,       :        ) = 0 ;
   s_3d(:,         end-1:end, :        ) = 0 ;
   s_3d(:,         :,         1:2      ) = 0 ;
   s_3d(:,         :,         end-1:end) = 0 ;


   %**************************************************************************
   %**************************************************************************
   %**************************************************************************
   % CALCULATE 3-D METRICS
   %**************************************************************************
   %**************************************************************************
   %************************************************lac**************************

   disp('CALCULATING 3-D METRICS ...') ;

   %**************************************************************************
   % Calculate size metrics.
   %**************************************************************************

   disp('   Calculating size metrics ...') ;

   % Calculate the volume.

   disp('      Calculating volume ...') ;
   d3_nodule_voxels = sum(sum(sum(s_3d))) ;
   d3_nodule_volume = d3_nodule_voxels * dx_mm * dy_mm * dz_mm ;
   disp('      Finished calculating volume.') ;

   % Calculate a bounding box.

   disp('      Calculating bounding box ...') ;
   [iii_list, jjj_list, kkk_list] = ind2sub(size(s_3d), find(s_3d > 0)) ;
   d3_bounding_box = [min(iii_list) min(jjj_list) min(kkk_list) ; ...
                      max(iii_list) max(jjj_list) max(kkk_list)] ;
   d3_bounding_x_mm = (d3_bounding_box(2, 2) - d3_bounding_box(1, 2)) * dx_mm ;
   d3_bounding_y_mm = (d3_bounding_box(2, 1) - d3_bounding_box(1, 1)) * dy_mm ;
   d3_bounding_z_mm = (d3_bounding_box(2, 3) - d3_bounding_box(1, 3)) * dz_mm ;
   clear iii_list jjj_list kkk_list ;
   disp('      Finished calculating bounding box.') ;

   % Calculate the surface area.

   disp('      Calculating surface area ...') ;
   type_a_surfaces_3d = sum(sum(sum(abs(s_3d(:,:,1:(end-1)) - s_3d(:,:,2:end))))) ;
   type_b_surfaces_3d = sum(sum(sum(abs(s_3d(:,1:(end-1),:) - s_3d(:,2:end,:))))) ;
   type_c_surfaces_3d = sum(sum(sum(abs(s_3d(1:(end-1),:,:) - s_3d(2:end,:,:))))) ;

   d3_surface_area_mm2 = (type_a_surfaces_3d * dx_mm * dy_mm) ...
                       + (type_b_surfaces_3d * dx_mm * dz_mm) ...
                       + (type_c_surfaces_3d * dy_mm * dz_mm) ;
   disp('      Finished calculating surface area.') ;

   disp('   Finished calculating size metrics.') ;

   %**************************************************************************
   % Calculate shape metrics.
   %**************************************************************************

   disp('   Calculating shape metrics ...') ;

   % Calculate the sphericity.

   disp('      Calculating sphericity ...') ;
   d3_sphericity = (pi^(1/3)) * ((6 * d3_nodule_volume)^(2/3)) / d3_surface_area_mm2 ;
   disp('      Finished calculating sphericity.') ;

   % Calculate the compactness. Compactness is sphericity cubed.

   disp('      Calculating compactness ...') ;
   d3_compactness = (36 * pi * (d3_nodule_volume^2)) / (d3_surface_area_mm2^3) ;
   disp('      Finished calculating compactness.') ;

   % Calculate the principal rotational moments.
   % This calculation uses the binary segmentation image.
   % See http://en.wikipedia.org/wiki/Moment_of_inertia for a definition of the
   % tensor matrix that is calculated.
   % The tensor matrix is then diagonalized using a singular-value
   % decomposition (SVD) and normalized to have unit energy so that it is
   % independent of size.

   disp('      Calculating principal rotational moments ...') ;

   % Find the centroid. This will be a tuple.

   x = 1:d3_long_pix ;
   y = 1:d3_trans_pix ;
   z = 1:d3_z_pix ;
   [X, Y, Z] = meshgrid(x, y, z) ;
   x_c = sum(sum(sum(s_3d .* X))) / d3_nodule_voxels ;
   y_c = sum(sum(sum(s_3d .* Y))) / d3_nodule_voxels ;
   z_c = sum(sum(sum(s_3d .* Z))) / d3_nodule_voxels ;
   clear X Y Z ;

   x_shift = x - x_c ;
   y_shift = y - y_c ;
   z_shift = z - z_c ;
   [X_shift, Y_shift, Z_shift] = meshgrid(x_shift, y_shift, z_shift) ;
   clear x y z x_c y_c z_c x_shift y_shift z_shift ;

   x_sq_term_3d = sum(sum(sum((s_3d .* X_shift).^2))) / d3_nodule_voxels ;
   y_sq_term_3d = sum(sum(sum((s_3d .* Y_shift).^2))) / d3_nodule_voxels ;
   z_sq_term_3d = sum(sum(sum((s_3d .* Z_shift).^2))) / d3_nodule_voxels ;

   xy_term_3d   = sum(sum(sum((s_3d .* X_shift) .* (s_3d .* Y_shift)))) / d3_nodule_voxels ;
   xz_term_3d   = sum(sum(sum((s_3d .* X_shift) .* (s_3d .* Z_shift)))) / d3_nodule_voxels ;
   yz_term_3d   = sum(sum(sum((s_3d .* Y_shift) .* (s_3d .* Z_shift)))) / d3_nodule_voxels ;
   clear X_shift Y_shift Z_shift ;

   % Define the tensor as shown on the wikipedia web page.

   I11_3d = y_sq_term_3d + z_sq_term_3d ;
   I22_3d = x_sq_term_3d + z_sq_term_3d ;
   I33_3d = x_sq_term_3d + y_sq_term_3d ;

   I12_3d = xy_term_3d ;
   I21_3d = I12_3d ;

   I13_3d = xz_term_3d ;
   I31_3d = I13_3d ;

   I23_3d = yz_term_3d ;
   I32_3d = I23_3d ;

   clear x_sq_term_3d y_sq_term_3d z_sq_term_3d xy_term_3d xz_term_3d yz_term_3d ;

   I_matrix_3d = [ I11_3d -I12_3d -I13_3d ; ...
                  -I21_3d  I22_3d -I23_3d ; ...
                  -I31_3d -I32_3d  I33_3d] ;
   clear I11_3d I12_3d I13_3d I21_3d I22_3d I23_3d I31_3d I32_3d I33_3d ;

   % Diagonalize it. The r_i_3d are the answers we seek.

   [U, S, V] = svd(I_matrix_3d) ;
   norm_fac = sqrt(sum(diag(S) .^ 2)) ;
   d3_r1 = S(1,1) / norm_fac ;
   d3_r2 = S(2,2) / norm_fac ;
   d3_r3 = S(3,3) / norm_fac ;
   clear norm_fac U S V I_matrix_3d ;

   d3_moment_ratio = d3_r1 / d3_r3 ;

   disp('      Finished calculating principal rotational moments.') ;

   disp('   Finished calculating shape metrics.') ;

   %**************************************************************************
   % Calculate attenuation value metrics.
   %**************************************************************************

   disp('   Calculating attenuation value metrics ...') ;
   index_inside_3d   = find(s_3d == 1) ;
   disp('      Calculating median of attenuation values ...') ;
   d3_median_atten   = median(g_3d(index_inside_3d)) ;
   disp('      Finished calculating median of attenuation values.') ;
   disp('      Calculating mean of attenuation values ...') ;
   d3_mean_atten     = mean(g_3d(index_inside_3d)) ;
   disp('      Finished calculating mean of attenuation values.') ;
   disp('      Calculating standard deviation of attenuation values ...') ;
   d3_stdev_atten    = std(g_3d(index_inside_3d))  ;
   disp('      Finished calculating standard deviation of attenuation values.') ;
   disp('      Calculating variance of attenuation values ...') ;
   d3_var_atten      = var(g_3d(index_inside_3d))  ;
   disp('      Finished calculating variance of attenuation values.') ;
   disp('      Calculating skewness of attenuation values ...') ;
   d3_skew_atten     = skewness(g_3d(index_inside_3d)) ;
   disp('      Finished calculating skewness of attenuation values.') ;
   disp('      Calculating kurtosis of attenuation values ...') ;
   d3_kurt_atten     = kurtosis(g_3d(index_inside_3d)) ;
   disp('      Finished calculating kurtosis of attenuation values.') ;
   disp('      Calculating entropy of attenuation values ...') ;
   d3_entropy_atten  = entropy(g_3d(index_inside_3d)) ;
   disp('      Finished calculating entropy of attenuation values.') ;

   disp('   Finished calculating attenuation value metrics.') ;

   %**************************************************************************
   % Calculate texture metrics.
   % Start by differencing the grayscale images with nearest neighbors.
   %**************************************************************************

   disp('   Calculating texture metrics ...') ;
   kern = zeros(3, 3, 3) ;
   q = -1/6 ;
   kern(:,:,1) = [0 0 0 ; ...
                  0 q 0 ; ...
                  0 0 0] ;
   kern(:,:,2) = [0 q 0 ; ...
                  q 1 q ; ...
                  0 q 0] ;
   kern(:,:,3) = [0 0 0 ; ...
                  0 q 0 ; ...
                  0 0 0] ;
   g_3d_diff = convn(g_3d, kern, 'same') ;
   disp('      Calculating mean of difference images ...') ;
   d3_mean_diff     = mean(g_3d_diff(index_inside_3d)) ;
   disp('      Finished calculating mean of difference images.') ;
   disp('      Calculating standard deviation of difference images ...') ;
   d3_stdev_diff      = std(g_3d_diff(index_inside_3d)) ;
   disp('      Finished calculating standard deviation of difference images.') ;
   disp('      Calculating variance of difference images ...') ;
   d3_var_diff      = var(g_3d_diff(index_inside_3d))  ;
   disp('      Finished calculating variance of difference images.') ;
   disp('      Calculating skewness of difference images ...') ;
   d3_skew_diff = skewness(g_3d_diff(index_inside_3d)) ;
   disp('      Finished calculating skewness of difference images.') ;
   disp('      Calculating kurtosis of difference images ...') ;
   d3_kurt_diff = kurtosis(g_3d_diff(index_inside_3d)) ;
   disp('      Finished calculating kurtosis of difference images.') ;
   disp('      Calculating entropy of difference images ...') ;
   d3_entropy_diff  = entropy(g_3d_diff(index_inside_3d)) ;
   disp('      Finished calculating entropy of difference images.') ;
   disp('      Calculating correlation metric of difference images ...') ;
   disp('         PLACEHOLDER FOR 3D CORRELATION METRIC') ;
   d3_corr_diff = -9999.0 ;
   disp('      Finished calculating correlation metric of difference images.') ;
   disp('      Calculating lacunarity of segmentation images ...') ;
   s_3d_small = s_3d(d3_bounding_box(1,1):d3_bounding_box(2,1), ...
                     d3_bounding_box(1,2):d3_bounding_box(2,2), ...
                     d3_bounding_box(1,3):d3_bounding_box(2,3)) ;
   [d3_temp_lacunarity, d3_box_size_lacunarity] = calc_lacunarity_3d(s_3d_small) ;
   d3_lacunarity_seg = zeros(1, 10) ;
   d3_lacunarity_seg(1:numel(d3_temp_lacunarity)) = d3_temp_lacunarity ;
   d3_lacunarity_seg(numel(d3_temp_lacunarity):end) = d3_temp_lacunarity(end) ;
   disp('      Finished calculating lacunarity of segmentation images.') ;
   disp(['Entering ngtdm: ' datestr(now)]) ;
   [d3_ak_coarseness_1, d3_ak_contrast_1, d3_ak_busyness_1, ...
      d3_ak_complexity_1, d3_ak_texture_strength_1] = ngtdm(g_3d, s_3d, 1) ;
   disp(['Finished with ngtdm: ' datestr(now)]) ;
   disp(['Entering ngtdm: ' datestr(now)]) ;
   [d3_ak_coarseness_2, d3_ak_contrast_2, d3_ak_busyness_2, ...
      d3_ak_complexity_2, d3_ak_texture_strength_2] = ngtdm(g_3d, s_3d, 2) ;
   disp(['Finished with ngtdm: ' datestr(now)]) ;
   clear g_3d_diff q kern ;
   disp('   Finished calculating texture metrics.') ;

   %**************************************************************************
   % Calculate margin metrics.
   %**************************************************************************

   disp('   Calculating margin metrics ...') ;

   % Calculate the mean distance to surface.

   if (fast_only == 0)
      
      disp('      Calculating distance to surface ...') ;
      dist_img_name_3d = [filename_root '_3d.dst'] ;
      fid_3d = fopen(dist_img_name_3d, 'w') ;

      disp('         Finding border voxels ...') ;
      kern = ones(3,3,3) ;
      border_3d = sign((1 - s_3d) .* convn(s_3d, kern, 'same')) ;
      clear kern ;
      [iii_list, jjj_list, kkk_list] = ind2sub(size(s_3d), find(border_3d > 0)) ;
      disp('         Finished finding border voxels.') ;

      disp('         Allocating 3D image for distance to surface ...') ;
      dist_exterior_3d = single(zeros(d3_trans_pix, d3_long_pix)) ;
      disp('         Finished allocating 3D image for distance to surface.') ;

      d3_summed_dist = 0.0 ;

      for k = 1:d3_z_pix
         nnztp = length(find(s_3d(:, :, k) > 0)) ;
         disp_string = ['         Plane ' num2str(k) ' out of ' ...
            num2str(d3_z_pix) ', ' num2str(nnztp) ...
            ' voxels inside the nodule. '   datestr(now())] ;
         disp(disp_string) ;
         clear nnztp disp_string ;
         term_3 = ((k - kkk_list) * dz_mm).^2 ;
         for j = 1:d3_long_pix
            term_2 = ((j - jjj_list) * dx_mm).^2 ;
            for i = 1:d3_trans_pix
               if (s_3d(i,j,k) == 1)
                  term_1 = ((i - iii_list) * dy_mm).^2 ;
                  dist_sq_vec = term_1 + term_2 + term_3 ;
                  dist_exterior_3d(i, j) = sqrt(min(dist_sq_vec)) ;
                  d3_summed_dist = d3_summed_dist + dist_exterior_3d(i, j) ;
               end
            end
         end
         count = fwrite(fid_3d, dist_exterior_3d, 'single') ;
      end
      clear term_1 term_2 dist_sq_vec dist_exterior_3d iii_list jjj_list kkk_list i j k ;
      d3_mean_dist = d3_summed_dist / d3_nodule_voxels ;
      d3_norm_summed_dist = d3_summed_dist / (d3_nodule_volume^(1/3)) ;
      d3_norm_mean_dist = d3_mean_dist / (d3_nodule_volume^(1/3)) ;

      fclose(fid_3d) ;
      clear fid_3d ;
      disp('      Finished calculating distance to surface.') ;
      
   else
      
      disp('      Deliberately skipping calculating distance to surface ...') ;
      d3_summed_dist      = -9999.0 ;
      d3_mean_dist        = -9999.0 ;
      d3_norm_summed_dist = -9999.0 ;
      d3_norm_mean_dist   = -9999.0 ;
      disp('      Finished deliberately skipping calculating distance to surface.') ;

   end

   % Calculate the fractal dimension of the 3D volume.

   disp('      Calculating fractal dimension of the volume ...') ;
   [n_temp_vol_3d, r_temp_vol_3d] = boxcount(s_3d, 'slope') ;
   s_temp_vol_3d = -gradient(log(n_temp_vol_3d)) ./ gradient(log(r_temp_vol_3d)) ;
   d3_fractal_vol = median(s_temp_vol_3d) ;
   pause(5) ;
   close ;
   % clear n_temp_vol_3d r_temp_vol_3d s_temp_vol_3d ;
   disp('      Finished calculating fractal dimension of the volume.') ;

   % Calculate the fractal dimension of the border surface of the 3D volume.

   disp('      Calculating fractal dimension of the border surface of the volume ...') ;
   if (fast_only ~= 0)
      disp('         Finding border voxels ...') ;
      kern = ones(3,3,3) ;
      border_3d = sign((1 - s_3d) .* convn(s_3d, kern, 'same')) ;
      clear kern ;
      disp('         Finished finding border voxels.') ;
   end
   [n_temp_surf_3d, r_temp_surf_3d] = boxcount(border_3d, 'slope') ;
   s_temp_surf_3d = -gradient(log(n_temp_surf_3d)) ./ gradient(log(r_temp_surf_3d)) ;
   d3_fractal_surf = median(s_temp_surf_3d) ;
   pause(5) ;
   close ;
   % clear n_temp_surf_3d r_temp_surf_3d s_temp_surf_3d ;
   disp('      Finished calculating fractal dimension of the border surface of the volume.') ;

   % Calculate volume versus threshold.

   disp('      Calculating volume versus threshold ...') ;
   thresh_3d_vec = -1025:25:1000 ;
   d3_nodule_volume_vec = zeros(size(thresh_3d_vec)) ;
   for index = 1:length(thresh_3d_vec)
      d3_nodule_volume_vec(index) = length(find(g_3d(index_inside_3d) > thresh_3d_vec(index))) * dx_mm * dy_mm * dz_mm ;
   end
   clear index ;
   disp('      Finished calculating volume versus threshold.') ;

   % Calculate gradient margin metric.

   disp('      Calculating gradient margin metric ...') ;
   [iii, jjj, kkk] = ind2sub(size(s_3d), find(border_3d > 0)) ;
   temp_sum = 0 ;
   for mmm = 1:length(iii)
      temp_sum = temp_sum + ...
         (((g_3d(iii(mmm) + 1, jjj(mmm),     kkk(mmm))         ...
          - g_3d(iii(mmm),     jjj(mmm),     kkk(mmm)    ))^2) ...
        + ((g_3d(iii(mmm),     jjj(mmm),     kkk(mmm))         ...
          - g_3d(iii(mmm) - 1, jjj(mmm),     kkk(mmm)    ))^2) ...
        + ((g_3d(iii(mmm),     jjj(mmm) + 1, kkk(mmm))         ...
          - g_3d(iii(mmm),     jjj(mmm),     kkk(mmm)    ))^2) ...
        + ((g_3d(iii(mmm),     jjj(mmm),     kkk(mmm))         ...
          - g_3d(iii(mmm),     jjj(mmm) - 1, kkk(mmm)    ))^2) ...
        + ((g_3d(iii(mmm),     jjj(mmm),     kkk(mmm) + 1)     ...
          - g_3d(iii(mmm),     jjj(mmm),     kkk(mmm)    ))^2) ...
        + ((g_3d(iii(mmm),     jjj(mmm),     kkk(mmm))         ...
          - g_3d(iii(mmm),     jjj(mmm),     kkk(mmm) - 1))^2)) ;
   end
   d3_grad_margin = sqrt(temp_sum / (length(iii) * 6)) ;
   disp('      Finished calculating gradient margin metric.') ;

   disp('   Finished calculating margin metrics.') ;

   disp('FINISHED CALCULATING 3-D METRICS.') ;




   %**************************************************************************
   %**************************************************************************
   %**************************************************************************
   % CALCULATE 2-D METRICS
   %**************************************************************************
   %**************************************************************************
   %**************************************************************************

   disp('CALCULATING 2-D METRICS ...') ;

   max_inside_2d = 0 ;
   d2_plane = 0 ;
   for i = 1:d3_z_pix
      test = sum(sum(s_3d(:,:,i))) ;
      if (test > max_inside_2d)
         max_inside_2d = test ;
         d2_plane = i ;
      end
   end
   s_2d = s_3d(:,:,d2_plane) ;
   g_2d = g_3d(:,:,d2_plane) ;
   clear max_inside_2d i test ;

   %**************************************************************************
   % Calculate size metrics ...') ;
   %**************************************************************************

   disp('   Calculating size metrics ...') ;

   % Calculate the area.

   disp('      Calculating area ...') ;
   d2_nodule_pixels = sum(sum(s_2d)) ;
   d2_nodule_area = d2_nodule_pixels * dx_mm * dy_mm ;
   disp('      Finished calculating area.') ;

   % Calculate a bounding box.

   disp('      Calculating bounding box ...') ;
   [iii_list, jjj_list] = ind2sub(size(s_2d), find(s_2d > 0)) ;
   d2_bounding_box = [min(iii_list) min(jjj_list) ; ...
                      max(iii_list) max(jjj_list)] ;
   d2_bounding_x_mm = (d2_bounding_box(2, 2) - d2_bounding_box(1, 2)) * dx_mm ;
   d2_bounding_y_mm = (d2_bounding_box(2, 1) - d2_bounding_box(1, 1)) * dy_mm ;
   clear iii_list jjj_list ;
   disp('      Finished calculating bounding box.') ;

   % Calculate the perimeter.

   disp('      Calculating perimeter ...') ;
   type_b_edges_2d = sum(sum(abs(s_2d(:,1:(end-1)) - s_2d(:,2:end)))) ;
   type_c_edges_2d = sum(sum(abs(s_2d(1:(end-1),:) - s_2d(2:end,:)))) ;

   d2_perim_mm = (type_b_edges_2d * dx_mm) ...
               + (type_c_edges_2d * dy_mm) ;
   disp('      Finished calculating perimeter.') ;

   disp('   Finished calculating size metrics.') ;

   %**************************************************************************
   % Calculate shape metrics.
   %**************************************************************************

   disp('   Calculating shape metrics ...') ;

   % Calculate the circularity (McNitt-Gray's definition).

   disp('      Calculating circularity ...') ;
   d2_circularity = (4 * pi * d2_nodule_area) / (d2_perim_mm^2) ;
   disp('      Finished calculating circularity.') ;

   % Calculate the principal rotational moments.
   % This calculation uses the binary segmentation image.
   % See http://en.wikipedia.org/Moment_of_inertia for a definition of the
   % tensor matrix that is calculated (modified for 2D).
   % The tensor matrix is then diagonalized using a singular-value
   % decomposition (SVD) and normalized to have unit energy so that it is
   % independent of size.

   disp('      Calculating principal rotational moments ...') ;

   % Find the centroid. This will be an ordered pair.

   x = 1:d3_long_pix ;
   y = 1:d3_trans_pix ;
   [X, Y] = meshgrid(x, y) ;
   x_c = sum(sum(s_2d .* X)) / d2_nodule_pixels ;
   y_c = sum(sum(s_2d .* Y)) / d2_nodule_pixels ;
   clear X Y ;

   x_shift = x - x_c ;
   y_shift = y - y_c ;
   [X_shift, Y_shift] = meshgrid(x_shift, y_shift) ;
   clear x y x_c y_c x_shift y_shift ;

   x_sq_term_2d = sum(sum((s_2d .* X_shift).^2)) / d2_nodule_pixels ;
   y_sq_term_2d = sum(sum((s_2d .* Y_shift).^2)) / d2_nodule_pixels ;

   xy_term_2d   = sum(sum((s_2d .* X_shift) .* (s_2d .* Y_shift))) / d2_nodule_pixels ;

   clear X_shift Y_shift Z_shift ;

   % Define the tensor as shown on the wikipedia web page, but modified for
   % 2D.

   I11_2d = y_sq_term_2d ;
   I22_2d = x_sq_term_2d ;

   I12_2d = xy_term_2d ;
   I21_2d = I12_2d ;

   clear x_sq_term_2d y_sq_term_2d xy_term_2d ;

   I_matrix_2d = [ I11_2d -I12_2d ; ...
                  -I21_2d  I22_2d] ;
   clear I11_2d I12_2d I21_2d I22_2d ;

   % Diagonalize it. The r_i_2d are the answers we seek.

   [U, S, V] = svd(I_matrix_2d) ;
   norm_fac = sqrt(sum(diag(S) .^ 2)) ;
   d2_r1 = S(1,1) / norm_fac ;
   d2_r2 = S(2,2) / norm_fac ;
   clear norm_fac U S V I_matrix_2d ;

   d2_moment_ratio = d2_r1 / d2_r2 ;

   disp('      Finished calculating principal rotational moments.') ;

   disp('   Finished calculating shape metrics.') ;

   %**************************************************************************
   % Calculate attenuation value metrics.
   %**************************************************************************

   disp('   Calculating attenuation value metrics ...') ;
   index_inside_2d  = find(s_2d == 1) ;
   disp('      Calculating median of attenuation values ...') ;
   d2_median_atten   = median(g_2d(index_inside_2d)) ;
   disp('      Finished calculating median of attenuation values.') ;
   disp('      Calculating mean of attenuation values ...') ;
   d2_mean_atten     = mean(g_2d(index_inside_2d)) ;
   disp('      Finished calculating mean of attenuation values.') ;
   disp('      Calculating standard deviation of attenuation values ...') ;
   d2_stdev_atten      = std(g_2d(index_inside_2d))  ;
   disp('      Finished calculating standard deviation of attenuation values.') ;
   disp('      Calculating variance of attenuation values ...') ;
   d2_var_atten      = var(g_2d(index_inside_2d))  ;
   disp('      Finished calculating variance of attenuation values.') ;
   disp('      Calculating skewness of attenuation values ...') ;
   d2_skew_atten = skewness(g_2d(index_inside_2d)) ;
   disp('      Finished calculating skewness of attenuation values.') ;
   disp('      Calculating kurtosis of attenuation values ...') ;
   d2_kurt_atten = kurtosis(g_2d(index_inside_2d)) ;
   disp('      Finished calculating kurtosis of attenuation values.') ;
   disp('      Calculating entropy of attenuation values ...') ;
   d2_entropy_atten  = entropy(g_2d(index_inside_2d)) ;
   disp('      Finished calculating entropy of attenuation values.') ;

   disp('   Finished calculating attenuation value metrics.') ;

   %**************************************************************************
   % Calculate texture metrics.
   % Start by differencing the grayscale images with nearest neighbors.
   %**************************************************************************

   disp('   Calculating texture metrics ...') ;
   kern = zeros(3, 3) ;
   q = -1/4 ;
   kern = [0 q 0 ; ...
           q 1 q ; ...
           0 q 0] ;
   g_2d_diff = convn(g_2d, kern, 'same') ;
   disp('      Calculating mean of difference images ...') ;
   d2_mean_diff     = mean(g_2d_diff(index_inside_2d)) ;
   disp('      Finished calculating mean of difference images.') ;
   disp('      Calculating standard deviation of difference images ...') ;
   d2_stdev_diff      = std(g_2d_diff(index_inside_2d)) ;
   disp('      Finished calculating standard deviation of difference images.') ;
   disp('      Calculating variance of difference images ...') ;
   d2_var_diff      = var(g_2d_diff(index_inside_2d)) ;
   disp('      Finished calculating variance of difference images.') ;
   disp('      Calculating skewness of difference images ...') ;
   d2_skew_diff = skewness(g_2d_diff(index_inside_2d)) ;
   disp('      Finished calculating skewness of difference images.') ;
   disp('      Calculating kurtosis of difference images ...') ;
   d2_kurt_diff = kurtosis(g_2d_diff(index_inside_2d)) ;
   disp('      Finished calculating kurtosis of difference images.') ;
   disp('      Calculating entropy of difference images ...') ;
   d2_entropy_diff  = entropy(g_2d_diff(index_inside_2d)) ;
   disp('      Finished calculating entropy of difference images.') ;
   disp('      Calculating correlation metric of difference images ...') ;
   disp('         PLACEHOLDER FOR 2D CORRELATION METRIC') ;
   d2_corr_diff = -9999.0 ;
   disp('      Finished calculating correlation metric of difference images.') ;
   disp('      Calculating lacunarity of segmentation images ...') ;
   s_2d_small = s_2d(d2_bounding_box(1,1):d2_bounding_box(2,1), ...
                     d2_bounding_box(1,2):d2_bounding_box(2,2)) ;
   [d2_temp_lacunarity, d2_box_size_lacunarity] = calc_lacunarity_2d(s_2d_small) ;
   d2_lacunarity_seg = zeros(1, 10) ;
   d2_lacunarity_seg(1:numel(d2_temp_lacunarity)) = d2_temp_lacunarity ;
   d2_lacunarity_seg(numel(d2_temp_lacunarity):end) = d2_temp_lacunarity(end) ;
   disp('      Finished calculating lacunarity of segmentation images.') ;
   disp(['Entering ngtdm: ' datestr(now)]) ;
   [d2_ak_coarseness_1, d2_ak_contrast_1, d2_ak_busyness_1, ...
      d2_ak_complexity_1, d2_ak_texture_strength_1] = ngtdm(g_2d, s_2d, 1) ;
   disp(['Finished with ngtdm: ' datestr(now)]) ;
   disp(['Entering ngtdm: ' datestr(now)]) ;
   [d2_ak_coarseness_2, d2_ak_contrast_2, d2_ak_busyness_2, ...
      d2_ak_complexity_2, d2_ak_texture_strength_2] = ngtdm(g_2d, s_2d, 2) ;
   disp(['Finished with ngtdm: ' datestr(now)]) ;
   clear g_2d_diff q kern ;
   disp('   Finished calculating texture metrics.') ;

   %**************************************************************************
   % Calculate margin metrics.
   %**************************************************************************

   disp('   Calculating margin metrics ...') ;

   % Calculate the mean distance to surface.

   disp('      Calculating distance to surface ...') ;
   dist_img_name_2d = [filename_root '_2d.dst'] ;
   fid_2d = fopen(dist_img_name_2d, 'w') ;

   disp('         Finding border voxels ...') ;
   kern = ones(3,3) ;
   border_2d = sign((1 - s_2d) .* convn(s_2d, kern, 'same')) ;
   clear kern ;
   [iii_list, jjj_list] = ind2sub(size(s_2d), find(border_2d > 0)) ;
   disp('         Finished finding border voxels.') ;

   disp('         Allocating 2D image for distance to surface ...') ;
   dist_exterior_2d = single(zeros(d3_trans_pix, d3_long_pix)) ;
   disp('         Finished allocating 2D image for distance to surface.') ;

   d2_summed_dist = 0.0 ;

   for j = 1:d3_long_pix
      term_2 = ((j - jjj_list) * dx_mm).^2 ;
      for i = 1:d3_trans_pix
         if (s_2d(i,j) == 1)
            term_1 = ((i - iii_list) * dy_mm).^2 ;
            dist_sq_vec = term_1 + term_2 ;
            dist_exterior_2d(i, j) = sqrt(min(dist_sq_vec)) ;
            d2_summed_dist = d2_summed_dist + dist_exterior_2d(i, j) ;
         end
      end
   end
   count = fwrite(fid_2d, dist_exterior_2d, 'single') ;
   clear count dist_sq_vec term_1 term_2 dist_exterior_2d iii_list jjj_list i j ;
   d2_mean_dist = d2_summed_dist / d2_nodule_pixels ;
   d2_norm_summed_dist = d2_summed_dist / (d2_nodule_area^(1/2)) ;
   d2_norm_mean_dist = d2_mean_dist / (d2_nodule_area^(1/2)) ;

   fclose(fid_2d) ;
   clear fid_2d ;
   disp('      Finished calculating distance to surface.') ;

   % Calculate the fractal dimension of the 2D area.

   disp('      Calculating fractal dimension of the area ...') ;
   [n_temp_area_2d, r_temp_area_2d] = boxcount(s_2d, 'slope') ;
   s_temp_area_2d = -gradient(log(n_temp_area_2d)) ./ gradient(log(r_temp_area_2d)) ;
   d2_fractal_area = median(s_temp_area_2d) ;
   pause(5) ;
   close ;
   % clear n_temp_area_2d r_temp_area_2d s_temp_area_2d ;
   disp('      Finished calculating fractal dimension of the area.') ;

   % Calculate the fractal dimension of the perimeter of the 2D area.

   disp('      Calculating fractal dimension of the perimeter of the area ...') ;
   [n_temp_perim_2d, r_temp_perim_2d] = boxcount(border_2d, 'slope') ;
   s_temp_perim_2d = -gradient(log(n_temp_perim_2d)) ./ gradient(log(r_temp_perim_2d)) ;
   d2_fractal_perim = median(s_temp_perim_2d) ;
   pause(5) ;
   close ;
   % clear n_temp_perim_2d r_temp_perim_2d s_temp_perim_2d ;
   disp('      Finished calculating fractal dimension of the perimeter of the area ...') ;

   % Calculate area versus threshold.

   disp('      Calculating area versus threshold ...') ;
   thresh_2d_vec = -1025:25:1000 ;
   d2_nodule_volume_vec = zeros(size(thresh_2d_vec)) ;
   for index = 1:length(thresh_2d_vec)
      d2_nodule_volume_vec(index) = length(find(g_2d(index_inside_2d) > thresh_2d_vec(index))) * dx_mm * dy_mm ;
   end
   clear index ;
   disp('      Finished calculating area versus threshold.') ;

   % Calculate gradient margin metric.

   disp('      Calculating gradient margin metric ...') ;
   [iii, jjj] = ind2sub(size(s_2d), find(border_2d > 0)) ;
   temp_sum = 0 ;
   for mmm = 1:length(iii)
      temp_sum = temp_sum + ...
         (((g_2d(iii(mmm) + 1, jjj(mmm)    )     - ...
            g_2d(iii(mmm),     jjj(mmm)    ))^2)   ...
        + ((g_2d(iii(mmm),     jjj(mmm)    )     - ...
            g_2d(iii(mmm) - 1, jjj(mmm)    ))^2)   ...
        + ((g_2d(iii(mmm),     jjj(mmm) + 1)       ...
          - g_2d(iii(mmm),     jjj(mmm)    ))^2)   ...
        + ((g_2d(iii(mmm),     jjj(mmm)    )       ...
          - g_2d(iii(mmm),     jjj(mmm) - 1))^2)) ;
   end
   d2_grad_margin = sqrt(temp_sum / (length(iii) * 4)) ;
   disp('      Finished calculating gradient margin metric.') ;

   disp('   Finished calculating margin metrics.') ;

   disp('FINISHED CALCULATING 2-D METRICS.') ;

   % Save the results in a Matlab ".mat" file. Clear the big arrays first.

   clear ans g_2d g_3d s_2d s_3d index_inside_2d index_inside_3d ;
   filename_mat  = [filename_root '.mat'] ;
   eval_string = ['save ''' filename_mat ''''] ;
   eval(eval_string) ;

   clear disp_string eval_string ;

   % Package the results in a nice dictionary for return.

   format long ;

   algo_stats = {};
   algo_stats{end+1} = {'Filename of grayscale image', grayscale_ana};
   algo_stats{end+1} = {'Filename of binary image', binary_ana};
   algo_stats{end+1} = {'Kernel' , kernel};
   algo_stats{end+1} = {'Delta d3_trans_pix (mm)', dy_mm};
   algo_stats{end+1} = {'Delta d3_long_pix (mm)',  num2str(dx_mm)};
   algo_stats{end+1} = {'Delta d3_z_pix (mm)', num2str(dz_mm)};
   algo_stats{end+1} = {'Height of input image (# rows)', num2str(d3_trans_pix)};
   algo_stats{end+1} = {'Width of input image (# columns)', num2str(d3_long_pix)};
   algo_stats{end+1} = {'Depth of input image (# planes)', num2str(d3_z_pix)};
   algo_stats{end+1} = {'Segmentation Number', segment_num};
   algo_stats{end+1} = {'Segmentation Threshold', gray_seg_thresh};
   algo_stats{end+1} = {'Lung nodule analysis (lna) version' , lna_version};


   disp(['****************************** STATISTICS FOR 3D ******************************']) ;
   metrics_3d = {};
   % disp(['********** Size Metrics **********']) ;
   metrics_3d{end+1} = {'Number of voxels inside', d3_nodule_voxels};
   metrics_3d{end+1} = {'Volume', d3_nodule_volume};
   metrics_3d{end+1} = {'Span of nodule in x direction (mm)', d3_bounding_x_mm};
   metrics_3d{end+1} = {'Span of nodule in y direction (mm)', d3_bounding_y_mm};
   metrics_3d{end+1} = {'Span of nodule in z direction (mm)', d3_bounding_z_mm};
   metrics_3d{end+1} = {'Surface Area', d3_surface_area_mm2};

   % disp(['********** Shape Metrics **********']) ; 
   metrics_3d{end+1} = {'Sphericity', d3_sphericity};
   metrics_3d{end+1} = {'Compactness', d3_compactness};
   metrics_3d{end+1} = {'Rotational moment, r_1', d3_r1};
   metrics_3d{end+1} = {'Rotational moment, r_2', d3_r2};
   metrics_3d{end+1} = {'Rotational moment, r_3', d3_r3};
   metrics_3d{end+1} = {'Ratio of largest to smallest rotational moment', d3_moment_ratio};

   % disp(['********** Attenuation Metrics **********']) ;
   metrics_3d{end+1} = {'Median of HU inside nodule' , d3_median_atten};
   metrics_3d{end+1} = {'Mean of HU inside nodule' , d3_mean_atten};
   metrics_3d{end+1} = {'Standard Deviation of HU inside nodule' , d3_stdev_atten};
   metrics_3d{end+1} = {'Variance of HU inside nodule' , d3_var_atten};
   metrics_3d{end+1} = {'Skewness of HU inside nodule' , d3_skew_atten};
   metrics_3d{end+1} = {'Kurtosis of HU inside nodule' , d3_kurt_atten};
   metrics_3d{end+1} = {'Entropy of HU inside nodule' , d3_entropy_atten};

   % disp(['********** Texture Metrics **********']) ;
   metrics_3d{end+1} = {'Mean of difference image of HU inside nodule' , d3_mean_diff};
   metrics_3d{end+1} = {'Standard Deviation of difference image of HU inside nodule' , d3_stdev_diff};
   metrics_3d{end+1} = {'Variance of difference image of HU inside nodule' , d3_var_diff};
   metrics_3d{end+1} = {'Skewness of difference image of HU inside nodule' , d3_skew_diff};
   metrics_3d{end+1} = {'Kurtosis of difference image of HU inside nodule' , d3_kurt_diff};
   metrics_3d{end+1} = {'Entropy of difference image of HU inside nodule' , d3_entropy_diff};
   metrics_3d{end+1} = {'Correlation metric of difference image of HU inside nodule' , d3_corr_diff};
   metrics_3d{end+1} = {'Lacunarity of segmentation image' , d3_lacunarity_seg};
   metrics_3d{end+1} = {'Box size for lacunarity calculation' , d3_box_size_lacunarity};
   metrics_3d{end+1} = {'Coarseness (d = 1)' , d3_ak_coarseness_1};
   metrics_3d{end+1} = {'Contrast (d = 1)' , d3_ak_contrast_1};
   metrics_3d{end+1} = {'Busyness (d = 1)' , d3_ak_busyness_1};
   metrics_3d{end+1} = {'Complexity (d = 1)' , d3_ak_complexity_1};
   metrics_3d{end+1} = {'Texture Strength (d = 1)' , d3_ak_texture_strength_1};
   metrics_3d{end+1} = {'Coarseness (d = 2)' , d3_ak_coarseness_2};
   metrics_3d{end+1} = {'Contrast (d = 2)' , d3_ak_contrast_2};
   metrics_3d{end+1} = {'Busyness (d = 2)' , d3_ak_busyness_2};
   metrics_3d{end+1} = {'Complexity (d = 2)' , d3_ak_complexity_2};
   metrics_3d{end+1} = {'Texture Strength (d = 2)' , d3_ak_texture_strength_2};

   % disp(['********** Margin Metrics **********']) ;
   metrics_3d{end+1} = {'Summed distance' , d3_summed_dist};
   metrics_3d{end+1} = {'Mean distance' , d3_mean_dist};
   metrics_3d{end+1} = {'Normalized summed distance' , d3_norm_summed_dist};
   metrics_3d{end+1} = {'Normalized mean distance' , d3_norm_mean_dist};
   metrics_3d{end+1} = {'Fractal dimension of volume' , d3_fractal_vol};
   metrics_3d{end+1} = {'Fractal dimension of surface' , d3_fractal_surf};
   metrics_3d{end+1} = {'cum_volume_thresh', thresh_3d_vec};
   metrics_3d{end+1} = {'cum_volume_value', d3_nodule_volume_vec};
   metrics_3d{end+1} = {'Gradient margin metric', d3_grad_margin};


   % disp(['****************************** STATISTICS FOR 2D ******************************']) ;
   metrics_2d = {};
   metrics_2d{end+1} = {'Transverse plane with maximal area' , d2_plane};
   % disp(['********** Size Metrics **********']) ;
   metrics_2d{end+1} = {'Number of pixels inside' , d2_nodule_pixels};
   metrics_2d{end+1} = {'Area' , d2_nodule_area};
   metrics_2d{end+1} = {'Span of nodule in x direction (mm)' , d2_bounding_x_mm};
   metrics_2d{end+1} = {'Span of nodule in y direction (mm)' , d2_bounding_y_mm};
   metrics_2d{end+1} = {'Perimeter' , d2_perim_mm};
   % disp(['********** Shape Metrics **********']) ;
   metrics_2d{end+1} = {'Circularity' , d2_circularity};
   metrics_2d{end+1} = {'Rotational moment, r_1' , d2_r1};
   metrics_2d{end+1} = {'Rotational moment, r_2' , d2_r2};
   metrics_2d{end+1} = {'Ratio of largest to smallest rotational moment' , d2_moment_ratio};
   % disp(['********** Attenuation Metrics **********']) ;
   metrics_2d{end+1} = {'Median of HU inside nodule' , d2_median_atten};
   metrics_2d{end+1} = {'Mean of HU inside nodule' , d2_mean_atten};
   metrics_2d{end+1} = {'Standard Deviation of HU inside nodule' , d2_stdev_atten};
   metrics_2d{end+1} = {'Variance of HU inside nodule' , d2_var_atten};
   metrics_2d{end+1} = {'Skewness of HU inside nodule' , d2_skew_atten};
   metrics_2d{end+1} = {'Kurtosis of HU inside nodule' , d2_kurt_atten};
   metrics_2d{end+1} = {'Entropy of HU inside nodule' , d2_entropy_atten};
   % disp(['********** Texture Metrics **********']) ;
   metrics_2d{end+1} = {'Mean of difference image of HU inside nodule' , d2_mean_diff};
   metrics_2d{end+1} = {'Standard Deviation of difference image of HU inside nodule' , d2_stdev_diff};
   metrics_2d{end+1} = {'Variance of difference image of HU inside nodule' , d2_var_diff};
   metrics_2d{end+1} = {'Skewness of difference image of HU inside nodule' , d2_skew_diff};
   metrics_2d{end+1} = {'Kurtosis of difference image of HU inside nodule' , d2_kurt_diff};
   metrics_2d{end+1} = {'Entropy of difference image of HU inside nodule' , d2_entropy_diff};
   metrics_2d{end+1} = {'Correlation metric of difference image of HU inside nodule' , d2_corr_diff};
   metrics_2d{end+1} = {'Lacunarity of segmentation image' , d2_lacunarity_seg};
   metrics_2d{end+1} = {'Box size for lacunarity calculation' , d2_box_size_lacunarity};
   metrics_2d{end+1} = {'Coarseness (d = 1)' , d2_ak_coarseness_1};
   metrics_2d{end+1} = {'Contrast (d = 1)' , d2_ak_contrast_1};
   metrics_2d{end+1} = {'Busyness (d = 1)' , d2_ak_busyness_1};
   metrics_2d{end+1} = {'Complexity (d = 1)' , d2_ak_complexity_1};
   metrics_2d{end+1} = {'Texture Strength (d = 1)' , d2_ak_texture_strength_1};
   metrics_2d{end+1} = {'Coarseness (d = 2)' , d2_ak_coarseness_2};
   metrics_2d{end+1} = {'Contrast (d = 2)' , d2_ak_contrast_2};
   metrics_2d{end+1} = {'Busyness (d = 2)' , d2_ak_busyness_2};
   metrics_2d{end+1} = {'Complexity (d = 2)' , d2_ak_complexity_2};
   metrics_2d{end+1} = {'Texture Strength (d = 2)' , d2_ak_texture_strength_2};
   % disp(['********** Margin Metrics **********']) ;
   metrics_2d{end+1} = {'Summed distance' , d2_summed_dist};
   metrics_2d{end+1} = {'Mean distance' , d2_mean_dist};
   metrics_2d{end+1} = {'Normalized summed distance' , d2_norm_summed_dist};
   metrics_2d{end+1} = {'Normalized mean distance' , d2_norm_mean_dist};
   metrics_2d{end+1} = {'Fractal dimension of area' , d2_fractal_area};
   metrics_2d{end+1} = {'Fractal dimension of perimeter' , d2_fractal_perim};
   metrics_2d{end+1} = {'cum_volume_thresh', thresh_2d_vec };
   metrics_2d{end+1} = {'cum volume_value', d2_nodule_volume_vec };
   metrics_2d{end+1} = {'Gradient margin metric', d2_grad_margin };

   diary off ;

   disp('Exiting lna01_4') ;
   toc ;

endfunction