Skip to content

Commit 62d3b6a

Browse files
authored
Python API support for the compatibility unit tests (#310)
1 parent 40bf073 commit 62d3b6a

File tree

7 files changed

+333
-11
lines changed

7 files changed

+333
-11
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ set(SOURCE
210210
src/nyx/environment.cpp
211211
src/nyx/environment_basic.cpp
212212
src/nyx/env_features.cpp
213+
src/nyx/env_metaparams.cpp
213214
src/nyx/feature_method.cpp
214215
src/nyx/feature_mgr.cpp
215216
src/nyx/feature_mgr_init.cpp

src/nyx/env_metaparams.cpp

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
#include <algorithm>
2+
#include <exception>
3+
#include <fstream>
4+
#include <iomanip>
5+
#include <iostream>
6+
#include <iterator>
7+
#include <sstream>
8+
#include <string>
9+
#include <tuple>
10+
#include <vector>
11+
12+
#include "environment.h"
13+
#include "featureset.h"
14+
15+
#include "features/basic_morphology.h"
16+
#include "features/chords.h"
17+
#include "features/convex_hull.h"
18+
#include "features/erosion.h"
19+
#include "features/caliper.h"
20+
#include "features/circle.h"
21+
#include "features/ellipse_fitting.h"
22+
#include "features/euler_number.h"
23+
#include "features/extrema.h"
24+
#include "features/fractal_dim.h"
25+
#include "features/geodetic_len_thickness.h"
26+
#include "features/gabor.h"
27+
#include "features/glcm.h"
28+
#include "features/gldm.h"
29+
#include "features/gldzm.h"
30+
#include "features/glrlm.h"
31+
#include "features/glszm.h"
32+
#include "features/hexagonality_polygonality.h"
33+
#include "features/2d_geomoments.h"
34+
#include "features/intensity.h"
35+
#include "features/neighbors.h"
36+
#include "features/ngldm.h"
37+
#include "features/ngtdm.h"
38+
#include "features/radial_distribution.h"
39+
#include "features/roi_radius.h"
40+
#include "features/zernike.h"
41+
42+
#include "features/3d_surface.h"
43+
#include "features/3d_glcm.h"
44+
#include "features/3d_gldm.h"
45+
#include "features/3d_ngldm.h"
46+
#include "features/3d_ngtdm.h"
47+
#include "features/3d_gldzm.h"
48+
#include "features/3d_glrlm.h"
49+
#include "features/3d_glszm.h"
50+
#include "features/3d_intensity.h"
51+
#include "features/3d_surface.h"
52+
53+
#include "features/focus_score.h"
54+
#include "features/power_spectrum.h"
55+
#include "features/saturation.h"
56+
#include "features/sharpness.h"
57+
58+
#include "helpers/helpers.h"
59+
#include "helpers/system_resource.h"
60+
#include "helpers/timing.h"
61+
#include "version.h"
62+
63+
std::optional<std::string> Environment::set_metaparam (const std::string & p_val)
64+
{
65+
// parse LHS=RHS
66+
std::vector<std::string> eq_sides;
67+
Nyxus::parse_delimited_string (p_val, "=", eq_sides);
68+
if (eq_sides.size() != 2)
69+
return "syntax error in \"" + p_val + "\": expecting <paramName>=<paramVal>";
70+
71+
// parse LHS
72+
std::vector<std::string> ppath;
73+
Nyxus::parse_delimited_string (eq_sides[0], "/", ppath);
74+
if (!(ppath.size() == 1 || ppath.size() == 2))
75+
return "syntax error in <paramName>=<paramVal> of \"" + p_val + "\": expecting <paramName> to be <feature name>/<parameter name> or <common parameter name>";
76+
77+
// feature parameters
78+
if (ppath.size() == 2)
79+
{
80+
if (ppath[0] == "3glcm")
81+
{
82+
// check feature-specific parameter name
83+
if (ppath[1] == "greydepth")
84+
{
85+
// interpret eq_sides[1] as int
86+
int n_greys;
87+
if (Nyxus::parse_as_int(eq_sides[1], n_greys) == false)
88+
{
89+
return "error: cannot parse value \"" + eq_sides[1] + "\" of 3glcm/greydepth: expecting an integer";
90+
}
91+
STNGS_GLCM_GREYDEPTH (fsett_D3_GLCM) = n_greys;
92+
}
93+
else if (ppath[1] == "offset")
94+
{
95+
// interpret eq_sides[1] as int
96+
int offs;
97+
if (Nyxus::parse_as_int(eq_sides[1], offs) == false)
98+
{
99+
return "error: cannot parse value \"" + eq_sides[1] + "\" of 3glcm/offset: expecting an integer";
100+
}
101+
STNGS_GLCM_OFFSET (fsett_D3_GLCM) = offs;
102+
}
103+
else if (ppath[1] == "numang")
104+
{
105+
// interpret eq_sides[1] as int
106+
int n_angs;
107+
if (Nyxus::parse_as_int(eq_sides[1], n_angs) == false)
108+
{
109+
return "error: cannot parse value \"" + eq_sides[1] + "\" of 3glcm/numang: expecting an integer";
110+
}
111+
STNGS_GLCM_NUMANG(fsett_D3_GLCM) = n_angs;
112+
}
113+
else if (ppath[1] == "sparseintensities")
114+
{
115+
// interpret eq_sides[1] as boolean
116+
bool bval;
117+
if (Nyxus::parse_as_bool(eq_sides[1], bval) == false)
118+
{
119+
return "error: cannot parse value \"" + eq_sides[1] + "\" of 3glcm/sparseintensities: expecting a boolean (\"true\" or \"false\")";
120+
}
121+
STNGS_GLCM_SPARSEINTENS(fsett_D3_GLCM) = bval;
122+
}
123+
else
124+
{
125+
return "error: unrecognized feature parameter of feature 3glcm: \"" + ppath[1] + "\"";
126+
}
127+
}
128+
else
129+
{
130+
return "error: unrecognized feature \"" + ppath[0] + "\"";
131+
}
132+
}
133+
134+
return std::nullopt;
135+
}
136+
137+
std::optional<std::string> Environment::get_metaparam (double & p_val, const std::string& p_name)
138+
{
139+
// parse LHS
140+
std::vector<std::string> ppath;
141+
Nyxus::parse_delimited_string(p_name, "/", ppath);
142+
if (!(ppath.size() == 1 || ppath.size() == 2))
143+
return "syntax error in <paramName>=<paramVal> of \"" + p_name + "\": expecting <paramName> to be <feature name>/<parameter name> or <common parameter name>";
144+
145+
// feature parameters
146+
if (ppath.size() == 2)
147+
{
148+
if (ppath[0] == "3glcm")
149+
{
150+
// check feature-specific parameter name
151+
if (ppath[1] == "greydepth")
152+
{
153+
// interpret eq_sides[1] as int
154+
int ival = STNGS_GLCM_GREYDEPTH(fsett_D3_GLCM);
155+
p_val = (double) ival;
156+
}
157+
else if (ppath[1] == "offset")
158+
{
159+
// interpret eq_sides[1] as int
160+
int ival = STNGS_GLCM_OFFSET(fsett_D3_GLCM);
161+
p_val = (double)ival;
162+
}
163+
else if (ppath[1] == "numang")
164+
{
165+
// interpret eq_sides[1] as int
166+
int ival = STNGS_GLCM_NUMANG(fsett_D3_GLCM);
167+
p_val = (double)ival;
168+
}
169+
else if (ppath[1] == "sparseintensities")
170+
{
171+
// interpret eq_sides[1] as boolean
172+
bool bval = STNGS_GLCM_SPARSEINTENS(fsett_D3_GLCM);
173+
p_val = (double)bval;
174+
}
175+
else
176+
{
177+
return "error: unrecognized feature parameter of feature 3glcm: \"" + ppath[1] + "\"";
178+
}
179+
}
180+
else
181+
{
182+
return "error: unrecognized feature \"" + ppath[0] + "\"";
183+
}
184+
}
185+
186+
return std::nullopt;
187+
}

src/nyx/environment.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,10 @@ class Environment: public BasicEnvironment
200200
void compile_feature_settings();
201201
const Fsettings & get_feature_settings (const std::type_info& ftype);
202202

203+
// Meta-parameters
204+
std::optional<std::string> set_metaparam (const std::string & p_val);
205+
std::optional<std::string> get_metaparam(double& p_val, const std::string& p_name);
206+
203207
// Features
204208
FeatureSet theFeatureSet;
205209
FeatureManager theFeatureMgr;

src/nyx/feature_settings.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,7 @@ enum class NyxSetting : int
4747

4848
// feature-specific settings
4949
#define STNGS_GLCM_GREYDEPTH(obj) (obj[(int)NyxSetting::GLCM_GREYDEPTH].ival)
50+
#define STNGS_GLCM_OFFSET(obj) (obj[(int)NyxSetting::GLCM_OFFSET].ival)
51+
#define STNGS_GLCM_NUMANG(obj) (obj[(int)NyxSetting::GLCM_NUMANG].ival)
52+
#define STNGS_GLCM_SPARSEINTENS(obj) (obj[(int)NyxSetting::GLCM_SPARSEINTENS].ival)
5053

src/nyx/python/new_bindings_py.cpp

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,7 @@ namespace Nyxus {
2828
Environment & findenv (uint64_t instid)
2929
{
3030
// create if missing
31-
32-
//if (Nyxus::unique_pynyxus_ids.find(instid) == Nyxus::unique_pynyxus_ids.end())
33-
//{
34-
// Environment newEnv;
35-
// Nyxus::pynyxus_cache [instid] = newEnv;
36-
//}
37-
3831
auto [it, inserted] = Nyxus::pynyxus_cache.try_emplace(instid);
39-
4032
// find
4133
Environment& env = Nyxus::pynyxus_cache [instid];
4234
return env;
@@ -1030,10 +1022,32 @@ bool arrow_is_enabled_imp (uint64_t instid)
10301022
return env.arrow_is_enabled();
10311023
}
10321024

1025+
py::tuple set_metaparam_imp (uint64_t instid, const std::string p_val)
1026+
{
1027+
Environment & env = Nyxus::findenv (instid);
1028+
std::optional<std::string> mayBerror = env.set_metaparam (p_val);
1029+
if (mayBerror.has_value())
1030+
return py::make_tuple (false, mayBerror.value());
1031+
else
1032+
return py::make_tuple (true, "success");
1033+
}
1034+
1035+
py::tuple get_metaparam_imp (uint64_t instid, const std::string p_name)
1036+
{
1037+
Environment& env = Nyxus::findenv(instid);
1038+
double p_val;
1039+
std::optional<std::string> mayBerror = env.get_metaparam (p_val, p_name);
1040+
if (mayBerror.has_value())
1041+
return py::make_tuple (false, mayBerror.value());
1042+
else
1043+
return py::make_tuple (p_val, "");
1044+
}
1045+
1046+
1047+
10331048
PYBIND11_MODULE(backend, m)
10341049
{
10351050
m.doc() = "Nyxus";
1036-
10371051
m.def("initialize_environment", &initialize_environment, "Environment initialization");
10381052
m.def("featurize_directory_imp", &featurize_directory_imp, "Calculate features of images defined by intensity and mask image collection directories");
10391053
m.def("featurize_directory_3D_imp", &featurize_directory_3D_imp, "Calculate 3D features of images defined by intensity and mask image collection directories");
@@ -1054,6 +1068,8 @@ PYBIND11_MODULE(backend, m)
10541068
m.def("arrow_is_enabled_imp", &arrow_is_enabled_imp, "Check if arrow is enabled.");
10551069
m.def("get_arrow_file_imp", &get_arrow_file_imp, "Get path to arrow file");
10561070
m.def("get_parquet_file_imp", &get_parquet_file_imp, "Returns path to parquet file");
1071+
m.def("set_metaparam_imp", &set_metaparam_imp, "Setting a common or feature-specific metaparameter");
1072+
m.def("get_metaparam_imp", &get_metaparam_imp, "Getting a common or feature-specific metaparameter value");
10571073
}
10581074

10591075

src/nyx/python/nyxus/nyxus.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
get_params_imp,
1818
arrow_is_enabled_imp,
1919
get_arrow_file_imp,
20-
get_parquet_file_imp)
20+
get_parquet_file_imp,
21+
set_metaparam_imp,
22+
get_metaparam_imp)
2123

2224
import os
2325
import numpy as np
@@ -998,6 +1000,43 @@ def __init__(
9981000
# list of valid outputs that are used throughout featurize functions
9991001
self._valid_output_types = ['pandas', 'arrowipc', 'parquet']
10001002

1003+
def set_metaparam(
1004+
self,
1005+
paramval: str):
1006+
""" Sets feature-specific parameter to a new value
1007+
1008+
Parameters
1009+
-------------
1010+
paramval : str
1011+
feature-specific value, for example: "3glcm/greydepth=25"
1012+
1013+
Returns
1014+
---------
1015+
success and optional error details
1016+
"""
1017+
ok,errordetails = set_metaparam_imp (id(self), paramval)
1018+
if ok==False:
1019+
raise ValueError (f'Invalid metaparameter value {paramval}: {errordetails}')
1020+
1021+
def get_metaparam (
1022+
self,
1023+
paramname: str):
1024+
""" Gets feature-specific parameter
1025+
1026+
Parameters
1027+
-------------
1028+
paramval : str
1029+
feature-specific value, for example: "3glcm/greydepth"
1030+
1031+
Returns
1032+
---------
1033+
requested value or throws an exception
1034+
"""
1035+
val,errordetails = get_metaparam_imp (id(self), paramname)
1036+
if len(errordetails) > 0:
1037+
raise NameError (f'Invalid metaparameter name {paramname}: {errordetails}')
1038+
return val
1039+
10011040
def featurize_directory(
10021041
self,
10031042
intensity_dir: str,
@@ -2163,4 +2202,4 @@ def featurize(self, parent_child_map: pd.DataFrame, child_features: pd.DataFrame
21632202
agg_features[col] = self.aggregate
21642203

21652204
return joined_df.groupby(by='label').agg(agg_features)
2166-
2205+

0 commit comments

Comments
 (0)