AIM-Harvard / pyradiomics

Open-source python package for the extraction of Radiomics features from 2D and 3D images and binary masks. Support: https://discourse.slicer.org/c/community/radiomics
http://pyradiomics.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
1.16k stars 500 forks source link

Voxel-wise feature calculation? #456

Closed Kuttner closed 5 years ago

Kuttner commented 5 years ago

Hi, I am trying to calculate a parametric image by extracting voxel-wise features from a square kernel. After reading the reply in this thread: https://groups.google.com/forum/#!topic/pyradiomics/k6WXuwYTqNA I tried to run the helloVoxel.py from: https://github.com/Radiomics/pyradiomics/blob/master/examples/helloVoxel.py using the following parameter file (exampleVoxel.yaml):


imageType: Original: {}

featureClass: firstorder:

setting: binWidth: 25 force2D: true label: 1

voxelSetting: kernelRadius: 2 maskedKernel: true initValue: nan


I.e. I would like to calculate the mean inside a 5x5 kernel for every pixel inside the mask.

However, the output is just constant value of 7.60209 inside the mask, throughout the entire volume (and of course nan outside the mask). And after trying out with simple synthetic images, it seems that it returns the mean inside the entire mask for every voxel in the mask, and not the mean inside the 5x5 kernel for every voxel inside the mask.

Could you please look into if this is a bug, or if I made a mistake here?

Thank you for your kind help!

Regards Samuel Kuttner

JoostJM commented 5 years ago

@Kuttner That is indeed a bug, good catch. I fixed it in 81e713a (the current master). If you can build the latest PyRadiomics master from source, the issue should be fixed. If you want to use the pre-built wheels, the fix will be included in the next release.

Kuttner commented 5 years ago

@JoostJM Thank you for looking into this. I think I managed to upgrade. Running: "pyradiomics --version" gives the output: "pyradiomics 2.1.2.post21+gb92bd22". Is this correct?

However, still, running the helloVoxel.py, with the above *.yaml file just gives constant value of 7.60209 inside the mask. Could you provide me with a minimal working example that shows it is working for you?

Thanks a lot!

JoostJM commented 5 years ago

Thats the latest master, the same I ran my test with. As a small bonus, this also contains a CLI interface for voxelwise extraction.

From the examples folder, can you run the following?

imageType: Original: {}

featureClass: firstorder:

setting: binWidth: 25 force2D: true label: 1

voxelSetting: kernelRadius: 2 maskedKernel: true initValue: nan


- run ` pyradiomics ..\data\brain1_image.nrrd ..\data\brain1_label.nrrd -p exampleSettings\exampleVoxel.yaml --mode voxel -v 4 -od results` 

This should generate the mean map in a subfolder called results. How are you viewing the result? I set `initValue`  to `nan`, as 3D Slicer will treat those voxels as 'transparant', but it does not load right in ITK Snap. So you can also try to just set `initValue: 0` , so you can at least see if the calculation goes right.
Kuttner commented 5 years ago

I´m running from within python using the following code and your exampleVoxel.yaml file:

from radiomics import featureextractor, getTestCase
import six
import sys, os
import matplotlib.pyplot as plt
import numpy as np
import SimpleITK as sitk

imageName, maskName = getTestCase('brain1', dataDir)
params = os.path.join(dataDir, "exampleVoxel.yaml")

extractor = featureextractor.RadiomicsFeaturesExtractor(params)

result = extractor.execute(imageName, maskName, voxelBased=True)

for key, val in six.iteritems(result):
    parametermap = sitk.GetArrayFromImage(val)
    parametermap[np.isnan(parametermap)] = 0

plt.imshow(parametermap[6, :, :])

And the plot output is just homogenous for "Mean" feature. However, running for example glcm:

Is there a reason for "mean" output being homogenous inside the mask using a 5x5 kernel, and not the "joint entropy"?``

JoostJM commented 5 years ago

Can you try using the following code:

import radiomics
from radiomics import featureextractor, getTestCase
import six
import sys, os
import matplotlib.pyplot as plt
import numpy as np
import SimpleITK as sitk

print(radiomics.__version__)
repo_root = r'path\to\your\pyradiomics\repo'  # Update this variable

dataDir = os.path.join(repo_root , 'data')
examples_settings_dir = os.path.join(repo_root, 'examples', 'exampleSettings')

imageName, maskName = getTestCase('brain1', dataDir)
params = os.path.join(examples_settings_dir, "exampleVoxel.yaml")

extractor = featureextractor.RadiomicsFeaturesExtractor(params)

extractor.disableAllFeatures()  # disable all features
extractor.enableFeaturesByName(firstorder=['Mean'])  # Only enable firstorder mean

extractor.settings['initValue'] = 0  # Set the non-calculated voxels to 0

result = extractor.execute(imageName, maskName, voxelBased=True)

for key, val in six.iteritems(result):
    parametermap = sitk.GetArrayFromImage(val)

plt.imshow(parametermap[6, :, :])
Kuttner commented 5 years ago

Thanks for the code. Unfortunately, I get the precise same output as before: 825.235 as the same pixel value in the entire mask. Do you get the same output?

The version output from the code is 2.0.0, but I´m sure I upgraded with the latest version, see terminal output: 2.1.2.post21+gb92bd22. How come this is not the same?

image

JoostJM commented 5 years ago

@Kuttner, that is because apparantly, your spyder implementation resolves the import to another installation of PyRadiomics than your default python interpreter does (or, to be more precise, the default scripts folder where the pyradiomics CLI script resides).

This older version also does not yet contain the fix, thereby explaining your problem. You can find the location of this installation by running the following line from spyder: print(radiomics.__file__)

Then, the easiest way to fix your error is to remove that installation, or ensure the version installed there is the most recent one.

An interesting side-note: if you install using python setup.py develop (instead of install), Python will build the extensions, deposit them in your source repository, and the link to your source repo in the easy-install.pth. This allows you to use pyradiomics in "development-mode", i.e. using the code in the repo, instead of an installed copy (better deals with updates, prevents mismatched versions when developing code, etc.)

Kuttner commented 5 years ago

@JoostJM, Thanks, we´re getting there, correct version is installed now, however, the last part of your code generates an error:

for key, val in six.iteritems(result):
    parametermap = sitk.GetArrayFromImage(val)

image Seems like SimpleITK is now generating the error. I tried to re-install SimpleITK without success. Any suggestions? Thanks!

JoostJM commented 5 years ago

Hi @Kuttner, that is not really an error. The output from PyRadiomics consists of diagnostic features (strings , ints, tuples, dicts) and features (floats in case of segment-based, SimpleITK images in case of voxel based). the easiest way to distinguish between the two is to use:

for key, val in six.iteritems(result):
  if isinstance(val, sitk.Image):
    # Do something to the featuer, e.g.
    parametermap = sitk.GetArrayFromImage(val)
  else:
    # Diagnostic feature, just print the value for now
    print('%s: %s' % (key, str(val)))
Kuttner commented 5 years ago

@JoostJM, yes of course. Now it all works! Thanks a lot for your kind help with solving this issue ;)