Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PyRadiomics Models #338

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
48 changes: 39 additions & 9 deletions bin/testParams.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,55 @@
# custom parameters specified will be printed. If validation fails, an error message specifying cause of validation
# error will be printed.

import sys
import argparse

import pykwalify.core

from radiomics import getParameterValidationFiles

def main(paramsFile):
schemaFile, schemaFuncs = getParameterValidationFiles()

c = pykwalify.core.Core(source_file=paramsFile, schema_files=[schemaFile], extensions=[schemaFuncs])
def main(paramsFile, is_model=False):
if is_model:
validate_model_file(paramsFile)
else:
validate_customization(paramsFile)


def validate_model_file(model_file):
schema_data, schemaFuncs = getParameterValidationFiles(is_model_validation=True)
c = pykwalify.core.Core(source_file=model_file, schema_data=schema_data, extensions=[schemaFuncs])

try:
params = c.validate()
print('Model validation successfull!\n\n'
'###Model Type###\n%s\n'
% (params['model']['name']))
except pykwalify.core.SchemaError as e:
print('Parameter validation failed!\n%s' % e.msg)


def validate_customization(parameter_file):
schema_data, schemaFuncs = getParameterValidationFiles()
c = pykwalify.core.Core(source_file=parameter_file, schema_data=schema_data, extensions=[schemaFuncs])

try:
params = c.validate()
print('Parameter validation successfull!\n\n'
'###Enabled Features###\n%s\n'
'###Enabled Image Types###\n%s\n'
'###Settings###\n%s' % (params['featureClass'], params['imageType'], params['setting']))
except Exception as e:
print('Parameter validation failed!\n%s' % e.message)
'###Settings###\n%s' % (params['featureClass'], params['imageType'], params['setting'])
)
except pykwalify.core.SchemaError as e:
print('Parameter validation failed!\n%s' % e.msg)


if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('parameter_file', help='File representing the yaml or json structured configuration file to be '
'tested')
parser.add_argument('--model', '-m', action='store_true',
help='If this argument is specified, the configuration file is treated as a PyRadiomics Model, '
'otherwise, it is treated as an extraction parameter file')

if __name__ == '__main__' and len(sys.argv) > 1:
main(sys.argv[1])
args = parser.parse_args()
main(args.parameter_file, args.model)
41 changes: 41 additions & 0 deletions examples/exampleModels/exampleModel.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# This is an example of how a PyRadiomics Model looks like.
# It consists of 2 main parts: "extraction" and "model"
#
# "extraction": this part defines the settings PyRadiomics needs to extract the required features for the input.
# A model can incorporate features extracted from different images (e.g. multiple time points, or different MR
# sequences). For each image, a customized extraction may be defined by providing the image name as a key, and the
# customization as a value. This customization value must adhere to the same rules as a parameter file.
# In addition, extraction parameters that are common to all input images can be defined under "general". Be aware,
# "general" is therefore not allowed as an image name.
# If an image name is provided in the model, but not included in the extraction settings, no additional customization
# is applied for that image (just the general settings, if present)
#
# "model": this part provides all needed information to build the model. In this case, a simple linear regression model
# is shown. Both the "name" key (identifying the model type) and "parameters" key (providing model specific parameters)
# are required. What kind of parameters are possible/required depends on the model. In this case, only the intercept of
# the model and the betas (slopes) of the included features are required.

extraction:
general:
setting:
binWidth: 25
imageType:
Original: {}

image1:
featureClass:
glcm:
- Dissimilarity

image2:
featureClass:
firstorder:
- Mean

model:
name: linearregression
parameters:
intercept: 0.334
betas:
image1_original_glcm_Dissimilarity: 0.1
image2_original_firstorder_Mean: 0.3
28 changes: 24 additions & 4 deletions radiomics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import tempfile

import numpy # noqa: F401
from pykwalify.compat import yaml
from six.moves import urllib

from . import imageoperations
Expand Down Expand Up @@ -184,17 +185,36 @@ def getTestCase(testCase, dataDirectory=None):
return None, None


def getParameterValidationFiles():
def getParameterValidationFiles(is_model_validation=False):
"""
Returns file locations for the parameter schema and custom validation functions, which are needed when validating
a parameter file using ``PyKwalify.core``.
This functions returns a tuple with the file location of the schema as first and python script with custom validation
functions as second element.
This functions returns a tuple with a dictionary representing the schema and the file location of a python script
containing the custom validation functions.
"""
dataDir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'schemas'))
schemaFile = os.path.join(dataDir, 'paramSchema.yaml')
modelFile = os.path.join(dataDir, 'modelSchema.yaml')
schemaFuncs = os.path.join(dataDir, 'schemaFuncs.py')
return schemaFile, schemaFuncs

if not (os.path.isfile(schemaFile) and os.path.isfile(schemaFuncs)):
raise IOError('Customization Validation Files not Found!')

with open(schemaFile) as schema:
schema_data = yaml.load(schema)

if is_model_validation:
if not os.path.isfile(modelFile):
raise IOError('Model Validation File not Found!')

# Add the additional validation requirements of the model schema
with open(modelFile) as model_schema:
schema_data.update(yaml.load(model_schema))
else:
# Add the include to ensure that the customization_schema is applied
schema_data.update({'include': 'customization_schema'})

return schema_data, schemaFuncs


class _DummyProgressReporter(object):
Expand Down
4 changes: 2 additions & 2 deletions radiomics/featureextractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,9 @@ def _applyParams(self, paramsFile=None, paramsDict=None):
# No handler available for either pykwalify or root logger, provide first radiomics handler (outputs to stderr)
pykwalify.core.log.addHandler(logging.getLogger('radiomics').handlers[0])

schemaFile, schemaFuncs = getParameterValidationFiles()
schema_data, schemaFuncs = getParameterValidationFiles()
c = pykwalify.core.Core(source_file=paramsFile, source_data=paramsDict,
schema_files=[schemaFile], extensions=[schemaFuncs])
schema_data=schema_data, extensions=[schemaFuncs])
params = c.validate()
self.logger.debug('Parameters parsed, input is valid.')

Expand Down
22 changes: 22 additions & 0 deletions radiomics/schemas/modelSchema.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# include: customization_schema
name: model_schema
type: map
mapping:
extraction:
type: map
mapping:
regex;(.+):
include: customization_schema
model:
type: map
required: true
mapping:
name:
type: str
required: true
parameters:
type: map
required: true
mapping:
regex;(.+):
type: any
Loading