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.18k stars 497 forks source link

[BUG] featureextractor.RadiomicsFeatureExtractor.execute reports error when using a mask of all ones #765

Open qiancao opened 2 years ago

qiancao commented 2 years ago

Describe the bug I didn't find clarification on this problem in the forums, and I don't think this is expected behavior, so I'm filing it under "bug". As the subject line states, RadiomicsFeatureExtractor.execute does not work when I have a mask of all ones (compute features using all voxels in the image).

The error thrown is: ValueError: No labels found in this mask (i.e. nothing is segmented)!

A hacky way to deal with this is to set a corner voxel to background, then it runs without issues.

To Reproduce Steps to reproduce the behavior (Scenario 1 fails and Scenario 2 works, albeit leaving out a single voxel from the calculation):

import numpy as np
import SimpleITK as sitk
from radiomics import featureextractor

# Extractor settings
settings = {}
settings['binWidth'] = 25
settings['resampledPixelSpacing'] = None  # [3,3,3] is an example for defining resampling (voxels with size 3x3x3mm)
settings['interpolator'] = sitk.sitkBSpline
settings['imageType'] = ['original']

# Create extractor
extractor = featureextractor.RadiomicsFeatureExtractor(**settings)

#%% Scenario 1: Using a mask of all ones (this fails)

# Test Image and Mask
volume = np.random.rand(3,3,3)*256
volumeSITK = sitk.GetImageFromArray(volume)

mask = np.ones(volume.shape).astype(int)
# mask[0,0,0] = 0
maskSITK = sitk.GetImageFromArray(mask)
featureVector = extractor.execute(volumeSITK, maskSITK, label=1)

#%% Scenario 2: Setting corner voxel of mask intentionally to zero (this works, but unecessarily hacky)

mask = np.ones(volume.shape).astype(int)
mask[0,0,0] = 0 # This is diffeer
maskSITK = sitk.GetImageFromArray(mask)
featureVector = extractor.execute(volumeSITK, maskSITK, label=1)

Expected behavior PyRadiomics should take a mask image of all ones and recognize that I would like the features to be computed on all voxels of the image.

Version (please complete the following information):

qiancao commented 2 years ago

This is because in imageoperations.py (lines 47-49):

  labels = numpy.unique(sitk.GetArrayFromImage(mask))
  if len(labels) == 1:
    raise ValueError('No labels found in this mask (i.e. nothing is segmented)!')

The use of numpy.unique checks for number of unique values in the mask image. I think this is inaccurate. The error message: "No labels found in this mask (i.e. nothing is segmented)!" should be interpreted as: when the mask image is all zeros (everything is background and nothing is segmented).

I suggest changing line 48 to: if np.all((sitk.GetArrayFromImage(mask) == 0)):

mgcyung commented 2 years ago

I met the same problem