-
Notifications
You must be signed in to change notification settings - Fork 86
Open
Description
Environment
- Python version: 3.10
- PyKwalify version: 1.8.0
Steps to Reproduce
- Validate any schema with additional extensions. Easiest to reproduce using
PyRadiomics
repo (which uses pykwalify). From the pyradiomics repo runpython bin/testParams.py examples/exampleSettings/exampleCT.yaml
. This won't fail, because the function is deprecated, but not removed. - Alternative, run
pyradiomcis
tests of example settings: from repotests
folder, runpytest test_exampleSettings.py
. This fails because of the deprecation warning.
Schema
See Steps to reproduce. Schema: PyRadiomics Schema, with extensions
Schema:
# Parameters schema
name: Parameter schema
desc: This schema defines what arguments may be present in the parameters file that can be passed to the pyradiomics package.
type: map
mapping:
setting: &settings
type: map
mapping:
minimumROIDimensions:
type: int
range:
min: 1
max: 3
minimumROISize:
type: int
range:
min-ex: 0
geometryTolerance:
type: float
range:
min-ex: 0
correctMask:
type: bool
additionalInfo:
type: bool
label:
type: int
range:
min-ex: 0
label_channel:
type: int
range:
min: 0
binWidth:
type: float
range:
min-ex: 0
binCount:
type: int
range:
min-ex: 0
normalize:
type: bool
normalizeScale:
type: float
range:
min-ex: 0
removeOutliers:
type: float
range:
min-ex: 0
resampledPixelSpacing:
seq:
- type: float
range:
min: 0
interpolator:
type: any
func: checkInterpolator
padDistance:
type: int
range:
min: 0
distances:
seq:
- type: int
range:
min-ex: 0
force2D:
type: bool
force2Ddimension:
type: int
range:
min: 0
max: 2
resegmentRange:
seq:
- type: float
resegmentMode:
type: str
enum: ['absolute', 'relative', 'sigma']
resegmentShape:
type: bool
preCrop:
type: bool
sigma:
seq:
- type: float
range:
min-ex: 0
start_level:
type: int
range:
min: 0
level:
type: int
range:
min-ex: 0
wavelet:
type: str
func: checkWavelet
gradientUseSpacing:
type: bool
lbp2DRadius:
type: float
range:
min-ex: 0
lbp2DSamples:
type: int
range:
min: 1
lbp2DMethod:
type: str
enum: ['default', 'ror', 'uniform', 'var']
lbp3DLevels:
type: int
range:
min: 1
lbp3DIcosphereRadius:
type: float
range:
min-ex: 0
lbp3DIcosphereSubdivision:
type: int
range:
min: 0
voxelArrayShift:
type: int
symmetricalGLCM:
type: bool
weightingNorm:
type: any
func: checkWeighting
gldm_a:
type: int
range:
min: 0
voxelSetting:
type: map
mapping:
kernelRadius:
type: int
range:
min-ex: 0
maskedKernel:
type: bool
initValue:
type: float
voxelBatch:
type: int
range:
min-ex: 0
featureClass:
type: map
func: checkFeatureClass
matching-rule: 'any'
mapping:
regex;(.+):
type: any
imageType:
type: map
func: checkImageType
matching-rule: 'any'
mapping:
regex;(.+): *settings
Schema extensions:
import pywt
import six
from radiomics import getFeatureClasses, getImageTypes
featureClasses = getFeatureClasses()
imageTypes = getImageTypes()
def checkWavelet(value, rule_obj, path):
if not isinstance(value, six.string_types):
raise TypeError('Wavelet not expected type (str)')
wavelist = pywt.wavelist()
if value not in wavelist:
raise ValueError('Wavelet "%s" not available in pyWavelets %s' % (value, wavelist))
return True
def checkInterpolator(value, rule_obj, path):
if value is None:
return True
if isinstance(value, six.string_types):
enum = {'sitkNearestNeighbor',
'sitkLinear',
'sitkBSpline',
'sitkGaussian',
'sitkLabelGaussian',
'sitkHammingWindowedSinc',
'sitkCosineWindowedSinc',
'sitkWelchWindowedSinc',
'sitkLanczosWindowedSinc',
'sitkBlackmanWindowedSinc'}
if value not in enum:
raise ValueError('Interpolator value "%s" not valid, possible values: %s' % (value, enum))
elif isinstance(value, int):
if value < 1 or value > 10:
raise ValueError('Intepolator value %i, must be in range of [1-10]' % (value))
else:
raise TypeError('Interpolator not expected type (str or int)')
return True
def checkWeighting(value, rule_obj, path):
if value is None:
return True
elif isinstance(value, six.string_types):
enum = ['euclidean', 'manhattan', 'infinity', 'no_weighting']
if value not in enum:
raise ValueError('WeightingNorm value "%s" not valid, possible values: %s' % (value, enum))
else:
raise TypeError('WeightingNorm not expected type (str or None)')
return True
def checkFeatureClass(value, rule_obj, path):
global featureClasses
if value is None:
raise TypeError('featureClass dictionary cannot be None value')
for className, features in six.iteritems(value):
if className not in featureClasses.keys():
raise ValueError(
'Feature Class %s is not recognized. Available feature classes are %s' % (className, list(featureClasses.keys())))
if features is not None:
if not isinstance(features, list):
raise TypeError('Value of feature class %s not expected type (list)' % (className))
unrecognizedFeatures = set(features) - set(featureClasses[className].getFeatureNames())
if len(unrecognizedFeatures) > 0:
raise ValueError('Feature Class %s contains unrecognized features: %s' % (className, str(unrecognizedFeatures)))
return True
def checkImageType(value, rule_obj, path):
global imageTypes
if value is None:
raise TypeError('imageType dictionary cannot be None value')
for im_type in value:
if im_type not in imageTypes:
raise ValueError('Image Type %s is not recognized. Available image types are %s' %
(im_type, imageTypes))
return True
Data
PyRadiomics Example Schema
imageType:
Original: {}
LoG:
sigma: [1.0, 2.0, 3.0, 4.0, 5.0] # If you include sigma values >5, remember to also increase the padDistance.
Wavelet: {}
featureClass:
# redundant Compactness 1, Compactness 2 an Spherical Disproportion features are disabled by default, they can be
# enabled by specifying individual feature names (as is done for glcm) and including them in the list.
shape:
firstorder:
glcm: # Disable SumAverage by specifying all other GLCM features available
- 'Autocorrelation'
- 'JointAverage'
- 'ClusterProminence'
- 'ClusterShade'
- 'ClusterTendency'
- 'Contrast'
- 'Correlation'
- 'DifferenceAverage'
- 'DifferenceEntropy'
- 'DifferenceVariance'
- 'JointEnergy'
- 'JointEntropy'
- 'Imc1'
- 'Imc2'
- 'Idm'
- 'Idmn'
- 'Id'
- 'Idn'
- 'InverseVariance'
- 'MaximumProbability'
- 'SumEntropy'
- 'SumSquares'
glrlm:
glszm:
gldm:
setting:
# Normalization:
# most likely not needed, CT gray values reflect absolute world values (HU) and should be comparable between scanners.
# If analyzing using different scanners / vendors, check if the extracted features are correlated to the scanner used.
# If so, consider enabling normalization by uncommenting settings below:
#normalize: true
#normalizeScale: 500 # This allows you to use more or less the same bin width.
# Resampling:
# Usual spacing for CT is often close to 1 or 2 mm, if very large slice thickness is used,
# increase the resampled spacing.
# On a side note: increasing the resampled spacing forces PyRadiomics to look at more coarse textures, which may or
# may not increase accuracy and stability of your extracted features.
interpolator: 'sitkBSpline'
resampledPixelSpacing: [1, 1, 1]
padDistance: 10 # Extra padding for large sigma valued LoG filtered images
# Mask validation:
# correctMask and geometryTolerance are not needed, as both image and mask are resampled, if you expect very small
# masks, consider to enable a size constraint by uncommenting settings below:
#minimumROIDimensions: 2
#minimumROISize: 50
# Image discretization:
# The ideal number of bins is somewhere in the order of 16-128 bins. A possible way to define a good binwidt is to
# extract firstorder:Range from the dataset to analyze, and choose a binwidth so, that range/binwidth remains approximately
# in this range of bins.
binWidth: 25
# first order specific settings:
voxelArrayShift: 1000 # Minimum value in HU is -1000, shift +1000 to prevent negative values from being squared.
# Misc:
# default label value. Labels can also be defined in the call to featureextractor.execute, as a commandline argument,
# or in a column "Label" in the input csv (batchprocessing)
label: 1
Expected Behavior
To run pykwalify without deprecation warnings. This is needed to allow PyTest to complete without errors.
Observed Behavior
Got a deprecation warning from the function in pykwalify core: core._load_extensions()
, uses SourceFileLoader.load_module()
, it suggests to use exec_module()
instead.
Metadata
Metadata
Assignees
Labels
No labels