diff --git a/CMakeLists.txt b/CMakeLists.txt index ff5d8cd6..2106401b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,7 +130,6 @@ set(CMAKE_CXX_FLAGS_RELEASE "-O2") #== GTest option(RUN_GTEST "Downloads google unit test API and runs google test scripts to test Nyxus" OFF) - #==== Source files set(SOURCE src/nyx/arrow_output_stream.cpp @@ -166,6 +165,8 @@ set(SOURCE src/nyx/features/2d_geomoments_basic_nt.cpp src/nyx/features/3d_intensity.cpp src/nyx/features/3d_gldzm.cpp + src/nyx/features/3d_glcm.cpp + src/nyx/features/3d_glcm_nontriv.cpp src/nyx/features/intensity.cpp src/nyx/features/neighbors.cpp src/nyx/features/ngldm.cpp diff --git a/src/nyx/env_features.cpp b/src/nyx/env_features.cpp index eca99af7..905528d6 100644 --- a/src/nyx/env_features.cpp +++ b/src/nyx/env_features.cpp @@ -32,6 +32,7 @@ #include "features/neighbors.h" #include "features/ngldm.h" #include "features/ngtdm.h" +#include "features/3d_glcm.h" #include "features/roi_radius.h" #include "helpers/helpers.h" #include "helpers/system_resource.h" @@ -437,6 +438,14 @@ bool Environment::expand_3D_featuregroup (const std::string& s) return true; } + if (s == Nyxus::theFeatureSet.findGroupNameByCode(Fgroup3D::FG3_GLCM)) + { + theFeatureSet.enableAll (false); + + theFeatureSet.enableFeatures (D3_GLCM_feature::featureset); + return true; + } + if (s == Nyxus::theFeatureSet.findGroupNameByCode(Fgroup3D::FG3_GLDZM)) { theFeatureSet.enableAll(false); @@ -467,8 +476,6 @@ bool Environment::expand_3D_featuregroup (const std::string& s) return true; } - - return false; } diff --git a/src/nyx/environment.cpp b/src/nyx/environment.cpp index 60bb5fb5..bf763bf5 100644 --- a/src/nyx/environment.cpp +++ b/src/nyx/environment.cpp @@ -801,7 +801,7 @@ bool Environment::parse_cmdline(int argc, char** argv) } // -- check if any feature is enablesd as a result of expanding user's choice - if (theFeatureSet.numOfEnabled() == 0) + if (theFeatureSet.numOfEnabled(dim()) == 0) { std::cerr << "Error: no features are selected. Stopping \n"; return false; diff --git a/src/nyx/feature_mgr_init.cpp b/src/nyx/feature_mgr_init.cpp index e3ca95fa..a3b105f0 100644 --- a/src/nyx/feature_mgr_init.cpp +++ b/src/nyx/feature_mgr_init.cpp @@ -29,6 +29,7 @@ #include "features/zernike.h" #include "features/3d_intensity.h" +#include "features/3d_glcm.h" #include "features/3d_gldzm.h" #include "features/focus_score.h" @@ -71,6 +72,7 @@ FeatureManager::FeatureManager() register_feature (new RadialDistributionFeature()); // 3D register_feature (new D3_PixelIntensityFeatures()); + register_feature (new D3_GLCM_feature()); register_feature (new D3_GLDZM_feature()); // image quality diff --git a/src/nyx/features/3d_glcm.cpp b/src/nyx/features/3d_glcm.cpp new file mode 100644 index 00000000..fd13c0f2 --- /dev/null +++ b/src/nyx/features/3d_glcm.cpp @@ -0,0 +1,1255 @@ +#include +#include "3d_glcm.h" +#include "../helpers/helpers.h" +#include "../environment.h" + +using namespace Nyxus; + +int D3_GLCM_feature::offset = 1; +int D3_GLCM_feature::n_levels = 0; +bool D3_GLCM_feature::symmetric_glcm = false; +std::vector D3_GLCM_feature::angles = { 0, 45, 90, 135 }; + +D3_GLCM_feature::D3_GLCM_feature() : FeatureMethod("D3_GLCM_feature") +{ + provide_features (D3_GLCM_feature::featureset); +} + +void D3_GLCM_feature::calculate (LR& r) +{ + // clear the feature values buffers + clear_result_buffers(); + + int w = r.aux_image_cube.width(), + h = r.aux_image_cube.height(), + d = r.aux_image_cube.depth(); + + // -- grey-bin intensities + SimpleCube D; + D.allocate (w, h, d); + + auto greyInfo = Nyxus::theEnvironment.get_coarse_gray_depth(); + auto greyInfo_localFeature = D3_GLCM_feature::n_levels; + if (greyInfo_localFeature != 0 && greyInfo != greyInfo_localFeature) + greyInfo = greyInfo_localFeature; + if (Nyxus::theEnvironment.ibsi_compliance) + greyInfo = 0; + + bin_intensities_3d (D, r.aux_image_cube, r.aux_min, r.aux_max, greyInfo); + + // calculate features for all the directions + for (auto a : D3_GLCM_feature::angles) + extract_texture_features_at_angle (a, D, r.aux_min, r.aux_max); +} + +void D3_GLCM_feature::clear_result_buffers() +{ + fvals_ASM.clear(); + fvals_acor.clear(); + fvals_cluprom.clear(); + fvals_clushade.clear(); + fvals_clutend.clear(); + fvals_contrast.clear(); + fvals_correlation.clear(); + fvals_diff_avg.clear(); + fvals_diff_var.clear(); + fvals_diff_entropy.clear(); + fvals_dis.clear(); + fvals_energy.clear(); + fvals_entropy.clear(); + fvals_homo.clear(); + fvals_hom2.clear(); + fvals_id.clear(); + fvals_idn.clear(); + fvals_IDM.clear(); + fvals_idmn.clear(); + fvals_meas_corr1.clear(); + fvals_meas_corr2.clear(); + fvals_iv.clear(); + fvals_jave.clear(); + fvals_je.clear(); + fvals_jmax.clear(); + fvals_jvar.clear(); + fvals_sum_avg.clear(); + fvals_sum_var.clear(); + fvals_sum_entropy.clear(); + fvals_variance.clear(); +} + +void D3_GLCM_feature::osized_add_online_pixel(size_t x, size_t y, uint32_t intensity) {} // Not supporting the online mode for this feature method + +void D3_GLCM_feature::copyfvals(AngledFeatures& dst, const AngledFeatures& src) +{ + dst.assign(src.begin(), src.end()); +} + +void D3_GLCM_feature::save_value(std::vector>& fvals) +{ + copyfvals(fvals[(int)Feature3D::GLCM_ASM], fvals_ASM); + + copyfvals(fvals[(int)Feature3D::GLCM_ACOR], fvals_acor); + copyfvals(fvals[(int)Feature3D::GLCM_ACOR], fvals_acor); + + copyfvals(fvals[(int)Feature3D::GLCM_CLUPROM], fvals_cluprom); + copyfvals(fvals[(int)Feature3D::GLCM_CLUSHADE], fvals_clushade); + copyfvals(fvals[(int)Feature3D::GLCM_CLUTEND], fvals_clutend); + copyfvals(fvals[(int)Feature3D::GLCM_CONTRAST], fvals_contrast); + copyfvals(fvals[(int)Feature3D::GLCM_CORRELATION], fvals_correlation); + copyfvals(fvals[(int)Feature3D::GLCM_DIFAVE], fvals_diff_avg); + copyfvals(fvals[(int)Feature3D::GLCM_DIFVAR], fvals_diff_var); + copyfvals(fvals[(int)Feature3D::GLCM_DIFENTRO], fvals_diff_entropy); + copyfvals(fvals[(int)Feature3D::GLCM_DIS], fvals_dis); + copyfvals(fvals[(int)Feature3D::GLCM_ENERGY], fvals_energy); + copyfvals(fvals[(int)Feature3D::GLCM_ENTROPY], fvals_entropy); + copyfvals(fvals[(int)Feature3D::GLCM_HOM1], fvals_homo); + copyfvals(fvals[(int)Feature3D::GLCM_HOM2], fvals_hom2); + copyfvals(fvals[(int)Feature3D::GLCM_ID], fvals_id); + copyfvals(fvals[(int)Feature3D::GLCM_IDN], fvals_idn); + copyfvals(fvals[(int)Feature3D::GLCM_IDM], fvals_IDM); + copyfvals(fvals[(int)Feature3D::GLCM_IDMN], fvals_idmn); + copyfvals(fvals[(int)Feature3D::GLCM_INFOMEAS1], fvals_meas_corr1); + copyfvals(fvals[(int)Feature3D::GLCM_INFOMEAS2], fvals_meas_corr2); + copyfvals(fvals[(int)Feature3D::GLCM_IV], fvals_iv); + copyfvals(fvals[(int)Feature3D::GLCM_JAVE], fvals_jave); + copyfvals(fvals[(int)Feature3D::GLCM_JE], fvals_je); + copyfvals(fvals[(int)Feature3D::GLCM_JMAX], fvals_jmax); + copyfvals(fvals[(int)Feature3D::GLCM_JVAR], fvals_jvar); + copyfvals(fvals[(int)Feature3D::GLCM_SUMAVERAGE], fvals_sum_avg); + copyfvals(fvals[(int)Feature3D::GLCM_SUMVARIANCE], fvals_sum_var); + copyfvals(fvals[(int)Feature3D::GLCM_SUMENTROPY], fvals_sum_entropy); + copyfvals(fvals[(int)Feature3D::GLCM_VARIANCE], fvals_variance); + + fvals[(int)Feature3D::GLCM_ASM_AVE][0] = calc_ave(fvals_ASM); + fvals[(int)Feature3D::GLCM_ACOR_AVE][0] = calc_ave(fvals_acor); + fvals[(int)Feature3D::GLCM_CLUPROM_AVE][0] = calc_ave(fvals_cluprom); + fvals[(int)Feature3D::GLCM_CLUSHADE_AVE][0] = calc_ave(fvals_clushade); + fvals[(int)Feature3D::GLCM_CLUTEND_AVE][0] = calc_ave(fvals_clutend); + fvals[(int)Feature3D::GLCM_CONTRAST_AVE][0] = calc_ave(fvals_contrast); + fvals[(int)Feature3D::GLCM_CORRELATION_AVE][0] = calc_ave(fvals_correlation); + fvals[(int)Feature3D::GLCM_DIFAVE_AVE][0] = calc_ave(fvals_diff_avg); + fvals[(int)Feature3D::GLCM_DIFVAR_AVE][0] = calc_ave(fvals_diff_var); + fvals[(int)Feature3D::GLCM_DIFENTRO_AVE][0] = calc_ave(fvals_diff_entropy); + fvals[(int)Feature3D::GLCM_DIS_AVE][0] = calc_ave(fvals_dis); + fvals[(int)Feature3D::GLCM_ENERGY_AVE][0] = calc_ave(fvals_energy); + fvals[(int)Feature3D::GLCM_ENTROPY_AVE][0] = calc_ave(fvals_entropy); + fvals[(int)Feature3D::GLCM_HOM1_AVE][0] = calc_ave(fvals_homo); + fvals[(int)Feature3D::GLCM_ID_AVE][0] = calc_ave(fvals_id); + fvals[(int)Feature3D::GLCM_IDN_AVE][0] = calc_ave(fvals_idn); + fvals[(int)Feature3D::GLCM_IDM_AVE][0] = calc_ave(fvals_IDM); + fvals[(int)Feature3D::GLCM_IDMN_AVE][0] = calc_ave(fvals_idmn); + fvals[(int)Feature3D::GLCM_IV_AVE][0] = calc_ave(fvals_iv); + fvals[(int)Feature3D::GLCM_JAVE_AVE][0] = calc_ave(fvals_jave); + fvals[(int)Feature3D::GLCM_JE_AVE][0] = calc_ave(fvals_je); + fvals[(int)Feature3D::GLCM_INFOMEAS1_AVE][0] = calc_ave(fvals_meas_corr1); + fvals[(int)Feature3D::GLCM_INFOMEAS2_AVE][0] = calc_ave(fvals_meas_corr2); + fvals[(int)Feature3D::GLCM_VARIANCE_AVE][0] = calc_ave(fvals_variance); + fvals[(int)Feature3D::GLCM_JMAX_AVE][0] = calc_ave(fvals_jmax); + fvals[(int)Feature3D::GLCM_JVAR_AVE][0] = calc_ave(fvals_jvar); + fvals[(int)Feature3D::GLCM_SUMAVERAGE_AVE][0] = calc_ave(fvals_sum_avg); + fvals[(int)Feature3D::GLCM_SUMVARIANCE_AVE][0] = calc_ave(fvals_sum_var); + fvals[(int)Feature3D::GLCM_SUMENTROPY_AVE][0] = calc_ave(fvals_sum_entropy); + fvals[(int)Feature3D::GLCM_VARIANCE_AVE][0] = calc_ave(fvals_variance); +} + +void D3_GLCM_feature::parallel_process_1_batch (size_t start, size_t end, std::vector* ptrLabels, std::unordered_map * ptrLabelData) +{ + for (auto i = start; i < end; i++) + { + int lab = (*ptrLabels)[i]; + LR& r = (*ptrLabelData)[lab]; + + // Skip calculation in case of bad data + + // + // !!! We need to smart-select the greyInfo rather than just theEnvironment.get_coarse_gray_depth() + // + auto binnedMin = bin_pixel(r.aux_min, r.aux_min, r.aux_max, theEnvironment.get_coarse_gray_depth()); + auto binnedMax = bin_pixel(r.aux_max, r.aux_min, r.aux_max, theEnvironment.get_coarse_gray_depth()); + if (binnedMin == binnedMax) + { + auto w = theEnvironment.nan_substitute; // safe NAN + + // assign it to each angled feature value + auto n = angles.size(); + r.fvals[(int)Feature3D::GLCM_ASM].assign(n, w); + r.fvals[(int)Feature3D::GLCM_ACOR].assign(n, w); + r.fvals[(int)Feature3D::GLCM_CLUPROM].assign(n, w); + r.fvals[(int)Feature3D::GLCM_CLUSHADE].assign(n, w); + r.fvals[(int)Feature3D::GLCM_CLUTEND].assign(n, w); + r.fvals[(int)Feature3D::GLCM_CONTRAST].assign(n, w); + r.fvals[(int)Feature3D::GLCM_CORRELATION].assign(n, w); + r.fvals[(int)Feature3D::GLCM_DIFAVE].assign(n, w); + r.fvals[(int)Feature3D::GLCM_DIFENTRO].assign(n, w); + r.fvals[(int)Feature3D::GLCM_DIFVAR].assign(n, w); + r.fvals[(int)Feature3D::GLCM_DIS].assign(n, w); + r.fvals[(int)Feature3D::GLCM_ENERGY].assign(n, w); + r.fvals[(int)Feature3D::GLCM_ENTROPY].assign(n, w); + r.fvals[(int)Feature3D::GLCM_HOM1].assign(n, w); + r.fvals[(int)Feature3D::GLCM_HOM2].assign(n, w); + r.fvals[(int)Feature3D::GLCM_IDMN].assign(n, w); + r.fvals[(int)Feature3D::GLCM_ID].assign(n, w); + r.fvals[(int)Feature3D::GLCM_IDN].assign(n, w); + r.fvals[(int)Feature3D::GLCM_INFOMEAS1].assign(n, w); + r.fvals[(int)Feature3D::GLCM_INFOMEAS2].assign(n, w); + r.fvals[(int)Feature3D::GLCM_IDM].assign(n, w); + r.fvals[(int)Feature3D::GLCM_IV].assign(n, w); + r.fvals[(int)Feature3D::GLCM_JAVE].assign(n, w); + r.fvals[(int)Feature3D::GLCM_JE].assign(n, w); + r.fvals[(int)Feature3D::GLCM_JMAX].assign(n, w); + r.fvals[(int)Feature3D::GLCM_JVAR].assign(n, w); + r.fvals[(int)Feature3D::GLCM_SUMAVERAGE].assign(n, w); + r.fvals[(int)Feature3D::GLCM_SUMENTROPY].assign(n, w); + r.fvals[(int)Feature3D::GLCM_SUMVARIANCE].assign(n, w); + r.fvals[(int)Feature3D::GLCM_VARIANCE].assign(n, w); + + r.fvals[(int)Feature3D::GLCM_ASM_AVE][0] = + r.fvals[(int)Feature3D::GLCM_ACOR_AVE][0] = + r.fvals[(int)Feature3D::GLCM_CLUPROM_AVE][0] = + r.fvals[(int)Feature3D::GLCM_CLUSHADE_AVE][0] = + r.fvals[(int)Feature3D::GLCM_CLUTEND_AVE][0] = + r.fvals[(int)Feature3D::GLCM_CONTRAST_AVE][0] = + r.fvals[(int)Feature3D::GLCM_CORRELATION_AVE][0] = + r.fvals[(int)Feature3D::GLCM_DIFAVE_AVE][0] = + r.fvals[(int)Feature3D::GLCM_DIFENTRO_AVE][0] = + r.fvals[(int)Feature3D::GLCM_DIFVAR_AVE][0] = + r.fvals[(int)Feature3D::GLCM_DIS_AVE][0] = + r.fvals[(int)Feature3D::GLCM_ENERGY_AVE][0] = + r.fvals[(int)Feature3D::GLCM_ENTROPY_AVE][0] = + r.fvals[(int)Feature3D::GLCM_HOM1_AVE][0] = + r.fvals[(int)Feature3D::GLCM_ID_AVE][0] = + r.fvals[(int)Feature3D::GLCM_IDN_AVE][0] = + r.fvals[(int)Feature3D::GLCM_IDM_AVE][0] = + r.fvals[(int)Feature3D::GLCM_IDMN_AVE][0] = + r.fvals[(int)Feature3D::GLCM_IV_AVE][0] = + r.fvals[(int)Feature3D::GLCM_JAVE_AVE][0] = + r.fvals[(int)Feature3D::GLCM_JE_AVE][0] = + r.fvals[(int)Feature3D::GLCM_INFOMEAS1_AVE][0] = + r.fvals[(int)Feature3D::GLCM_INFOMEAS2_AVE][0] = + r.fvals[(int)Feature3D::GLCM_JMAX_AVE][0] = + r.fvals[(int)Feature3D::GLCM_JVAR_AVE][0] = + r.fvals[(int)Feature3D::GLCM_SUMAVERAGE_AVE][0] = + r.fvals[(int)Feature3D::GLCM_SUMENTROPY_AVE][0] = + r.fvals[(int)Feature3D::GLCM_SUMVARIANCE_AVE][0] = + r.fvals[(int)Feature3D::GLCM_VARIANCE_AVE][0] = w; + + // No need to calculate features for this ROI + continue; + } + + D3_GLCM_feature f; + f.calculate(r); + f.save_value(r.fvals); + } +} + +void D3_GLCM_feature::extract_texture_features_at_angle (int angle, const SimpleCube & binned_greys, PixIntens min_val, PixIntens max_val) +{ + for (int kdz = 0; kdz <= 1; kdz++) + { + int dz = offset * kdz; + + // Compute the gray-tone spatial dependence matrix + int dx, dy; + switch (angle) + { + case 0: + dx = offset; + dy = 0; + break; + case 45: + dx = offset; + dy = offset; + break; + case 90: + dx = 0; + dy = offset; + break; + case 135: + dx = -offset; + dy = offset; + break; + default: + std::cerr << "Cannot create co-occurence matrix for angle " << angle << ": unsupported angle\n"; + return; + } + + calculateCoocMatAtAngle (P_matrix, dx, dy, dz, binned_greys, min_val, max_val); + + // Blank cooc-matrix? -- no point to use it, assign each feature value '0' and return. + if (sum_p == 0) + { + auto _ = theEnvironment.nan_substitute; // safe NAN + fvals_ASM.push_back(_); + fvals_acor.push_back(_); + fvals_cluprom.push_back(_); + fvals_clushade.push_back(_); + fvals_clutend.push_back(_); + fvals_contrast.push_back(_); + fvals_correlation.push_back(_); + fvals_diff_avg.push_back(_); + fvals_diff_var.push_back(_); + fvals_diff_entropy.push_back(_); + fvals_dis.push_back(_); + fvals_energy.push_back(_); + fvals_entropy.push_back(_); + fvals_homo.push_back(_); + fvals_hom2.push_back(_); + fvals_id.push_back(_); + fvals_idn.push_back(_); + fvals_IDM.push_back(_); + fvals_idmn.push_back(_); + fvals_meas_corr1.push_back(_); + fvals_meas_corr2.push_back(_); + fvals_iv.push_back(_); + fvals_jave.push_back(_); + fvals_je.push_back(_); + fvals_jmax.push_back(_); + fvals_jvar.push_back(_); + fvals_sum_avg.push_back(_); + fvals_sum_var.push_back(_); + fvals_sum_entropy.push_back(_); + fvals_variance.push_back(_); + return; + } + + // Output - 'Pxpy' (meaning: 'x plus y') and 'Pxmy' (meaning: 'x minus y') + calculatePxpmy(); + + // Calculate by-row mean. (Output - 'by_row_mean') + calculate_by_row_mean(); + + // Compute Haralick statistics + double f; + f = theFeatureSet.isEnabled(Feature3D::GLCM_ASM) ? f_asm(P_matrix) : 0.0; + fvals_ASM.push_back(f); + + f = theFeatureSet.isEnabled(Feature3D::GLCM_CONTRAST) ? f_contrast(P_matrix) : 0.0; + fvals_contrast.push_back(f); + + f = theFeatureSet.isEnabled(Feature3D::GLCM_CORRELATION) ? f_corr() : 0.0; + fvals_correlation.push_back(f); + + f = theFeatureSet.isEnabled(Feature3D::GLCM_ENERGY) ? f_energy(P_matrix) : 0.0; + fvals_energy.push_back(f); + + f = theFeatureSet.isEnabled(Feature3D::GLCM_HOM1) ? f_homogeneity() : 0.0; + fvals_homo.push_back(f); + + f = theFeatureSet.isEnabled(Feature3D::GLCM_VARIANCE) ? f_var(P_matrix) : 0.0; + fvals_variance.push_back(f); + + f = theFeatureSet.isEnabled(Feature3D::GLCM_IDM) ? f_idm() : 0.0; + fvals_IDM.push_back(f); + + f = theFeatureSet.isEnabled(Feature3D::GLCM_SUMAVERAGE) ? f_savg() : 0.0; + fvals_sum_avg.push_back(f); + + f = theFeatureSet.isEnabled(Feature3D::GLCM_SUMENTROPY) ? f_sentropy() : 0.0; + fvals_sum_entropy.push_back(f); + + f = theFeatureSet.isEnabled(Feature3D::GLCM_ENTROPY) ? f_entropy(P_matrix) : 0.0; + fvals_entropy.push_back(f); + + f = theFeatureSet.isEnabled(Feature3D::GLCM_DIFVAR) ? f_dvar(P_matrix) : 0.0; + fvals_diff_var.push_back(f); + + f = theFeatureSet.isEnabled(Feature3D::GLCM_DIFENTRO) ? f_dentropy(P_matrix) : 0.0; + fvals_diff_entropy.push_back(f); + + f = theFeatureSet.isEnabled(Feature3D::GLCM_DIFAVE) ? f_difference_avg() : 0.0; + fvals_diff_avg.push_back(f); + + f = theFeatureSet.isEnabled(Feature3D::GLCM_INFOMEAS1) ? f_info_meas_corr1(P_matrix) : 0.0; + fvals_meas_corr1.push_back(f); + + f = theFeatureSet.isEnabled(Feature3D::GLCM_INFOMEAS2) ? f_info_meas_corr2(P_matrix) : 0.0; + fvals_meas_corr2.push_back(f); + + f = !theFeatureSet.isEnabled(Feature3D::GLCM_ACOR) ? 0 : f_GLCM_ACOR(P_matrix); + fvals_acor.push_back(f); + + f = !theFeatureSet.isEnabled(Feature3D::GLCM_CLUPROM) ? 0 : f_GLCM_CLUPROM(); + fvals_cluprom.push_back(f); + + f = !theFeatureSet.isEnabled(Feature3D::GLCM_CLUSHADE) ? 0 : f_GLCM_CLUSHADE(); + fvals_clushade.push_back(f); + + // 'cluster tendency' is equivalent to 'sum variance', so calculate it once + f = (theFeatureSet.isEnabled(Feature3D::GLCM_CLUTEND) || theFeatureSet.isEnabled(Feature3D::GLCM_SUMVARIANCE)) ? f_GLCM_CLUTEND() : 0.0; + fvals_clutend.push_back(f); + fvals_sum_var.push_back(f); + + f = !theFeatureSet.isEnabled(Feature3D::GLCM_DIS) ? 0 : f_GLCM_DIS(P_matrix); + fvals_dis.push_back(f); + + f = !theFeatureSet.isEnabled(Feature3D::GLCM_HOM2) ? 0 : f_GLCM_HOM2(P_matrix); + fvals_hom2.push_back(f); + + f = !theFeatureSet.isEnabled(Feature3D::GLCM_IDMN) ? 0 : f_GLCM_IDMN(); + fvals_idmn.push_back(f); + + f = !theFeatureSet.isEnabled(Feature3D::GLCM_ID) ? 0 : f_GLCM_ID(); + fvals_id.push_back(f); + + f = !theFeatureSet.isEnabled(Feature3D::GLCM_IDN) ? 0 : f_GLCM_IDN(); + fvals_idn.push_back(f); + + f = !theFeatureSet.isEnabled(Feature3D::GLCM_IV) ? 0 : f_GLCM_IV(); + fvals_iv.push_back(f); + + f = !theFeatureSet.isEnabled(Feature3D::GLCM_JAVE) ? 0 : f_GLCM_JAVE(); + fvals_jave.push_back(f); + auto jave = f; + + f = !theFeatureSet.isEnabled(Feature3D::GLCM_JE) ? 0 : f_GLCM_JE(P_matrix); + fvals_je.push_back(f); + + f = !theFeatureSet.isEnabled(Feature3D::GLCM_JMAX) ? 0 : f_GLCM_JMAX(P_matrix); + fvals_jmax.push_back(f); + + f = !theFeatureSet.isEnabled(Feature3D::GLCM_JVAR) ? 0 : f_GLCM_JVAR(P_matrix, jave); + fvals_jvar.push_back(f); + } +} + +// prerequisite: needs previously grey-binned ROI. No grey binning is done in this method +void D3_GLCM_feature::calculateCoocMatAtAngle( + // out + SimpleMatrix& GLCM, + // in + int dx, + int dy, + int dz, + const SimpleCube & D, // grey-binned ROI + PixIntens grays_min_val, + PixIntens grays_max_val) +{ + int w = D.width(), + h = D.height(), + d = D.depth(); + + // we need the following info about how 'D' was grey-binned + auto greyInfo = Nyxus::theEnvironment.get_coarse_gray_depth(); + auto greyInfo_localFeature = D3_GLCM_feature::n_levels; + if (greyInfo_localFeature != 0 && greyInfo != greyInfo_localFeature) + greyInfo = greyInfo_localFeature; + if (Nyxus::theEnvironment.ibsi_compliance) + greyInfo = 0; + + // allocate the cooc and intensities matrices + if (radiomics_grey_binning(greyInfo)) + { + // unique intensities + std::unordered_set U (D.begin(), D.end()); + U.erase(0); // discard intensity '0' + + I.assign(U.begin(), U.end()); + std::sort(I.begin(), I.end()); + + GLCM.allocate((int)I.size(), (int)I.size()); + } + else + if (matlab_grey_binning(greyInfo)) + { + auto n_matlab_levels = greyInfo; + I.resize(n_matlab_levels); + for (int i = 0; i < n_matlab_levels; i++) + I[i] = i + 1; + + GLCM.allocate(n_matlab_levels, n_matlab_levels); + } + else + { + // IBSI + auto ibsi_levels_it = std::max_element (D.begin(), D.end()); + auto n_ibsi_levels = *ibsi_levels_it; + + I.resize(n_ibsi_levels); + for (int i = 0; i < n_ibsi_levels; i++) + I[i] = i + 1; + + GLCM.allocate(n_ibsi_levels, n_ibsi_levels); + } + if (GLCM.capacity() < GLCM.width() * GLCM.height()) + { + std::cerr << "Error: cannot allocate a " << GLCM.width() << "x" << GLCM.height() << " matrix \n"; + throw std::runtime_error("Allocation error in GLCMFeature::calculateCoocMatAtAngle(): requested " + std::to_string(GLCM.width() * GLCM.height()) + " but received " + std::to_string(GLCM.capacity())); + } + + std::fill (GLCM.begin(), GLCM.end(), 0.0); + + for (int zslice=0; zslice < d; zslice++) + for (int row = 0; row < h; row++) + for (int col = 0; col < w; col++) + { + if (D.safe(zslice+dz, row+dy, col+dx)) + { + // Raw intensities + PixIntens lvl_b = D.zyx(zslice, row, col), + lvl_a = D.zyx (zslice + dz, row + dy, col + dx); + + // Skip 0-intensity pixels (usually out of mask pixels) + if (ibsi_grey_binning(greyInfo)) + if (lvl_a == 0 || lvl_b == 0) + continue; + + // 0-based grey tone indices, hence '-1' + int a = lvl_a, + b = lvl_b; + + // raw intensities need to be modified for different grey binning paradigms (Matlab, PyRadiomics, IBSI) + if (radiomics_grey_binning(greyInfo)) + { + // skip zeroes + if (a == 0 || b == 0) + continue; + + // index of 'a' + auto lower = std::lower_bound(I.begin(), I.end(), a); // enjoy sorted vector 'I' + a = int(lower - I.begin()); // intensity index in array of unique intensities 'I' + // index of 'b' + lower = std::lower_bound(I.begin(), I.end(), b); // enjoy sorted vector 'I' + b = int(lower - I.begin()); // intensity index in array of unique intensities 'I' + } + else // matlab and IBSI + { + a = a - 1; + b = b - 1; + } + + (GLCM.xy(a, b))++; + + // Radiomics GLCM is symmetric, Matlab one is not + if (D3_GLCM_feature::symmetric_glcm || radiomics_grey_binning(greyInfo) || ibsi_grey_binning(greyInfo)) + (GLCM.xy(b, a))++; + } + } + + // Calculate sum of GLCM for feature calculations + sum_p = 0; + for (int i = 0; i < GLCM.width(); ++i) + for (int j = 0; j < GLCM.height(); ++j) + sum_p += GLCM.xy(i, j); +} + +void D3_GLCM_feature::calculatePxpmy() +{ + int Ng = I.size(); + + Pxpy.resize(2 * Ng); + std::fill(Pxpy.begin(), Pxpy.end(), 0); + + Pxmy.resize(Ng); + std::fill(Pxmy.begin(), Pxmy.end(), 0); + + kValuesSum.resize(2 * Ng); + std::fill(kValuesSum.begin(), kValuesSum.end(), 0); + + kValuesDiff.resize(Ng, 0); + std::fill(kValuesDiff.begin(), kValuesDiff.end(), 0); + + for (int x = 0; x < Ng; x++) + for (int y = 0; y < Ng; y++) + { + // normalize with '/sum_p' per IBSI + Pxpy[x + y] += P_matrix.yx(x, y) / sum_p; + Pxmy[std::abs(x - y)] += P_matrix.yx(x, y) / sum_p; + + double xval = I[x], yval = I[y]; + kValuesSum[x + y] = xval + yval; + kValuesDiff[std::abs(x - y)] = std::abs(xval - yval); + } +} + +void D3_GLCM_feature::calculate_by_row_mean() +{ + int n_levels = I.size(); + + // px[i] is the (i-1)th entry in the marginal probability matrix obtained + // by summing the rows of p[i][j] + std::vector px(n_levels); + for (int j = 0; j < n_levels; j++) + for (int i = 0; i < n_levels; i++) + px[i] += P_matrix.xy(i, j) / sum_p; + + // Now calculate the means and standard deviations of px and py */ + // - fix supplied by J. Michael Christensen, 21 Jun 1991 */ + // - further modified by James Darrell McCauley, 16 Aug 1991 + // after realizing that meanx=meany and stddevx=stddevy + by_row_mean = 0; + for (int i = 0; i < n_levels; ++i) + { + double ival = I[i]; + by_row_mean += px[i] * ival; + } +} + +/* Angular Second Moment +* +* The angular second-moment feature (ASM) f1 is a measure of homogeneity +* of the image. In a homogeneous image, there are very few dominant +* gray-tone transitions. Hence the P matrix for such an image will have +* fewer entries of large magnitude. +*/ +double D3_GLCM_feature::f_asm(const SimpleMatrix& P) +{ + auto Ng = P.width(); + + int i, j; + double sum = 0; + + for (j = 0; j < Ng; ++j) + for (i = 0; i < Ng; ++i) + sum += (P.xy(i, j) / sum_p) * (P.xy(i, j) / sum_p); + + return sum; +} + +/* Contrast +* +* The contrast feature is a difference moment of the P matrix and is a +* measure of the contrast or the amount of local variations present in an +* image. +*/ +double D3_GLCM_feature::f_contrast(const SimpleMatrix& P) +{ + auto Ng = P.width(); + + double sum = 0; + + for (int i = 0; i < Ng; i++) + { + double ival = I[i]; + for (int j = 0; j < Ng; j++) + { + double jval = I[j]; + double d = ival - jval; + sum += P.yx(i, j) * d * d; + } + } + + return sum / sum_p; +} + +/* Correlation +* +* This correlation feature is a measure of gray-tone linear-dependencies +* in the image. +* +* Returns marginal totals 'px' and their mean 'meanx' +*/ +double D3_GLCM_feature::f_corr() +{ + auto Ng = P_matrix.width(); + + // radiomics + double mr = 0; + for (int c = 0; c < Ng; c++) + for (int r = 0; r < Ng; r++) + mr += P_matrix.yx(r, c) * double(I[r]); + + mr /= sum_p; + + double mc = 0; + for (int c = 0; c < Ng; c++) + for (int r = 0; r < Ng; r++) + mc += P_matrix.yx(r, c) * double(I[c]); + + mc /= sum_p; + + double s2r = 0; + for (int c = 0; c < Ng; c++) + for (int r = 0; r < Ng; r++) + { + double dr = double(I[r]) - mr; + s2r += P_matrix.yx(r, c) / sum_p * dr * dr; + } + double sr = sqrt(s2r); + + double s2c = 0; + for (int c = 0; c < Ng; c++) + for (int r = 0; r < Ng; r++) + { + double dc = double(I[c]) - mc; + s2c += P_matrix.yx(r, c) / sum_p * dc * dc; + } + double sc = sqrt(s2c); + + double tmp1 = 0; + for (int c = 0; c < Ng; c++) + for (int r = 0; r < Ng; r++) + tmp1 += (double(I[r]) - mr) * (double(I[c]) - mc) * P_matrix.yx(r, c) / sum_p; + double cor = tmp1 / (sr * sc); + + return cor; +} + +// Variance aka 'Sum of Squares' aka IBSI 'Joint Variance' +double D3_GLCM_feature::f_var(const SimpleMatrix& P) +{ + auto Ng = P.width(); + + double mean = 0, var = 0; + + /*- Corrected by James Darrell McCauley, 16 Aug 1991 + * calculates the mean intensity level instead of the mean of + * cooccurrence matrix elements + */ + for (int r = 0; r < Ng; r++) + { + double sum_x = 0; + for (int c = 0; c < Ng; c++) + sum_x += P.yx(r, c); + mean += sum_x * I[r]; + } + + mean /= sum_p; + + for (int r = 0; r < Ng; r++) + { + double rval = I[r]; + double d = rval - mean; + for (int c = 0; c < Ng; c++) + var += d * d * P.yx(r, c); + } + + return var / sum_p; +} + +/* Inverse Difference Moment */ +double D3_GLCM_feature::f_idm() +{ + int n_levels = I.size(); + + double idm = 0; + + for (int k = 0; k < n_levels; ++k) { + idm += Pxmy[k] / (1 + (k * k)); + } + + return idm; +} + +/* Sum Average */ +double D3_GLCM_feature::f_savg() +{ + // \textit{sum average} = \sum^{2N_g}_{k=2}{p_{x+y}(k)k} + + double f = 0; + auto n = Pxpy.size(); + + for (int i = 0; i < n; i++) + f += kValuesSum[i] * Pxpy[i]; + + return f; +} + +/* Sum Entropy */ +double D3_GLCM_feature::f_sentropy() +{ + double f = 0; + auto n = Pxpy.size(); + + for (int k = 0; k < n; k++) + { + double p = Pxpy[k]; + f += p * fast_log10(p + EPSILON) / LOG10_2; + } + + return -f; +} + +/* Entropy */ +double D3_GLCM_feature::f_entropy(const SimpleMatrix& P) +{ + auto Ng = P.width(); + + int i, j; + double entropy = 0; + + for (j = 0; j < Ng; j++) + for (i = 0; i < Ng; i++) + entropy += P.xy(i, j) * fast_log10(P.xy(i, j) + EPSILON) / LOG10_2; + + return -entropy; +} + +/* Difference Variance */ +double D3_GLCM_feature::f_dvar(const SimpleMatrix& P) +{ + auto Ng = P.width(); + + int n_levels = I.size(); + + size_t n = Pxmy.size(); + + double diffAvg = f_difference_avg(); + std::vector var(n, 0); + + for (int x = 0; x < n; x++) + { + for (int k = 0; k < n; k++) + { + var[k] += pow((k - diffAvg), 2) * Pxmy[k]; + } + } + + double sum = 0; + for (int x = 0; x < n; x++) + sum += var[x]; + + return sum / double(n); +} + +/* Difference Entropy */ +double D3_GLCM_feature::f_dentropy(const SimpleMatrix& P) +{ + auto Ng = P.width(); + + int n_levels = I.size(); + + std::vector entropy(n_levels, 0); + double sum = 0; + + for (int k = 0; k < n_levels; ++k) { + if (Pxmy[k] == 0) continue; // avoid NaN from log2 (note that Pxmy will never be negative) + sum += Pxmy[k] * fast_log10(Pxmy[k] + EPSILON) / LOG10_2; + } + + return -sum; +} + +double D3_GLCM_feature::f_difference_avg() +{ + double f = 0; + auto n = Pxmy.size(); + + for (int i = 0; i < n; i++) + f += kValuesDiff[i] * Pxmy[i]; + + return f; +} + +void D3_GLCM_feature::calcH(const SimpleMatrix& P, std::vector& px, std::vector& py) +{ + auto Ng = P.width(); + + hx = hy = hxy = hxy1 = hxy2 = 0; + + for (int j = 0; j < Ng; j++) + for (int i = 0; i < Ng; i++) + { + auto p = P.xy(i, j) / sum_p; + px[i] += p; + py[j] += p; + } + + for (int j = 0; j < Ng; j++) + { + auto pyj = py[j]; + + for (int i = 0; i < Ng; i++) + { + auto p = P.xy(i, j) / sum_p, + pxi = px[i]; + double log_pp; + if (pxi == 0 || pyj == 0) { + log_pp = 0; + } + else { + log_pp = fast_log10(pxi * pyj + EPSILON) / LOG10_2 /*avoid /LOG10_2 */; + } + + hxy1 += p * log_pp; + hxy2 += pxi * pyj * log_pp; + if (p > 0) + hxy += p * fast_log10(p + EPSILON) / LOG10_2 /*avoid /LOG10_2 */; + } + } + + /* Calculate entropies of px and py */ + for (int i = 0; i < Ng; ++i) + { + if (px[i] > 0) + hx += px[i] * fast_log10(px[i] + EPSILON) / LOG10_2 /*avoid /LOG10_2 */; + + + if (py[i] > 0) + hy += py[i] * fast_log10(py[i] + EPSILON) / LOG10_2 /*avoid /LOG10_2 */; + } +} + +double D3_GLCM_feature::f_info_meas_corr1(const SimpleMatrix& P) +{ + + auto Ng = P.width(); + + double HX = 0, HXY = 0, HXY1 = 0; + + std::vector px, py; + px.resize(Ng); + py.resize(Ng); + std::fill(px.begin(), px.end(), 0); + std::fill(py.begin(), py.end(), 0); + + for (int i = 0; i < Ng; ++i) { + for (int j = 0; j < Ng; ++j) { + px[i] += P.xy(i, j) / sum_p; + py[j] += P.xy(i, j) / sum_p; + } + } + + for (int i = 0; i < Ng; ++i) { + for (int j = 0; j < Ng; ++j) { + HXY += P.xy(i, j) / sum_p * fast_log10(P.xy(i, j) / sum_p + EPSILON) / LOG10_2; + HXY1 += P.xy(i, j) / sum_p * fast_log10(px[i] * py[j] + EPSILON) / LOG10_2; + } + } + + for (int i = 0; i < Ng; ++i) { + HX += px[i] * fast_log10(px[i] + EPSILON) / LOG10_2; + + } + + return (HXY - HXY1) / HX; +} + +double D3_GLCM_feature::f_info_meas_corr2(const SimpleMatrix& P) +{ + auto Ng = P.width(); + + double HX = 0, HXY = 0, HXY2 = 0; + + std::vector px, py; + px.resize(Ng); + py.resize(Ng); + std::fill(px.begin(), px.end(), 0); + std::fill(py.begin(), py.end(), 0); + + for (int i = 0; i < Ng; ++i) { + for (int j = 0; j < Ng; ++j) { + px[i] += P.xy(i, j) / sum_p; + py[j] += P.xy(i, j) / sum_p; + } + } + + for (int i = 0; i < Ng; ++i) { + for (int j = 0; j < Ng; ++j) { + HXY += P.xy(i, j) / sum_p * fast_log10(P.xy(i, j) / sum_p + EPSILON) / LOG10_2; + + HXY2 += px[i] * py[j] * fast_log10(px[i] * py[j] + EPSILON) / LOG10_2; + } + } + + return sqrt(fabs(1 - exp(-2 * (-HXY2 + HXY)))); +} + +double D3_GLCM_feature::f_energy(const SimpleMatrix& P) +{ + auto Ng = P.width(); + + int n_levels = I.size(); + + double energy = 0.0; + + for (int x = 0; x < Ng; x++) + for (int y = 0; y < Ng; y++) + { + auto p = P.xy(x, y) / sum_p; + energy += p * p; + } + + return energy; +} + +double D3_GLCM_feature::f_homogeneity() +{ + int Ng = I.size(); + + double homogeneity = 0.0; + + for (int r = 0; r < Ng; r++) + for (int c = 0; c < Ng; c++) + homogeneity += P_matrix.yx(r, c) / sum_p / (1.0 + (double)std::abs(r - c)); + + return homogeneity; +} + +double D3_GLCM_feature::f_GLCM_ACOR(const SimpleMatrix& P) +{ + auto Ng = P.width(); + + // autocorrelation = \sum^{N_g}_{i=1} sum^{N_g}_{j=1} p(i,j) i j + + double f = 0; + + for (int x = 0; x < Ng; x++) + { + double xval = (double)I[x]; + for (int y = 0; y < Ng; y++) + { + double yval = (double)I[y]; + f += P.xy(x, y) * xval * yval; + } + } + f = f / sum_p; + return f; +} + +// +// Argument 'mean_x' is calculated by f_corr() +// +double D3_GLCM_feature::f_GLCM_CLUPROM() +{ + int n_levels = I.size(); + + // cluster prominence = \sum^{N_g}_{i=1} \sum^{N_g}_{j=1} (i + j - \mu_x - \mu_y) ^4 P(i,j) + + double f = 0; + + for (int r = 0; r < n_levels; r++) + { + double rval = I[r]; + for (int c = 0; c < n_levels; c++) + { + double cval = I[c]; + double m = rval + cval - by_row_mean - by_row_mean; + f += m * m * m * m * P_matrix.yx(r, c) / sum_p; + } + } + + return f; +} + +double D3_GLCM_feature::f_GLCM_CLUSHADE() +{ + int n_levels = I.size(); + + // cluster shade = \sum^{N_g}_{i=1} \sum^{N_g}_{j=1} (i + j - \mu_x - \mu_y) ^3 p(i,j) + + double f = 0; + + for (int r = 0; r < n_levels; r++) + { + double rval = I[r]; + for (int c = 0; c < n_levels; c++) + { + double cval = I[c]; + double m = rval + cval - by_row_mean - by_row_mean; + f += m * m * m * P_matrix.yx(r, c) / sum_p; + } + } + + return f; +} + +double D3_GLCM_feature::f_GLCM_CLUTEND() +{ + int n_levels = I.size(); + + double f = 0; + + // + // if (theEnvironment.ibsi_compliance) + // // According to IBSI, feature "cluster tendency" is equivalent to "sum variance" + // f = f_svar(P_matrix, -999.999, this->Pxpy); + // else + + // Calculate it the radiomics way: cluster tendency = \sum^{N_g}_{i=1} \sum^{N_g}_{j=1} (i + j - \mu_x - \mu_y) ^2 p(i,j) + for (int x = 0; x < n_levels; x++) + { + double xval = I[x]; + for (int y = 0; y < n_levels; y++) + { + double yval = I[y]; + double m = xval + yval - by_row_mean * 2.0; + f += m * m * P_matrix.xy(x, y) / sum_p; + } + } + + return f; +} + +double D3_GLCM_feature::f_GLCM_DIS(const SimpleMatrix& P_matrix) +{ + int n_levels = I.size(); + + // dissimilarity = \sum^{N_g}_{i=1} \sum^{N_g}_{j=1} |i-j| p(i,j) + + double f = 0; + + for (int x = 0; x < n_levels; x++) + for (int y = 0; y < n_levels; y++) + f += std::fabs(double(x + 1) - double(y + 1)) * P_matrix.xy(x, y) / sum_p; + + return f; +} + +double D3_GLCM_feature::f_GLCM_HOM2(const SimpleMatrix& P_matrix) +{ + int n_levels = I.size(); + + // homogeneity2 = \sum^{N_g}_{i=1} \sum^{N_g}_{j=1} \frac {p(i,j)} {1+|i-j|^2} + + double hom2 = 0.0; + + for (int x = 0; x < n_levels; x++) + for (int y = 0; y < n_levels; y++) + hom2 += P_matrix.xy(x, y) / (1.0 + (double)std::abs(x - y) * (double)std::abs(x - y)); + + return hom2; +} + +double D3_GLCM_feature::f_GLCM_IDMN() +{ + auto Ng = P_matrix.width(); + + // IDMN = \sum^{N_g-1}_{k=0} \frac {p_{x-y}(k)} {1+\frac{k^2}{N_g^2}} + + double f = 0; + + double Ng2 = double(Ng) * double(Ng); + for (int k = 0; k < Ng; k++) + f += Pxmy[k] / (1.0 + (double(k) * double(k)) / Ng2); + + return f; +} + +double D3_GLCM_feature::f_GLCM_ID() +{ + // inverse difference = \sum^{N_g-1}_{k=0} \frac {p_{x-y}(k)} {1+k} + + auto Ng = I.size(); + double f = 0; + + for (int k = 0; k < Ng; k++) + f += Pxmy[k] / (1.0 + double(k)); + + return f; +} + +double D3_GLCM_feature::f_GLCM_IDN() +{ + // inverse difference normalized = \sum^{N_g-1}_{k=0} \frac {p_{x-y}(k)} {1+\frac{k}{N_g}} + + auto Ng = I.size(); + + double f = 0; + + for (int k = 0; k < Ng; k++) + f += Pxmy[k] / (1.0 + double(k) / Ng); + + return f; +} + +double D3_GLCM_feature::f_GLCM_IV() +{ + // inverse variance = \sum^{N_g-1}_{k=1} \frac {p_{x-y}(k)} {k^2} + + double f = 0; + auto n = Pxmy.size(); + + for (int k = 1; k < n; k++) + { + //f += Pxmy[k] / (double(I[k]) * double(I[k])); + double kval = kValuesDiff[k]; + f += Pxmy[k] / (kval * kval); + } + + return f; +} + +double D3_GLCM_feature::f_GLCM_JAVE() +{ + // joint average = \mu_x = \sum^{N_g}_{i=1} \sum^{N_g}_{j=1} p(i,j) i + + auto Ng = I.size(); + double f = 0; + + for (int i = 0; i < Ng; i++) + { + double ival = I[i]; + for (int j = 0; j < Ng; j++) + f += P_matrix.yx(i, j) * ival; + } + return f / sum_p; +} + +double D3_GLCM_feature::f_GLCM_JE(const SimpleMatrix& P) +{ + int Ng = I.size(); + + // jointentropy = - \sum^{N_g}_{i=1} sum^{N_g}_{j=1} p(i,j) \log_2 ( p(i,j) + \epsilon ) + + double f = 0.0; + + for (int x = 0; x < Ng; x++) + for (int y = 0; y < Ng; y++) + { + double p = P_matrix.xy(x, y) / sum_p; + f += p * fast_log10(p + EPSILON) / LOG10_2; + } + + return -f; +} + +double D3_GLCM_feature::f_GLCM_JMAX(const SimpleMatrix& P_matrix) +{ + int n_levels = I.size(); + + // maximum probability = \max p(i,j) + + double max_p = -1; // never-probability + + for (int x = 0; x < n_levels; x++) + for (int y = 0; y < n_levels; y++) + { + double p = P_matrix.xy(x, y) / sum_p; + max_p = std::max(max_p, p); + } + + return max_p; +} + +double D3_GLCM_feature::f_GLCM_JVAR(const SimpleMatrix& P_matrix, double joint_ave) +{ + int n_levels = I.size(); + + // joint variance = \sum^{N_g}_{i=1} \sum^{N_g}_{j=1} (i-\mu_x) ^2 p(i,j) + // where \mu_x is the value of joint average feature (IBSI: "Fcm.joint.avg"), + // \mu_x = \sum^{N_g}_{i=1} \sum^{N_g}_{j=1} i p(i,j) + + double f = 0; + for (int x = 0; x < n_levels; x++) + { + double d = double(x + 1) - joint_ave, + d2 = d * d; + for (int y = 0; y < n_levels; y++) + f += d2 * P_matrix.xy(x, y) / sum_p; + } + return f; +} + +// 'afv' is angled feature values +double D3_GLCM_feature::calc_ave(const std::vector& afv) +{ + if (afv.empty()) + return 0; + + double n = static_cast (afv.size()), + ave = std::reduce(afv.begin(), afv.end()) / n; + + return ave; +} + +void D3_GLCM_feature::reduce(size_t start, size_t end, std::vector* ptrLabels, std::unordered_map * ptrLabelData) +{ + for (auto i = start; i < end; i++) + { + int lab = (*ptrLabels)[i]; + LR& r = (*ptrLabelData)[lab]; + D3_GLCM_feature f; + f.calculate(r); + f.save_value(r.fvals); + } +} diff --git a/src/nyx/features/3d_glcm.h b/src/nyx/features/3d_glcm.h new file mode 100644 index 00000000..55c7576f --- /dev/null +++ b/src/nyx/features/3d_glcm.h @@ -0,0 +1,253 @@ +#pragma once + +#include +#include "../roi_cache.h" +#include "image_matrix.h" +#include "../feature_method.h" +#include "texture_feature.h" + +class D3_GLCM_feature : public FeatureMethod, public TextureFeature +{ +public: + + // Codes of features implemented by this class. Used in feature manager's mechanisms, + // in the feature group nickname expansion, and in the feature value output + const constexpr static std::initializer_list featureset = + { + Nyxus::Feature3D::GLCM_ACOR, // Autocorrelation, IBSI # QWB0 + Nyxus::Feature3D::GLCM_ASM, // Angular second moment IBSI # 8ZQL + Nyxus::Feature3D::GLCM_CLUPROM, // Cluster prominence, IBSI # AE86 + Nyxus::Feature3D::GLCM_CLUSHADE, // Cluster shade, IBSI # 7NFM + Nyxus::Feature3D::GLCM_CLUTEND, // Cluster tendency, IBSI # DG8W + Nyxus::Feature3D::GLCM_CONTRAST, // Contrast, IBSI # ACUI + Nyxus::Feature3D::GLCM_CORRELATION, // Correlation, IBSI # NI2N + Nyxus::Feature3D::GLCM_DIFAVE, // Difference average, IBSI # TF7R + Nyxus::Feature3D::GLCM_DIFENTRO, // Difference entropy, IBSI # NTRS + Nyxus::Feature3D::GLCM_DIFVAR, // Difference variance, IBSI # D3YU + Nyxus::Feature3D::GLCM_DIS, // Dissimilarity, IBSI # 8S9J + Nyxus::Feature3D::GLCM_ENERGY, // Energy + Nyxus::Feature3D::GLCM_ENTROPY, // Entropy + Nyxus::Feature3D::GLCM_HOM1, // Homogeneity-1 (PyR) + Nyxus::Feature3D::GLCM_HOM2, // Homogeneity-2 (PyR) + Nyxus::Feature3D::GLCM_ID, // Inv diff, IBSI # IB1Z + Nyxus::Feature3D::GLCM_IDN, // Inv diff normalized, IBSI # NDRX + Nyxus::Feature3D::GLCM_IDM, // Inv diff mom, IBSI # WF0Z + Nyxus::Feature3D::GLCM_IDMN, // Inv diff mom normalized, IBSI # 1QCO + Nyxus::Feature3D::GLCM_INFOMEAS1, // Information measure of correlation 1, IBSI # R8DG + Nyxus::Feature3D::GLCM_INFOMEAS2, // Information measure of correlation 2, IBSI # JN9H + Nyxus::Feature3D::GLCM_IV, // Inv variance, IBSI # E8JP + Nyxus::Feature3D::GLCM_JAVE, // Joint average, IBSI # 60VM + Nyxus::Feature3D::GLCM_JE, // Joint entropy, IBSI # TU9B + Nyxus::Feature3D::GLCM_JMAX, // Joint max (aka PyR max probability), IBSI # GYBY + Nyxus::Feature3D::GLCM_JVAR, // Joint var (aka PyR Sum of Squares), IBSI # UR99 + Nyxus::Feature3D::GLCM_SUMAVERAGE, // Sum average, IBSI # ZGXS + Nyxus::Feature3D::GLCM_SUMENTROPY, // Sum entropy, IBSI # P6QZ + Nyxus::Feature3D::GLCM_SUMVARIANCE, // Sum variance, IBSI # OEEB + Nyxus::Feature3D::GLCM_VARIANCE, // Variance + Nyxus::Feature3D::GLCM_ASM_AVE, + Nyxus::Feature3D::GLCM_ACOR_AVE, + Nyxus::Feature3D::GLCM_CLUPROM_AVE, + Nyxus::Feature3D::GLCM_CLUSHADE_AVE, + Nyxus::Feature3D::GLCM_CLUTEND_AVE, + Nyxus::Feature3D::GLCM_CONTRAST_AVE, + Nyxus::Feature3D::GLCM_CORRELATION_AVE, + Nyxus::Feature3D::GLCM_DIFAVE_AVE, + Nyxus::Feature3D::GLCM_DIFENTRO_AVE, + Nyxus::Feature3D::GLCM_DIFVAR_AVE, + Nyxus::Feature3D::GLCM_DIS_AVE, + Nyxus::Feature3D::GLCM_ENERGY_AVE, + Nyxus::Feature3D::GLCM_ENTROPY_AVE, + Nyxus::Feature3D::GLCM_HOM1_AVE, + Nyxus::Feature3D::GLCM_ID_AVE, + Nyxus::Feature3D::GLCM_IDN_AVE, + Nyxus::Feature3D::GLCM_IDM_AVE, + Nyxus::Feature3D::GLCM_IDMN_AVE, + Nyxus::Feature3D::GLCM_IV_AVE, + Nyxus::Feature3D::GLCM_JAVE_AVE, + Nyxus::Feature3D::GLCM_JE_AVE, + Nyxus::Feature3D::GLCM_INFOMEAS1_AVE, + Nyxus::Feature3D::GLCM_INFOMEAS2_AVE, + Nyxus::Feature3D::GLCM_VARIANCE_AVE, + Nyxus::Feature3D::GLCM_JMAX_AVE, + Nyxus::Feature3D::GLCM_JVAR_AVE, + Nyxus::Feature3D::GLCM_SUMAVERAGE_AVE, + Nyxus::Feature3D::GLCM_SUMENTROPY_AVE, + Nyxus::Feature3D::GLCM_SUMVARIANCE_AVE + }; + + // Features implemented by this class that do not require vector-like angled output. Instead, they are output as a single values + const constexpr static std::initializer_list nonAngledFeatures = + { + Nyxus::Feature3D::GLCM_ASM_AVE, + Nyxus::Feature3D::GLCM_ACOR_AVE, + Nyxus::Feature3D::GLCM_CLUPROM_AVE, + Nyxus::Feature3D::GLCM_CLUSHADE_AVE, + Nyxus::Feature3D::GLCM_CLUTEND_AVE, + Nyxus::Feature3D::GLCM_CONTRAST_AVE, + Nyxus::Feature3D::GLCM_CORRELATION_AVE, + Nyxus::Feature3D::GLCM_DIFAVE_AVE, + Nyxus::Feature3D::GLCM_DIFENTRO_AVE, + Nyxus::Feature3D::GLCM_DIFVAR_AVE, + Nyxus::Feature3D::GLCM_DIS_AVE, + Nyxus::Feature3D::GLCM_ENERGY_AVE, + Nyxus::Feature3D::GLCM_ENTROPY_AVE, + Nyxus::Feature3D::GLCM_HOM1_AVE, + Nyxus::Feature3D::GLCM_ID_AVE, + Nyxus::Feature3D::GLCM_IDN_AVE, + Nyxus::Feature3D::GLCM_IDM_AVE, + Nyxus::Feature3D::GLCM_IDMN_AVE, + Nyxus::Feature3D::GLCM_IV_AVE, + Nyxus::Feature3D::GLCM_JAVE_AVE, + Nyxus::Feature3D::GLCM_JE_AVE, + Nyxus::Feature3D::GLCM_INFOMEAS1_AVE, + Nyxus::Feature3D::GLCM_INFOMEAS2_AVE, + Nyxus::Feature3D::GLCM_VARIANCE_AVE, + Nyxus::Feature3D::GLCM_JMAX_AVE, + Nyxus::Feature3D::GLCM_JVAR_AVE, + Nyxus::Feature3D::GLCM_SUMAVERAGE_AVE, + Nyxus::Feature3D::GLCM_SUMENTROPY_AVE, + Nyxus::Feature3D::GLCM_SUMVARIANCE_AVE + }; + + static int offset; // default value: 1 + static int n_levels; // default value: 0 + static bool symmetric_glcm; // default value: false + static std::vector angles; // default value: {0,45,90,135} (the supreset) + double sum_p = 0; // sum of P matrix for normalization + + static bool required(const FeatureSet& fs) + { + return fs.anyEnabled (D3_GLCM_feature::featureset); + } + + D3_GLCM_feature(); + void calculate(LR& r); + void osized_add_online_pixel(size_t x, size_t y, uint32_t intensity); + void osized_calculate(LR& r, ImageLoader& imloader); + void save_value(std::vector>& feature_vals); + static void parallel_process_1_batch(size_t start, size_t end, std::vector* ptrLabels, std::unordered_map * ptrLabelData); + static void reduce(size_t start, size_t end, std::vector* ptrLabels, std::unordered_map * ptrLabelData); + +private: + + void Extract_Texture_Features2_NT(int angle, WriteImageMatrix_nontriv& grays, PixIntens min_val, PixIntens max_val); + void calculateCoocMatAtAngle_NT( + // out + SimpleMatrix& matrix, + // in + int dx, + int dy, + int dz, + WriteImageMatrix_nontriv& grays, + PixIntens min_val, + PixIntens max_val, + bool normalize); + + void extract_texture_features_at_angle (int angle, const SimpleCube & grays, PixIntens min_val, PixIntens max_val); + + void calculateCoocMatAtAngle( + // out + SimpleMatrix& p_matrix, + // in + int dx, + int dy, + int dz, + const SimpleCube & grays, + PixIntens min_val, + PixIntens max_val); + + void calculatePxpmy(); + void calculate_by_row_mean(); + + static inline int cast_to_range(PixIntens orig_I, PixIntens min_orig_I, PixIntens max_orig_I, int min_target_I, int max_target_I) + { + int target_I = (int)(double(orig_I - min_orig_I) / double(max_orig_I - min_orig_I) * double(max_target_I - min_target_I) + min_target_I); + return target_I; + } + + double f_asm(const SimpleMatrix& P_matrix); + double f_contrast(const SimpleMatrix& P_matix); + double f_corr(); + double f_var(const SimpleMatrix& P_matrix); + double f_idm(); + double f_savg(); + double f_sentropy(); + double f_svar(const SimpleMatrix& P_matrix, double sum_entropy); + double f_entropy(const SimpleMatrix& P_matrix); + double f_dvar(const SimpleMatrix& P_matrix); + double f_dentropy(const SimpleMatrix& P_matrix); + double f_GLCM_ACOR(const SimpleMatrix& P_matrix); + double f_GLCM_CLUPROM(); + double f_GLCM_CLUSHADE(); + double f_GLCM_CLUTEND(); + double f_GLCM_DIS(const SimpleMatrix& P_matrix); + double f_GLCM_HOM2(const SimpleMatrix& P_matrix); + double f_GLCM_IDMN(); + double f_GLCM_ID(); + double f_GLCM_IDN(); + double f_GLCM_IV(); + double f_GLCM_JAVE(); + double f_GLCM_JE(const SimpleMatrix& P_matrix); + double f_GLCM_JMAX(const SimpleMatrix& P_matrix); + double f_GLCM_JVAR(const SimpleMatrix& P_matrix, double mean_x); + + double calc_ave(const std::vector& angled_feature_vals); + + using AngledFeatures = std::vector; + void copyfvals(AngledFeatures& dst, const AngledFeatures& src); + + // Angled feature values. Each vector contains 1 to 4 values corresponding to angles 0, 45, 90, and 135 degrees + std::vector fvals_ASM, + fvals_acor, + fvals_cluprom, + fvals_clushade, + fvals_clutend, + fvals_contrast, + fvals_correlation, + fvals_diff_avg, + fvals_diff_var, + fvals_diff_entropy, + fvals_dis, + fvals_energy, + fvals_entropy, + fvals_homo, + fvals_hom2, + fvals_id, + fvals_idn, + fvals_IDM, + fvals_idmn, + fvals_meas_corr1, + fvals_meas_corr2, + fvals_iv, + fvals_jave, + fvals_je, + fvals_jmax, + fvals_jvar, + fvals_sum_avg, + fvals_sum_var, + fvals_sum_entropy, + fvals_variance; + + void clear_result_buffers(); + + double hx = -1, hy = -1, hxy = -1, hxy1 = -1, hxy2 = -1; // Entropies for f12/f13_icorr calculation + void calcH(const SimpleMatrix& P_matrix, std::vector& px, std::vector& py); + double f_info_meas_corr1(const SimpleMatrix& P_matrix); + double f_info_meas_corr2(const SimpleMatrix& P_matrix); + + double f_energy(const SimpleMatrix& P_matrix); + double f_inv_difference(const SimpleMatrix& P_matrix); + double f_homogeneity(); + double f_difference_avg(); + + const double LOG10_2 = 0.30102999566; // precalculated log 2 base 10 + SimpleMatrix P_matrix; + std::vector I; // unique sorted intensities + std::vector Pxpy, + Pxmy, + kValuesSum, // intensities x+y + kValuesDiff; // intensities x-y + double by_row_mean; + const double EPSILON = 0.000000001; +}; + diff --git a/src/nyx/features/3d_glcm_nontriv.cpp b/src/nyx/features/3d_glcm_nontriv.cpp new file mode 100644 index 00000000..140b46fa --- /dev/null +++ b/src/nyx/features/3d_glcm_nontriv.cpp @@ -0,0 +1,11 @@ +#include "../environment.h" +#include "3d_glcm.h" +#include "image_matrix_nontriv.h" + +using namespace Nyxus; + +void D3_GLCM_feature::osized_calculate (LR& r, ImageLoader & imloader) +{ + calculate (r); +} + diff --git a/src/nyx/features/3d_gldzm.cpp b/src/nyx/features/3d_gldzm.cpp index 26f7a026..5b0730e5 100644 --- a/src/nyx/features/3d_gldzm.cpp +++ b/src/nyx/features/3d_gldzm.cpp @@ -53,6 +53,7 @@ void D3_GLDZM_feature::prepare_GLDZM_matrix_kit (SimpleMatrix& GLD // -- Zones (intensity clusters) std::vector Z; + // -- binned intensities SimpleCube D; D.allocate (r.aux_image_cube.width(), r.aux_image_cube.height(), r.aux_image_cube.depth()); diff --git a/src/nyx/features/glcm.cpp b/src/nyx/features/glcm.cpp index d98074aa..2ac38103 100644 --- a/src/nyx/features/glcm.cpp +++ b/src/nyx/features/glcm.cpp @@ -228,9 +228,6 @@ void GLCMFeature::Extract_Texture_Features2(int angle, const ImageMatrix& grays, int nrows = grays.height; int ncols = grays.width; - // Mean of marginal totals of GLCM - double mean_x; - // Compute the gray-tone spatial dependence matrix int dx, dy; switch (angle) diff --git a/src/nyx/features/glcm.h b/src/nyx/features/glcm.h index 3f4016e4..f02b9ced 100644 --- a/src/nyx/features/glcm.h +++ b/src/nyx/features/glcm.h @@ -153,14 +153,7 @@ class GLCMFeature : public FeatureMethod, public TextureFeature PixIntens max_val, bool normalize); - void Extract_Texture_Features( - int distance, - int angle, - const SimpleMatrix& grays); // 'grays' is 0-255 grays - void Extract_Texture_Features2(int angle, const ImageMatrix& grays, PixIntens min_val, PixIntens max_val); - - void calculate_normalized_graytone_matrix(SimpleMatrix& G, int minI, int maxI, const ImageMatrix& Im); - void calculate_normalized_graytone_matrix(OOR_ReadMatrix& G, int minI, int maxI, const ImageMatrix& Im); + void Extract_Texture_Features2 (int angle, const ImageMatrix& grays, PixIntens min_val, PixIntens max_val); void calculateCoocMatAtAngle( // out diff --git a/src/nyx/featureset.cpp b/src/nyx/featureset.cpp index d8b1de9c..5c1598bd 100644 --- a/src/nyx/featureset.cpp +++ b/src/nyx/featureset.cpp @@ -620,105 +620,166 @@ namespace Nyxus std::map UserFacing_3D_featureNames = { // Intensity - { "3D_COV", Nyxus::Feature3D::COV }, - { "3D_COVERED_IMAGE_INTENSITY_RANGE", Nyxus::Feature3D::COVERED_IMAGE_INTENSITY_RANGE }, - { "3D_ENERGY", Nyxus::Feature3D::ENERGY }, - { "3D_ENTROPY", Nyxus::Feature3D::ENTROPY }, - { "3D_EXCESS_KURTOSIS", Nyxus::Feature3D::EXCESS_KURTOSIS }, - { "3D_HYPERFLATNESS", Nyxus::Feature3D::HYPERFLATNESS }, - { "3D_HYPERSKEWNESS", Nyxus::Feature3D::HYPERSKEWNESS }, - { "3D_INTEGRATED_INTENSITY", Nyxus::Feature3D::INTEGRATED_INTENSITY }, - { "3D_INTERQUARTILE_RANGE", Nyxus::Feature3D::INTERQUARTILE_RANGE }, - { "3D_KURTOSIS", Nyxus::Feature3D::KURTOSIS }, - { "3D_MAX", Nyxus::Feature3D::MAX }, - { "3D_MEAN", Nyxus::Feature3D::MEAN }, - { "3D_MEAN_ABSOLUTE_DEVIATION", Nyxus::Feature3D::MEAN_ABSOLUTE_DEVIATION }, - { "3D_MEDIAN", Nyxus::Feature3D::MEDIAN }, - { "3D_MEDIAN_ABSOLUTE_DEVIATION", Nyxus::Feature3D::MEDIAN_ABSOLUTE_DEVIATION }, - { "3D_MIN", Nyxus::Feature3D::MIN }, - { "3D_MODE", Nyxus::Feature3D::MODE }, - { "3D_P01", Nyxus::Feature3D::P01 }, - { "3D_P10", Nyxus::Feature3D::P10 }, - { "3D_P25", Nyxus::Feature3D::P25 }, - { "3D_P75", Nyxus::Feature3D::P75 }, - { "3D_P90", Nyxus::Feature3D::P90 }, - { "3D_P99", Nyxus::Feature3D::P99 }, - { "3D_QCOD", Nyxus::Feature3D::QCOD }, - { "3D_RANGE", Nyxus::Feature3D::RANGE }, - { "3D_ROBUST_MEAN", Nyxus::Feature3D::ROBUST_MEAN }, - { "3D_ROBUST_MEAN_ABSOLUTE_DEVIATION", Nyxus::Feature3D::ROBUST_MEAN_ABSOLUTE_DEVIATION }, - { "3D_ROOT_MEAN_SQUARED", Nyxus::Feature3D::ROOT_MEAN_SQUARED }, - { "3D_SKEWNESS", Nyxus::Feature3D::SKEWNESS }, - { "3D_STANDARD_DEVIATION", Nyxus::Feature3D::STANDARD_DEVIATION }, - { "3D_STANDARD_DEVIATION_BIASED", Nyxus::Feature3D::STANDARD_DEVIATION_BIASED }, - { "3D_STANDARD_ERROR", Nyxus::Feature3D::STANDARD_ERROR }, - { "3D_UNIFORMITY", Nyxus::Feature3D::UNIFORMITY }, - { "3D_UNIFORMITY_PIU", Nyxus::Feature3D::UNIFORMITY_PIU }, - { "3D_VARIANCE", Nyxus::Feature3D::VARIANCE }, - { "3D_VARIANCE_BIASED", Nyxus::Feature3D::VARIANCE_BIASED }, + { "3COV", Nyxus::Feature3D::COV }, + { "3COVERED_IMAGE_INTENSITY_RANGE", Nyxus::Feature3D::COVERED_IMAGE_INTENSITY_RANGE }, + { "3ENERGY", Nyxus::Feature3D::ENERGY }, + { "3ENTROPY", Nyxus::Feature3D::ENTROPY }, + { "3EXCESS_KURTOSIS", Nyxus::Feature3D::EXCESS_KURTOSIS }, + { "3HYPERFLATNESS", Nyxus::Feature3D::HYPERFLATNESS }, + { "3HYPERSKEWNESS", Nyxus::Feature3D::HYPERSKEWNESS }, + { "3INTEGRATED_INTENSITY", Nyxus::Feature3D::INTEGRATED_INTENSITY }, + { "3INTERQUARTILE_RANGE", Nyxus::Feature3D::INTERQUARTILE_RANGE }, + { "3KURTOSIS", Nyxus::Feature3D::KURTOSIS }, + { "3MAX", Nyxus::Feature3D::MAX }, + { "3MEAN", Nyxus::Feature3D::MEAN }, + { "3MEAN_ABSOLUTE_DEVIATION", Nyxus::Feature3D::MEAN_ABSOLUTE_DEVIATION }, + { "3MEDIAN", Nyxus::Feature3D::MEDIAN }, + { "3MEDIAN_ABSOLUTE_DEVIATION", Nyxus::Feature3D::MEDIAN_ABSOLUTE_DEVIATION }, + { "3MIN", Nyxus::Feature3D::MIN }, + { "3MODE", Nyxus::Feature3D::MODE }, + { "3P01", Nyxus::Feature3D::P01 }, + { "3P10", Nyxus::Feature3D::P10 }, + { "3P25", Nyxus::Feature3D::P25 }, + { "3P75", Nyxus::Feature3D::P75 }, + { "3P90", Nyxus::Feature3D::P90 }, + { "3P99", Nyxus::Feature3D::P99 }, + { "3QCOD", Nyxus::Feature3D::QCOD }, + { "3RANGE", Nyxus::Feature3D::RANGE }, + { "3ROBUST_MEAN", Nyxus::Feature3D::ROBUST_MEAN }, + { "3ROBUST_MEAN_ABSOLUTE_DEVIATION", Nyxus::Feature3D::ROBUST_MEAN_ABSOLUTE_DEVIATION }, + { "3ROOT_MEAN_SQUARED", Nyxus::Feature3D::ROOT_MEAN_SQUARED }, + { "3SKEWNESS", Nyxus::Feature3D::SKEWNESS }, + { "3STANDARD_DEVIATION", Nyxus::Feature3D::STANDARD_DEVIATION }, + { "3STANDARD_DEVIATION_BIASED", Nyxus::Feature3D::STANDARD_DEVIATION_BIASED }, + { "3STANDARD_ERROR", Nyxus::Feature3D::STANDARD_ERROR }, + { "3UNIFORMITY", Nyxus::Feature3D::UNIFORMITY }, + { "3UNIFORMITY_PIU", Nyxus::Feature3D::UNIFORMITY_PIU }, + { "3VARIANCE", Nyxus::Feature3D::VARIANCE }, + { "3VARIANCE_BIASED", Nyxus::Feature3D::VARIANCE_BIASED }, // Morphology - { "3D_AREA", Feature3D::AREA }, - { "3D_MESH_VOLUME", Feature3D::MESH_VOLUME }, - { "3D_VOLUME_CONVEXHULL", Feature3D::VOLUME_CONVEXHULL }, - { "3D_DIAMETER_EQUAL_AREA", Feature3D::DIAMETER_EQUAL_AREA }, - { "3D_DIAMETER_EQUAL_VOLUME", Feature3D::DIAMETER_EQUAL_VOLUME }, - - { "3D_VOLUME_PIXELS", Feature3D::VOLUME_PIXELS }, - { "3D_CENTROID_X", Feature3D::CENTROID_X }, - { "3D_CENTROID_Y", Feature3D::CENTROID_Y }, - { "3D_CENTROID_Z", Feature3D::CENTROID_Z }, - { "3D_BBOX_XMIN", Feature3D::BBOX_XMIN }, - { "3D_BBOX_YMIN", Feature3D::BBOX_YMIN }, - { "3D_BBOX_ZMIN", Feature3D::BBOX_ZMIN }, - { "3D_BBOX_HEIGHT", Feature3D::BBOX_HEIGHT }, - { "3D_BBOX_WIDTH", Feature3D::BBOX_WIDTH }, - { "3D_BBOX_DEPTH", Feature3D::BBOX_DEPTH }, + { "3AREA", Feature3D::AREA }, + { "3MESH_VOLUME", Feature3D::MESH_VOLUME }, + { "3VOLUME_CONVEXHULL", Feature3D::VOLUME_CONVEXHULL }, + { "3DIAMETER_EQUAL_AREA", Feature3D::DIAMETER_EQUAL_AREA }, + { "3DIAMETER_EQUAL_VOLUME", Feature3D::DIAMETER_EQUAL_VOLUME }, + + { "3VOLUME_PIXELS", Feature3D::VOLUME_PIXELS }, + { "3CENTROID_X", Feature3D::CENTROID_X }, + { "3CENTROID_Y", Feature3D::CENTROID_Y }, + { "3CENTROID_Z", Feature3D::CENTROID_Z }, + { "3BBOX_XMIN", Feature3D::BBOX_XMIN }, + { "3BBOX_YMIN", Feature3D::BBOX_YMIN }, + { "3BBOX_ZMIN", Feature3D::BBOX_ZMIN }, + { "3BBOX_HEIGHT", Feature3D::BBOX_HEIGHT }, + { "3BBOX_WIDTH", Feature3D::BBOX_WIDTH }, + { "3BBOX_DEPTH", Feature3D::BBOX_DEPTH }, // Neighbor features - { "3D_NUM_NEIGHBORS", Feature3D::NUM_NEIGHBORS }, - { "3D_PERCENT_TOUCHING", Feature3D::PERCENT_TOUCHING }, - { "3D_CLOSEST_NEIGHBOR1_DIST", Feature3D::CLOSEST_NEIGHBOR1_DIST }, - { "3D_CLOSEST_NEIGHBOR1_ANG", Feature3D::CLOSEST_NEIGHBOR1_ANG }, - { "3D_CLOSEST_NEIGHBOR2_DIST", Feature3D::CLOSEST_NEIGHBOR2_DIST }, - { "3D_CLOSEST_NEIGHBOR2_ANG", Feature3D::CLOSEST_NEIGHBOR2_ANG }, - { "3D_ANG_BW_NEIGHBORS_MEAN", Feature3D::ANG_BW_NEIGHBORS_MEAN }, - { "3D_ANG_BW_NEIGHBORS_STDDEV", Feature3D::ANG_BW_NEIGHBORS_STDDEV }, - { "3D_ANG_BW_NEIGHBORS_MODE", Feature3D::ANG_BW_NEIGHBORS_MODE }, + { "3NUM_NEIGHBORS", Feature3D::NUM_NEIGHBORS }, + { "3PERCENT_TOUCHING", Feature3D::PERCENT_TOUCHING }, + { "3CLOSEST_NEIGHBOR1_DIST", Feature3D::CLOSEST_NEIGHBOR1_DIST }, + { "3CLOSEST_NEIGHBOR1_ANG", Feature3D::CLOSEST_NEIGHBOR1_ANG }, + { "3CLOSEST_NEIGHBOR2_DIST", Feature3D::CLOSEST_NEIGHBOR2_DIST }, + { "3CLOSEST_NEIGHBOR2_ANG", Feature3D::CLOSEST_NEIGHBOR2_ANG }, + { "3ANG_BW_NEIGHBORS_MEAN", Feature3D::ANG_BW_NEIGHBORS_MEAN }, + { "3ANG_BW_NEIGHBORS_STDDEV", Feature3D::ANG_BW_NEIGHBORS_STDDEV }, + { "3ANG_BW_NEIGHBORS_MODE", Feature3D::ANG_BW_NEIGHBORS_MODE }, // Geometric moments - { "3D_SPAT_MOMENT_00", Feature3D::SPAT_MOMENT_00 }, - { "3D_SPAT_MOMENT_01", Feature3D::SPAT_MOMENT_01 }, - { "3D_SPAT_MOMENT_02", Feature3D::SPAT_MOMENT_02 }, - { "3D_SPAT_MOMENT_03", Feature3D::SPAT_MOMENT_03 }, - { "3D_SPAT_MOMENT_10", Feature3D::SPAT_MOMENT_10 }, - { "3D_SPAT_MOMENT_11", Feature3D::SPAT_MOMENT_11 }, - { "3D_SPAT_MOMENT_12", Feature3D::SPAT_MOMENT_12 }, - { "3D_SPAT_MOMENT_13", Feature3D::SPAT_MOMENT_13 }, - { "3D_SPAT_MOMENT_20", Feature3D::SPAT_MOMENT_20 }, - { "3D_SPAT_MOMENT_21", Feature3D::SPAT_MOMENT_21 }, - { "3D_SPAT_MOMENT_22", Feature3D::SPAT_MOMENT_22 }, - { "3D_SPAT_MOMENT_23", Feature3D::SPAT_MOMENT_23 }, - { "3D_SPAT_MOMENT_30", Feature3D::SPAT_MOMENT_30 }, - - { "3D_GLDZM_SDE", Feature3D::GLDZM_SDE}, // Small Distance Emphasis - { "3D_GLDZM_LDE", Feature3D::GLDZM_LDE }, // Large Distance Emphasis - { "3D_GLDZM_LGLZE", Feature3D::GLDZM_LGLZE }, // Low Grey Level Zone Emphasis - { "3D_GLDZM_HGLZE", Feature3D::GLDZM_HGLZE }, // High Grey Level Zone Emphasis - { "3D_GLDZM_SDLGLE", Feature3D::GLDZM_SDLGLE }, // Small Distance Low Grey Level Emphasis - { "3D_GLDZM_SDHGLE", Feature3D::GLDZM_SDHGLE }, // Small Distance High GreyLevel Emphasis - { "3D_GLDZM_LDLGLE", Feature3D::GLDZM_LDLGLE }, // Large Distance Low Grey Level Emphasis - { "3D_GLDZM_LDHGLE", Feature3D::GLDZM_LDHGLE }, // Large Distance High Grey Level Emphasis - { "3D_GLDZM_GLNU", Feature3D::GLDZM_GLNU }, // Grey Level Non Uniformity - { "3D_GLDZM_GLNUN", Feature3D::GLDZM_GLNUN }, // Grey Level Non Uniformity Normalized - { "3D_GLDZM_ZDNU", Feature3D::GLDZM_ZDNU }, // Zone Distance Non Uniformity - { "3D_GLDZM_ZDNUN", Feature3D::GLDZM_ZDNUN }, // Zone Distance Non Uniformity Normalized - { "3D_GLDZM_ZP", Feature3D::GLDZM_ZP }, // Zone Percentage - { "3D_GLDZM_GLM", Feature3D::GLDZM_GLM }, // Grey Level Mean - { "3D_GLDZM_GLV", Feature3D::GLDZM_GLV }, // Grey Level Variance - { "3D_GLDZM_ZDM", Feature3D::GLDZM_ZDM }, // Zone Distance Mean - { "3D_GLDZM_ZDV", Feature3D::GLDZM_ZDV }, // Zone Distance Variance - { "3D_GLDZM_ZDE", Feature3D::GLDZM_ZDE }, // Zone Distance Entropy + { "3SPAT_MOMENT_00", Feature3D::SPAT_MOMENT_00 }, + { "3SPAT_MOMENT_01", Feature3D::SPAT_MOMENT_01 }, + { "3SPAT_MOMENT_02", Feature3D::SPAT_MOMENT_02 }, + { "3SPAT_MOMENT_03", Feature3D::SPAT_MOMENT_03 }, + { "3SPAT_MOMENT_10", Feature3D::SPAT_MOMENT_10 }, + { "3SPAT_MOMENT_11", Feature3D::SPAT_MOMENT_11 }, + { "3SPAT_MOMENT_12", Feature3D::SPAT_MOMENT_12 }, + { "3SPAT_MOMENT_13", Feature3D::SPAT_MOMENT_13 }, + { "3SPAT_MOMENT_20", Feature3D::SPAT_MOMENT_20 }, + { "3SPAT_MOMENT_21", Feature3D::SPAT_MOMENT_21 }, + { "3SPAT_MOMENT_22", Feature3D::SPAT_MOMENT_22 }, + { "3SPAT_MOMENT_23", Feature3D::SPAT_MOMENT_23 }, + { "3SPAT_MOMENT_30", Feature3D::SPAT_MOMENT_30 }, + + // Texture / GLCM + { "3GLCM_ACOR", Feature3D::GLCM_ACOR }, + { "3GLCM_ASM", Feature3D::GLCM_ASM }, + { "3GLCM_CLUPROM", Feature3D::GLCM_CLUPROM }, + { "3GLCM_CLUSHADE", Feature3D::GLCM_CLUSHADE }, + { "3GLCM_CLUTEND", Feature3D::GLCM_CLUTEND }, + { "3GLCM_CONTRAST", Feature3D::GLCM_CONTRAST }, + { "3GLCM_CORRELATION", Feature3D::GLCM_CORRELATION }, + { "3GLCM_DIFAVE", Feature3D::GLCM_DIFAVE }, + { "3GLCM_DIFENTRO", Feature3D::GLCM_DIFENTRO }, + { "3GLCM_DIFVAR", Feature3D::GLCM_DIFVAR }, + { "3GLCM_DIS", Feature3D::GLCM_DIS }, + { "3GLCM_ENERGY", Feature3D::GLCM_ENERGY }, + { "3GLCM_ENTROPY", Feature3D::GLCM_ENTROPY }, + { "3GLCM_HOM1", Feature3D::GLCM_HOM1 }, + { "3GLCM_HOM2", Feature3D::GLCM_HOM2 }, + { "3GLCM_ID", Feature3D::GLCM_ID }, + { "3GLCM_IDN", Feature3D::GLCM_IDN }, + { "3GLCM_IDM", Feature3D::GLCM_IDM }, + { "3GLCM_IDMN", Feature3D::GLCM_IDMN }, + { "3GLCM_INFOMEAS1", Feature3D::GLCM_INFOMEAS1 }, + { "3GLCM_INFOMEAS2", Feature3D::GLCM_INFOMEAS2 }, + { "3GLCM_IV", Feature3D::GLCM_IV }, + { "3GLCM_JAVE", Feature3D::GLCM_JAVE }, + { "3GLCM_JE", Feature3D::GLCM_JE }, + { "3GLCM_JMAX", Feature3D::GLCM_JMAX }, + { "3GLCM_JVAR", Feature3D::GLCM_JVAR }, + { "3GLCM_SUMAVERAGE", Feature3D::GLCM_SUMAVERAGE }, + { "3GLCM_SUMENTROPY", Feature3D::GLCM_SUMENTROPY }, + { "3GLCM_SUMVARIANCE", Feature3D::GLCM_SUMVARIANCE }, + { "3GLCM_VARIANCE", Feature3D::GLCM_VARIANCE }, + { "3GLCM_ASM_AVE", Feature3D::GLCM_ASM_AVE }, + { "3GLCM_ACOR_AVE", Feature3D::GLCM_ACOR_AVE }, + { "3GLCM_CLUPROM_AVE", Feature3D::GLCM_CLUPROM_AVE }, + { "3GLCM_CLUSHADE_AVE", Feature3D::GLCM_CLUSHADE_AVE }, + { "3GLCM_CLUTEND_AVE", Feature3D::GLCM_CLUTEND_AVE }, + { "3GLCM_CONTRAST_AVE", Feature3D::GLCM_CONTRAST_AVE }, + { "3GLCM_CORRELATION_AVE", Feature3D::GLCM_CORRELATION_AVE }, + { "3GLCM_DIFAVE_AVE", Feature3D::GLCM_DIFAVE_AVE }, + { "3GLCM_DIFENTRO_AVE", Feature3D::GLCM_DIFENTRO_AVE }, + { "3GLCM_DIFVAR_AVE", Feature3D::GLCM_DIFVAR_AVE }, + { "3GLCM_DIS_AVE", Feature3D::GLCM_DIS_AVE }, + { "3GLCM_ENERGY_AVE", Feature3D::GLCM_ENERGY_AVE }, + { "3GLCM_ENTROPY_AVE", Feature3D::GLCM_ENTROPY_AVE }, + { "3GLCM_HOM1_AVE", Feature3D::GLCM_HOM1_AVE }, + { "3GLCM_ID_AVE", Feature3D::GLCM_ID_AVE }, + { "3GLCM_IDN_AVE", Feature3D::GLCM_IDN_AVE }, + { "3GLCM_IDM_AVE", Feature3D::GLCM_IDM_AVE }, + { "3GLCM_IDMN_AVE", Feature3D::GLCM_IDMN_AVE }, + { "3GLCM_IV_AVE", Feature3D::GLCM_IV_AVE }, + { "3GLCM_JAVE_AVE", Feature3D::GLCM_JAVE_AVE }, + { "3GLCM_JE_AVE", Feature3D::GLCM_JE_AVE }, + { "3GLCM_INFOMEAS1_AVE", Feature3D::GLCM_INFOMEAS1_AVE }, + { "3GLCM_INFOMEAS2_AVE", Feature3D::GLCM_INFOMEAS2_AVE }, + { "3GLCM_VARIANCE_AVE", Feature3D::GLCM_VARIANCE_AVE }, + { "3GLCM_JMAX_AVE", Feature3D::GLCM_JMAX_AVE }, + { "3GLCM_JVAR_AVE", Feature3D::GLCM_JVAR_AVE }, + { "3GLCM_SUMAVERAGE_AVE", Feature3D::GLCM_SUMAVERAGE_AVE }, + { "3GLCM_SUMENTROPY_AVE", Feature3D::GLCM_SUMENTROPY_AVE }, + { "3GLCM_SUMVARIANCE_AVE", Feature3D::GLCM_SUMVARIANCE_AVE }, + + { "3GLDZM_SDE", Feature3D::GLDZM_SDE}, + { "3GLDZM_LDE", Feature3D::GLDZM_LDE }, + { "3GLDZM_LGLZE", Feature3D::GLDZM_LGLZE }, + { "3GLDZM_HGLZE", Feature3D::GLDZM_HGLZE }, + { "3GLDZM_SDLGLE", Feature3D::GLDZM_SDLGLE }, + { "3GLDZM_SDHGLE", Feature3D::GLDZM_SDHGLE }, + { "3GLDZM_LDLGLE", Feature3D::GLDZM_LDLGLE }, + { "3GLDZM_LDHGLE", Feature3D::GLDZM_LDHGLE }, + { "3GLDZM_GLNU", Feature3D::GLDZM_GLNU }, + { "3GLDZM_GLNUN", Feature3D::GLDZM_GLNUN }, + { "3GLDZM_ZDNU", Feature3D::GLDZM_ZDNU }, + { "3GLDZM_ZDNUN", Feature3D::GLDZM_ZDNUN }, + { "3GLDZM_ZP", Feature3D::GLDZM_ZP }, + { "3GLDZM_GLM", Feature3D::GLDZM_GLM }, + { "3GLDZM_GLV", Feature3D::GLDZM_GLV }, + { "3GLDZM_ZDM", Feature3D::GLDZM_ZDM }, + { "3GLDZM_ZDV", Feature3D::GLDZM_ZDV }, + { "3GLDZM_ZDE", Feature3D::GLDZM_ZDE }, }; std::map UserFacing3dFeaturegroupNames = @@ -727,6 +788,7 @@ namespace Nyxus { "*3D_ALL_INTENSITY*", Fgroup3D::FG3_INTENSITY }, { "*3D_ALL_MORPHOLOGY*", Fgroup3D::FG3_MORPHOLOGY }, { "*3D_ALL_TEXTURE*", Fgroup3D::FG3_TEXTURE }, + { "*3D_GLCM*", Fgroup3D::FG3_GLCM }, { "*3D_GLDZM*", Fgroup3D::FG3_GLDZM }, { "*3D_ALL_NEIGHBOR*", Fgroup3D::FG3_NEIG }, { "*3D_MOMENTS*", Fgroup3D::FG3_MOMENTS }, diff --git a/src/nyx/featureset.h b/src/nyx/featureset.h index d14e6cdb..faf0b14d 100644 --- a/src/nyx/featureset.h +++ b/src/nyx/featureset.h @@ -658,6 +658,67 @@ namespace Nyxus SPAT_MOMENT_23, SPAT_MOMENT_30, + // Texture / GLCM + GLCM_ACOR, // Autocorrelation, IBSI # QWB0 + GLCM_ASM, // Angular second moment IBSI # 8ZQL + GLCM_CLUPROM, // Cluster prominence, IBSI # AE86 + GLCM_CLUSHADE, // Cluster shade, IBSI # 7NFM + GLCM_CLUTEND, // Cluster tendency, IBSI # DG8W + GLCM_CONTRAST, // Contrast, IBSI # ACUI + GLCM_CORRELATION, // Correlation, IBSI # NI2N + GLCM_DIFAVE, // Difference average, IBSI # TF7R + GLCM_DIFENTRO, // Difference entropy, IBSI # NTRS + GLCM_DIFVAR, // Difference variance, IBSI # D3YU + GLCM_DIS, // Dissimilarity, IBSI # 8S9J + GLCM_ENERGY, // Energy + GLCM_ENTROPY, // Entropy + GLCM_HOM1, // Homogeneity-1 (PyR) + GLCM_HOM2, // Homogeneity-2 (PyR) + GLCM_ID, // Inv diff, IBSI # IB1Z + GLCM_IDN, // Inv diff normalized, IBSI # NDRX + GLCM_IDM, // Inv diff mom, IBSI # WF0Z + GLCM_IDMN, // Inv diff mom normalized, IBSI # 1QCO + GLCM_INFOMEAS1, // Information measure of correlation 1, IBSI # R8DG + GLCM_INFOMEAS2, // Information measure of correlation 2, IBSI # JN9H + GLCM_IV, // Inv variance, IBSI # E8JP + GLCM_JAVE, // Joint average, IBSI # 60VM + GLCM_JE, // Joint entropy, IBSI # TU9B + GLCM_JMAX, // Joint max (aka PyR max probability), IBSI # GYBY + GLCM_JVAR, // Joint var (aka PyR Sum of Squares), IBSI # UR99 + GLCM_SUMAVERAGE, // Sum average, IBSI # ZGXS + GLCM_SUMENTROPY, // Sum entropy, IBSI # P6QZ + GLCM_SUMVARIANCE, // Sum variance, IBSI # OEEB + GLCM_VARIANCE, // Variance + GLCM_ASM_AVE, + GLCM_ACOR_AVE, + GLCM_CLUPROM_AVE, + GLCM_CLUSHADE_AVE, + GLCM_CLUTEND_AVE, + GLCM_CONTRAST_AVE, + GLCM_CORRELATION_AVE, + GLCM_DIFAVE_AVE, + GLCM_DIFENTRO_AVE, + GLCM_DIFVAR_AVE, + GLCM_DIS_AVE, + GLCM_ENERGY_AVE, + GLCM_ENTROPY_AVE, + GLCM_HOM1_AVE, + GLCM_ID_AVE, + GLCM_IDN_AVE, + GLCM_IDM_AVE, + GLCM_IDMN_AVE, + GLCM_IV_AVE, + GLCM_JAVE_AVE, + GLCM_JE_AVE, + GLCM_INFOMEAS1_AVE, + GLCM_INFOMEAS2_AVE, + GLCM_VARIANCE_AVE, + GLCM_JMAX_AVE, + GLCM_JVAR_AVE, + GLCM_SUMAVERAGE_AVE, + GLCM_SUMENTROPY_AVE, + GLCM_SUMVARIANCE_AVE, + // Texture / GLDZM GLDZM_SDE, // Small Distance Emphasis GLDZM_LDE, // Large Distance Emphasis @@ -722,6 +783,7 @@ namespace Nyxus FG3_INTENSITY, FG3_MORPHOLOGY, FG3_TEXTURE, // 3D_GLCM + 3D_GLRLM + 3D_DLSZM + etc + FG3_GLCM, FG3_GLDZM, FG3_NEIG, FG3_MOMENTS, @@ -866,9 +928,11 @@ class FeatureSet return true; return false; } - int numOfEnabled() { + int numOfEnabled (int dim) + { int cnt = 0; - for (int i = 0; i < (int) Nyxus::Feature2D::_COUNT_; i++) + int n = dim == 2 ? (int)Nyxus::Feature2D::_COUNT_ : (int)Nyxus::Feature3D::_COUNT_; + for (int i = 0; i < n; i++) if (m_enabledFeatures[i]) cnt++; return cnt; diff --git a/src/nyx/reduce_trivial_rois.cpp b/src/nyx/reduce_trivial_rois.cpp index 481bd96d..a172d12f 100644 --- a/src/nyx/reduce_trivial_rois.cpp +++ b/src/nyx/reduce_trivial_rois.cpp @@ -42,6 +42,7 @@ #include "features/zernike.h" #include "features/3d_intensity.h" +#include "features/3d_glcm.h" #include "features/3d_gldzm.h" //--future-- #include "features/3d_surface.h" @@ -372,7 +373,12 @@ namespace Nyxus STOPWATCH("3D intensity/3Dintensity/3DI/#FFFF00", "\t="); runParallel (D3_PixelIntensityFeatures::reduce, n_threads, work_per_thread, job_size, &L, &roiData); } - //==== texture + //==== texture + if (D3_GLCM_feature::required(theFeatureSet)) + { + STOPWATCH("3D GLCM/3DGLCM/3DGLCM/#FFFF00", "\t="); + runParallel (D3_GLCM_feature::reduce, n_threads, work_per_thread, job_size, &L, &roiData); + } if (D3_GLDZM_feature::required(theFeatureSet)) { STOPWATCH("3D GLDZM/3DGLDZM/3DGLDZM/#FFFF00", "\t=");