import numpy from scipy import ndimage from radiomics.firstorder import RadiomicsFirstOrder from .mybase import MyRadiomicsFeaturesBase class MyRadiomicsFirstOrder(MyRadiomicsFeaturesBase): def __init__(self, inputImage, inputMask, **kwargs): super().__init__(inputImage, inputMask, **kwargs) self.pixelSpacing = inputImage.GetSpacing() self.voxelArrayShift = kwargs.get('voxelArrayShift', 0) def _initCalculation(self): return super()._initSegmentBasedCalculation() self.targetVoxelArray = self.imageArray[self.labelledVoxelCoordinates].astype( 'float' ) self.discretizedTargetVoxelArray = None # Lazy instantiation self.logger.debug('First order feature class initialized') def _moment(self, a, moment=1, axis=0): return RadiomicsFirstOrder._moment(self, a, moment, axis) def _getDiscretizedTargetVoxelArray(self): return RadiomicsFirstOrder._getDiscretizedTargetVoxelArray(self) def getSuvMaxFeatureValue(self): r""" **20. SUV max** Implemented as one of the intensity features extracted by Vallieres et al. Maximum SUV of the tumour region. .. note:: Extracted from PET scans and not used in the CT feature set. """ ROIPet = self.imageArray mask = self.maskArray ROIPet[~mask] = numpy.nan return numpy.max(ROIPet[~numpy.isnan(ROIPet)]) def getSuvMeanFeatureValue(self): r""" **21. SUV mean** Implemented as one of the intensity features extracted by valieres et al. Average SUV of the tumour region. .. note:: Extracted from PET scans and not used in the CT feature set. """ ROIPet = self.imageArray mask = self.maskArray ROIPet[~mask] = numpy.nan return numpy.mean(ROIPet[~numpy.isnan(ROIPet)]) def getTLGFeatureValue(self): r""" **22. TLG** Total lesion glycolysis. Defined as .. math:: \textit{SUVMean}\times \textit{total volume of the tumour region} .. note:: Extracted from PET scans and not used in the CT feature set. """ z, y, x = self.pixelSpacing Np = len(self.labelledVoxelCoordinates[0]) volume = Np * (z * x * y) ROIPet = self.imageArray mask = self.maskArray ROIPet[~mask] = numpy.nan return numpy.mean(ROIPet[~numpy.isnan(ROIPet)]) * volume def getAUCCSHFeatureValue(self): r""" **23. Inactive volume ** Area under the curve of the cumulative SUV-volume histogram describing the percentage of total tumour volume above a percentage threshold of maximum SUV, as defined by van Velden et al. .. note:: Extracted from PET scans and not used in the CT feature set. """ nBins = 1000 # By default ROIPet = self.imageArray mask = self.maskArray ROIPet[~mask] = numpy.nan outliers = numpy.where( ROIPet > ( numpy.mean(ROIPet[~numpy.isnan(ROIPet)]) + 3 * numpy.std(ROIPet[~numpy.isnan(ROIPet)]) ) )[0] good_voxels = numpy.where( ROIPet <= ( numpy.mean(ROIPet[~numpy.isnan(ROIPet)]) + 3 * numpy.std(ROIPet[~numpy.isnan(ROIPet)]) ) )[0] ROIPet[outliers] = numpy.mean(ROIPet[good_voxels]) ROIPet = ROIPet - numpy.min(ROIPet[~numpy.isnan(ROIPet)]) ROIPet = ROIPet / numpy.max(ROIPet[~numpy.isnan(ROIPet)]) volume = ROIPet[~numpy.isnan(ROIPet)] nVoxel = len(volume) bins, _ = numpy.histogram(volume, nBins) csh = numpy.flipud(numpy.cumsum(numpy.flipud(bins)) / nVoxel) return numpy.sum(csh / nBins) def getgETUFeatureValue(self): r""" **24. gETU** Generalized effective total uptake, with parameter a = 0.25 as defined by Rahim et al. - input: 3D array representing the PET volume in SUV format, with voxels outside the ROI set to NaNs. .. note:: Extracted from PET scans and not used in the CT feature set. """ ROIPet = self.imageArray mask = self.maskArray ROIPet[~mask] = numpy.nan a = 0.25 n_voxels = numpy.sum(ROIPet[~numpy.isnan(ROIPet)]) ROIPet = ROIPet ** a return numpy.sum(ROIPet[~numpy.isnan(ROIPet)] / n_voxels) ** (1 / a)