Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,18 +263,18 @@ Nyxus can also be [built from source](#building-from-source) and used from the c

All parameters to configure Nyxus are available to set within the constructor. These parameters can also be updated after the object is created using the `set_params`
method. This method takes in keyword arguments where the key is a valid parameter in Nyxus and the value is the updated value for the parameter. For example,
to update the `coarse_gray_depth` to 256 and the `gabor_f0` parameter to 0.1, the following can be done:
to update the `gabor_f0` parameter to 0.1, the following can be done:

```python
from nyxus import Nyxus
nyx = Nyxus(["*ALL*"])
intensityDir = "/path/to/images/intensities/"
maskDir = "/path/to/images/labels/"
features = nyx.featurize_directory (intensityDir, maskDir)
nyx.set_params(coarse_gray_depth=256, gabor_f0=0.1)
nyx.set_params(gabor_f0=0.1)
```

A list of valid parameters is included in the documentation for this method.
A list of valid parameters is included in the documentation for this method. `coarse_gray_depth` is the exception: the grey binning is fixed when the object is constructed, so `set_params` accepts only the current value and a different depth goes to the constructor.

To get the values of the parameters in Nyxus, the `get_params` method is used. If no arguments are passed to this function, then a dictionary mapping all of the variable names to the respective value is returned. For example,

Expand Down
6 changes: 3 additions & 3 deletions docs/source/cmdline_and_examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ The `features` variable will now use the custom names, as shown below

All parameters to configure Nyxus are available to set within the constructor. These parameters can also be updated after the object is created using the `set_params`
method. This method takes in keyword arguments where the key is a valid parameter in Nyxus and the value is the updated value for the parameter. For example,
to update the `coarse_gray_depth` to 256 and the `gabor_f0` parameter to 0.1, the following can be done:
to update the `gabor_f0` parameter to 0.1, the following can be done:

.. code-block:: python

Expand All @@ -397,10 +397,10 @@ to update the `coarse_gray_depth` to 256 and the `gabor_f0` parameter to 0.1, th
maskDir = "/path/to/images/labels/"
features = nyx.featurize_directory (intensityDir, maskDir)

nyx.set_params(coarse_gray_depth=256, gabor_f0=0.1)
nyx.set_params(gabor_f0=0.1)


A list of valid parameters is included in the documentation for this method.
A list of valid parameters is included in the documentation for this method. `coarse_gray_depth` is the exception: the grey binning is fixed when the object is constructed, so `set_params` accepts only the current value and a different depth goes to the constructor.

To get the values of the parameters in Nyxus, the `get_params` method is used. If no arguments are passed to this function, then a dictionary mapping all of the variable names to the respective value is returned. For example,

Expand Down
10 changes: 10 additions & 0 deletions src/nyx/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,13 @@
constexpr double DEFAULT_T_INDEX = 0.0;
constexpr double DEFAULT_C_INDEX = 0.0; // default channel index for single-channel / non-OME input
constexpr int DEFAULT_NUM_HISTO_BINS = 24;

// The number of bins the intensity histograms take from a coarse grey depth. Its sign selects a
// texture binning scheme, which a histogram has no use for, so only the magnitude is a bin count;
// 0, which bins no texture family at all, leaves a histogram at its default.
constexpr int histogram_bin_count (int coarse_gray_depth)
{
return coarse_gray_depth > 0 ? coarse_gray_depth
: coarse_gray_depth < 0 ? -coarse_gray_depth
: DEFAULT_NUM_HISTO_BINS;
}
7 changes: 7 additions & 0 deletions src/nyx/env_features.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,13 @@ void Environment::compile_feature_settings()
s[(int)NyxSetting::GLCM_OFFSET].ival = 1;
s[(int)NyxSetting::GLCM_GREYDEPTH].ival = get_coarse_gray_depth();
s[(int)NyxSetting::GLCM_NUMANG].ival = (int)GLCMFeature::angles.size();
// The other per-family grey depths start at the common one too, so the coarse grey depth
// bins every texture family; a family's own metaparameter ("3glrlm/greydepth=...") then
// overrides it for that family alone.
s[(int)NyxSetting::GLDM_GREYDEPTH].ival = get_coarse_gray_depth();
s[(int)NyxSetting::GLRLM_GREYDEPTH].ival = get_coarse_gray_depth();
s[(int)NyxSetting::GLSZM_GREYDEPTH].ival = get_coarse_gray_depth();
s[(int)NyxSetting::NGTDM_GREYDEPTH].ival = get_coarse_gray_depth();
Comment on lines +738 to +744

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: These lines move the default output of 3D GLRLM, GLSZM, GLDM and NGTDM from raw levels to 64 MATLAB-style levels, so every existing 3D run now returns different numbers for those four families. On top of that, set_params(coarse_gray_depth=...) and set_environment_params(coarse_gray_depth=...) used to return and now raise ValueError (nyxus.py _without_coarse_gray_depth). Both changes break existing users. The commit is still fix(binning): ..., with no ! and no BREAKING CHANGE: footer (git log -1 --format=%B 1a6f5dcd), so release-please files it under Bug Fixes and bumps a patch version. The repo already marks breaking changes this way (feat(omezarr)!: with a BREAKING CHANGE: footer).

Please make the commit and the PR title fix(binning)!: ... and add a BREAKING CHANGE: footer that states three things:

  • the new 3D default;
  • that --coarseGrayDepth=0 or 3<family>/greydepth=0 gives the old output;
  • that set_params/set_environment_params now refuse a different coarse_gray_depth.

// NGTDM-specific default (otherwise zero-initialised). NGTDM_RADIUS=0 empties the
// neighbourhood - the scan visits only the voxel of consideration and skips it - so no
// voxel is recorded as having a neighbour, the matrix stays empty and every 3D NGTDM
Expand Down
64 changes: 34 additions & 30 deletions src/nyx/env_metaparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,22 @@
#include "helpers/timing.h"
#include "version.h"

// Parses the value of a <family>/greydepth metaparameter with the same rule as --coarseGrayDepth:
// returns the error message, or nothing once 'n_greys' holds the depth
static std::optional<std::string> parse_greydepth_metaparam (const std::string& raw, const std::string& name, int& n_greys)
{
switch (Nyxus::parse_grey_depth (raw, n_greys))
{
case Nyxus::GreyDepthParse::not_an_integer:
return "error: cannot parse value \"" + raw + "\" of " + name + ": expecting an integer";
case Nyxus::GreyDepthParse::out_of_range:
return "error: value \"" + raw + "\" of " + name + " is out of range: the magnitude must be at most " + std::to_string (INT_MAX);
case Nyxus::GreyDepthParse::ok:
break;
}
return std::nullopt;
}

std::optional<std::string> Environment::set_metaparam (const std::string & p_val)
{
// parse LHS=RHS
Expand All @@ -82,12 +98,10 @@ std::optional<std::string> Environment::set_metaparam (const std::string & p_val
// check feature-specific parameter name
if (ppath[1] == "greydepth")
{
// interpret eq_sides[1] as int
// interpret eq_sides[1] as a grey depth
int n_greys;
if (Nyxus::parse_as_int(eq_sides[1], n_greys) == false)
{
return "error: cannot parse value \"" + eq_sides[1] + "\" of glcm/greydepth: expecting an integer";
}
if (auto err = parse_greydepth_metaparam (eq_sides[1], "glcm/greydepth", n_greys))
return err;
STNGS_GLCM_GREYDEPTH(fsett_GLCM) = n_greys;
}
else if (ppath[1] == "offset")
Expand All @@ -111,12 +125,10 @@ std::optional<std::string> Environment::set_metaparam (const std::string & p_val
// check feature-specific parameter name
if (ppath[1] == "greydepth")
{
// interpret eq_sides[1] as int
// interpret eq_sides[1] as a grey depth
int n_greys;
if (Nyxus::parse_as_int(eq_sides[1], n_greys) == false)
{
return "error: cannot parse value \"" + eq_sides[1] + "\" of 3glcm/greydepth: expecting an integer";
}
if (auto err = parse_greydepth_metaparam (eq_sides[1], "3glcm/greydepth", n_greys))
return err;
STNGS_GLCM_GREYDEPTH (fsett_D3_GLCM) = n_greys;
}
else if (ppath[1] == "offset")
Expand Down Expand Up @@ -159,12 +171,10 @@ std::optional<std::string> Environment::set_metaparam (const std::string & p_val
// check feature-specific parameter name
if (ppath[1] == "greydepth")
{
// interpret eq_sides[1] as int
// interpret eq_sides[1] as a grey depth
int n_greys;
if (Nyxus::parse_as_int(eq_sides[1], n_greys) == false)
{
return "error: cannot parse value \"" + eq_sides[1] + "\" of 3gldm/greydepth: expecting an integer";
}
if (auto err = parse_greydepth_metaparam (eq_sides[1], "3gldm/greydepth", n_greys))
return err;
STNGS_GLDM_GREYDEPTH (fsett_D3_GLDM) = n_greys;
}
else
Expand All @@ -177,12 +187,10 @@ std::optional<std::string> Environment::set_metaparam (const std::string & p_val
// check feature-specific parameter name
if (ppath[1] == "greydepth")
{
// interpret eq_sides[1] as int
// interpret eq_sides[1] as a grey depth
int n_greys;
if (Nyxus::parse_as_int(eq_sides[1], n_greys) == false)
{
return "error: cannot parse value \"" + eq_sides[1] + "\" of 3ngtdm/greydepth: expecting an integer";
}
if (auto err = parse_greydepth_metaparam (eq_sides[1], "3ngtdm/greydepth", n_greys))
return err;
STNGS_NGTDM_GREYDEPTH(fsett_D3_NGTDM) = n_greys;
}
else
Expand All @@ -206,12 +214,10 @@ std::optional<std::string> Environment::set_metaparam (const std::string & p_val
// check feature-specific parameter name
if (ppath[1] == "greydepth")
{
// interpret eq_sides[1] as int
// interpret eq_sides[1] as a grey depth
int n_greys;
if (Nyxus::parse_as_int(eq_sides[1], n_greys) == false)
{
return "error: cannot parse value \"" + eq_sides[1] + "\" of 3glrlm/greydepth: expecting an integer";
}
if (auto err = parse_greydepth_metaparam (eq_sides[1], "3glrlm/greydepth", n_greys))
return err;
STNGS_GLRLM_GREYDEPTH (fsett_D3_GLRLM) = n_greys;
}
else
Expand All @@ -224,12 +230,10 @@ std::optional<std::string> Environment::set_metaparam (const std::string & p_val
// check feature-specific parameter name
if (ppath[1] == "greydepth")
{
// interpret eq_sides[1] as int
// interpret eq_sides[1] as a grey depth
int n_greys;
if (Nyxus::parse_as_int(eq_sides[1], n_greys) == false)
{
return "error: cannot parse value \"" + eq_sides[1] + "\" of 3glszm/greydepth: expecting an integer";
}
if (auto err = parse_greydepth_metaparam (eq_sides[1], "3glszm/greydepth", n_greys))
return err;
STNGS_GLSZM_GREYDEPTH(fsett_D3_GLSZM) = n_greys;
}
else
Expand Down
14 changes: 10 additions & 4 deletions src/nyx/environment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ void Environment::show_cmdline_help()
<< "\t\t\tDefault: 1 \n"
<< "\t\t" << OPT << clo_PXLDIST << "=<number of pixels as neighbor features radius> \n"
<< "\t\t\tDefault: 5 \n"
<< "\t\t" << OPT << clo_COARSEGRAYDEPTH << "=<custom number of grayscale levels> \n"
<< "\t\t" << OPT << clo_COARSEGRAYDEPTH << "=<texture grey binning: N > 0 is N MATLAB-style levels, 0 is no binning, N < 0 is |N| PyRadiomics-style bins> \n"
<< "\t\t\tDefault: 64 \n"
<< "\t\t" << OPT << clo_GLCMANGLES << "=<one or more comma separated rotation angles from set {0, 45, 90, and 135}> \n"
<< "\t\t\tDefault: 0,45,90,135 \n"
Expand Down Expand Up @@ -665,11 +665,17 @@ bool Environment::parse_cmdline(int argc, char** argv)
// parse COARSEGRAYDEPTH
if (!raw_coarse_grayscale_depth.empty())
{
// string -> integer
if (sscanf(raw_coarse_grayscale_depth.c_str(), "%d", &coarse_grayscale_depth) != 1)
// string -> integer; every sign is meaningful, and the magnitude has to be an int (see parse_grey_depth)
switch (Nyxus::parse_grey_depth (raw_coarse_grayscale_depth, coarse_grayscale_depth))
{
case Nyxus::GreyDepthParse::not_an_integer:
std::cerr << "Error: " << clo_COARSEGRAYDEPTH << "=" << raw_coarse_grayscale_depth << ": expecting an integer constant\n";
return false;
case Nyxus::GreyDepthParse::out_of_range:
std::cerr << "Error: " << clo_COARSEGRAYDEPTH << "=" << raw_coarse_grayscale_depth << ": out of range, the magnitude must be at most " << INT_MAX << "\n";
return false;
case Nyxus::GreyDepthParse::ok:
break;
}
}

Expand Down Expand Up @@ -930,7 +936,7 @@ int Environment::get_coarse_gray_depth()
return coarse_grayscale_depth;
}

void Environment::set_coarse_gray_depth(unsigned int new_depth)
void Environment::set_coarse_gray_depth(int new_depth)
{
coarse_grayscale_depth = new_depth;
}
Expand Down
2 changes: 1 addition & 1 deletion src/nyx/environment.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class Environment: public BasicEnvironment
int get_floating_point_precision();

int get_coarse_gray_depth();
void set_coarse_gray_depth(unsigned int new_depth);
void set_coarse_gray_depth(int new_depth);

// implementation of SKIPROI
bool roi_is_blacklisted (const std::string& fname, int roi_label);
Expand Down
24 changes: 7 additions & 17 deletions src/nyx/features/3d_glcm.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <numeric>
#include "3d_glcm.h"
#include "3d_ooc_volume.h"
#include "../helpers/helpers.h"
#include "../environment.h"

Expand Down Expand Up @@ -40,19 +41,13 @@ void D3_GLCM_feature::calculate (LR& r, const Fsettings& s)
// clear the feature values buffers
clear_result_buffers();

// grey-bin intensities
int w = r.aux_image_cube.width(),
h = r.aux_image_cube.height(),
d = r.aux_image_cube.depth();

SimpleCube<PixIntens> D;
D.allocate (w,h,d);

auto greyBinningInfo = STNGS_GLCM_GREYDEPTH(s); // former Nyxus::theEnvironment.get_coarse_gray_depth()
if (STNGS_IBSI(s)) // former Nyxus::theEnvironment.ibsi_compliance
greyBinningInfo = 0;

bin_intensities_3d (D, r.aux_image_cube, r.aux_min, r.aux_max, greyBinningInfo);
// grey-bin the ROI's voxels; the background outside the ROI takes level 0, which pairs with nothing
SimpleCube<PixIntens> D;
Nyxus::bin_roi_3d (D, r, greyBinningInfo);

// calculate features for all the 13 directions
for (const ShiftToNeighbor & sh : shifts)
Expand Down Expand Up @@ -340,10 +335,9 @@ void D3_GLCM_feature::calculateCoocMatAtAngle(
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;
// skip pairs with a background cell, which holds level 0 under every binning scheme

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This comment and the one at 3d_glcm_nontriv.cpp:92 say what the skip does now, but not what was wrong. Under MATLAB-style binning the background used to sit at level 1 and pair with the ROI, and the zero skip ran only with no binning. Please name that on the changed line, e.g. // MATLAB-style binning put the background at level 1 and paired it with the ROI; bin_roi_3d leaves it at 0, skipped under every scheme.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Diverted The comments state what the code does now, by this repo's convention for changed lines; what was wrong is recorded in c42d6fa's message, which names the level-1 background pairing under MATLAB-style binning.

if (lvl_a == 0 || lvl_b == 0)
continue;

// 0-based grey tone indices, hence '-1'
int a = lvl_a,
Expand All @@ -352,10 +346,6 @@ void D3_GLCM_feature::calculateCoocMatAtAngle(
// 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'
Expand Down
22 changes: 8 additions & 14 deletions src/nyx/features/3d_glcm_nontriv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,14 @@ void D3_GLCM_feature::osized_calculate (LR& r, const Fsettings& s, ImageLoader&)
int greyInfo = STNGS_IBSI(s) ? 0 : STNGS_GLCM_GREYDEPTH(s);
bool ibsi = STNGS_IBSI(s);
double soft_nan = STNGS_NAN(s);
PixIntens mn = r.aux_min, mx = r.aux_max;

// background cells carry the binned level of intensity 0, as they do in the in-core cube
const PixIntens bg = TextureFeature::bin_pixel (0, mn, mx, greyInfo);
Nyxus::OocBinnedVolume vol (r, [mn, mx, greyInfo](PixIntens v) { return TextureFeature::bin_pixel (v, mn, mx, greyInfo); },
bg, D3_GLCM_feature::offset + 1);
// as in calculate(), background cells carry level 0 and every ROI voxel 1 or more
Nyxus::OocBinnedVolume vol (r, Nyxus::roi_voxel_binning (r, greyInfo), 0, D3_GLCM_feature::offset + 1);
const int W = vol.width(), H = vol.height(), Dz = vol.depth();

// --- the grey levels I. Under radiomics binning they are the unique levels of the whole binned
// cube, so the background level counts when the bounding box has background.
// --- the grey levels I. Under radiomics binning they are the unique nonzero levels of the ROI's
// voxels.
PixIntens maxbin = 0;
std::set<PixIntens> uniq = vol.levels (/*with_background=*/ true, /*drop_zero=*/ true, maxbin);
std::set<PixIntens> uniq = vol.levels (/*with_background=*/ false, /*drop_zero=*/ true, maxbin);

I.clear();
if (radiomics_grey_binning(greyInfo))
Expand Down Expand Up @@ -93,14 +89,12 @@ void D3_GLCM_feature::osized_calculate (LR& r, const Fsettings& s, ImageLoader&)
// symmetric: radiomics, no binning, or symmetric_glcm
auto add_pair = [&](SimpleMatrix<double>& M, PixIntens lvl_base, PixIntens lvl_nbr)
{
if (ibsi_grey_binning(greyInfo))
if (lvl_nbr == 0 || lvl_base == 0)
return;
// a background cell holds level 0 under every binning scheme
if (lvl_nbr == 0 || lvl_base == 0)
return;
int a = (int) lvl_nbr, b = (int) lvl_base;
if (radiomics)
{
if (a == 0 || b == 0)
return;
a = rowLUT[a];
b = rowLUT[b];
}
Expand Down
Loading
Loading