def _calculateCMatrix(self): angles = imageoperations.generateAngles(self.boundingBoxSize, **self.kwargs) Ng = self.coefficients['Ng'] P_gldm = cMatrices.calculate_gldm(self.matrix, self.maskArray, angles, Ng, self.gldm_a) jvector = numpy.arange(1, P_gldm.shape[1] + 1, dtype='float64') # Delete rows and columns that specify gray levels not present in the ROI sumP_gldm = numpy.sum(P_gldm) pd = numpy.sum(P_gldm, 0) pg = numpy.sum(P_gldm, 1) P_gldm = numpy.delete(P_gldm, numpy.where(pg == 0), 0) P_gldm = numpy.delete(P_gldm, numpy.where(pd == 0), 1) jvector = numpy.delete(jvector, numpy.where(pd == 0)) pg = numpy.delete(pg, numpy.where(pg == 0)) pd = numpy.delete(pd, numpy.where(pd == 0)) self.coefficients['sumP_gldm'] = sumP_gldm self.coefficients['pd'] = pd self.coefficients['pg'] = pg self.coefficients['ivector'] = self.coefficients['grayLevels'] self.coefficients['jvector'] = jvector return P_gldm
def _calculateCMatrix(self): self.logger.debug('Calculating GLSZM matrix in C') # Do not pass kwargs directly, as distances may be specified, which must be forced to [1] for this class angles = imageoperations.generateAngles( self.boundingBoxSize, force2D=self.kwargs.get('force2D', False), force2Ddimension=self.kwargs.get('force2Ddimension', 0)) Ng = self.coefficients['Ng'] Ns = self.coefficients['Np'] P_glszm = cMatrices.calculate_glszm(self.matrix, self.maskArray, angles, Ng, Ns) # Delete rows that specify gray levels not present in the ROI NgVector = range(1, Ng + 1) # All possible gray values GrayLevels = self.coefficients[ 'grayLevels'] # Gray values present in ROI emptyGrayLevels = numpy.array( list(set(NgVector) - set(GrayLevels))) # Gray values NOT present in ROI P_glszm = numpy.delete(P_glszm, emptyGrayLevels - 1, 0) return P_glszm
def _calculateCMatrix(self): self.logger.debug('Calculating GLCM matrix in C') size = numpy.max(self.matrixCoordinates, 1) - numpy.min( self.matrixCoordinates, 1) + 1 angles = imageoperations.generateAngles(size, **self.kwargs) Ng = self.coefficients['Ng'] P_glcm = cMatrices.calculate_glcm(self.matrix, self.maskArray, angles, Ng) P_glcm = self._applyMatrixOptions(P_glcm, angles) # Delete rows and columns that specify gray levels not present in the ROI Ng = self.coefficients['Ng'] NgVector = range(1, Ng + 1) # All possible gray values GrayLevels = self.coefficients[ 'grayLevels'] # Gray values present in ROI emptyGrayLevels = numpy.array( list(set(NgVector) - set(GrayLevels))) # Gray values NOT present in ROI P_glcm = numpy.delete(P_glcm, emptyGrayLevels - 1, 0) P_glcm = numpy.delete(P_glcm, emptyGrayLevels - 1, 1) return P_glcm
def _calculateMatrix(self): self.matrix = self.matrix.astype('float') # Set voxels outside delineation to padding value padVal = numpy.nan self.matrix[~self.maskArray] = padVal angles = imageoperations.generateAngles(self.boundingBoxSize, **self.kwargs) angles = numpy.concatenate((angles, angles * -1)) depMat = numpy.zeros(self.matrix.shape, dtype='int') with self.progressReporter(angles, desc='Calculate shifted matrices (GLDM)') as bar: for a in bar: # create shifted array (by angle), so that for an index idx, angMat[idx] is the neigbour of self.matrix[idx] # for the current angle. angMat = numpy.roll(numpy.roll(numpy.roll(self.matrix, -a[0], 0), -a[1], 1), -a[2], 2) - self.matrix if a[0] > 0: angMat[-a[0]:, :, :] = padVal elif a[0] < 0: angMat[:-a[0], :, :] = padVal if a[1] > 0: angMat[:, -a[1]:, :] = padVal elif a[1] < 0: angMat[:, :-a[1], :] = padVal if a[2] > 0: angMat[:, :, -a[2]:] = padVal elif a[2] < 0: angMat[:, :, :-a[2]] = padVal nanMask = numpy.isnan(angMat) depMat[~nanMask] += (numpy.abs(angMat[~nanMask]) <= self.gldm_a) grayLevels = self.coefficients['grayLevels'] dependenceSizes = numpy.unique(depMat[self.maskArray]) P_gldm = numpy.zeros((len(grayLevels), len(dependenceSizes))) with self.progressReporter(grayLevels, desc='calculate GLDM') as bar: for i_idx, i in enumerate(bar): i_mat = (self.matrix == i) for d_idx, d in enumerate(dependenceSizes): # By multiplying i_mat and depMat == d, a boolean area is obtained, # where the number of elements that are true (1) is equal to the number of voxels # with gray level i and dependence d. P_gldm[i_idx, d_idx] = numpy.sum(i_mat * (depMat == d)) pd = numpy.sum(P_gldm, 0) pg = numpy.sum(P_gldm, 1) self.coefficients['ivector'] = grayLevels self.coefficients['jvector'] = dependenceSizes self.coefficients['pd'] = pd self.coefficients['pg'] = pg return P_gldm
def _calculateCMatrix(self): size = numpy.max(self.matrixCoordinates, 1) - numpy.min( self.matrixCoordinates, 1) + 1 angles = imageoperations.generateAngles(size) Ng = self.coefficients['Ng'] P_glcm = cMatrices.calculate_glcm(self.matrix, self.maskArray, angles, Ng) P_glcm = self._applyMatrixOptions(P_glcm, angles) return P_glcm
def _calculateCMatrix(self): self.logger.debug('Calculating GLCM matrix in C') size = numpy.max(self.matrixCoordinates, 1) - numpy.min(self.matrixCoordinates, 1) + 1 angles = imageoperations.generateAngles(size, **self.kwargs) Ng = self.coefficients['Ng'] P_glcm = cMatrices.calculate_glcm(self.matrix, self.maskArray, angles, Ng) P_glcm = self._applyMatrixOptions(P_glcm, angles) return P_glcm
def _calculateCMatrix(self): self.logger.debug('Calculating GLSZM matrix in C') size = numpy.max(self.matrixCoordinates, 1) - numpy.min( self.matrixCoordinates, 1) + 1 angles = imageoperations.generateAngles(size, **self.kwargs) Ng = self.coefficients['Ng'] Ns = self.coefficients['Np'] return cMatrices.calculate_glszm(self.matrix, self.maskArray, angles, Ng, Ns)
def _calculateCMatrix(self): angles = imageoperations.generateAngles(self.boundingBoxSize, **self.kwargs) Ng = self.coefficients['Ng'] P_ngtdm = cMatrices.calculate_ngtdm(self.matrix, self.maskArray, angles, Ng) # Delete empty grey levels P_ngtdm = numpy.delete(P_ngtdm, numpy.where(P_ngtdm[:, 0] == 0), 0) # Normalize P_ngtdm[:, 0] (= p_i) P_ngtdm[:, 0] = P_ngtdm[:, 0] / self.coefficients['Np'] return P_ngtdm
def _calculateMatrix(self): r""" Compute GLCMs for the input image for every direction in 3D. Calculated GLCMs are placed in array P_glcm with shape (i/j, a) i/j = total gray-level bins for image array, a = directions in 3D (generated by imageoperations.generateAngles) """ self.logger.debug('Calculating GLCM matrix in Python') Ng = self.coefficients['Ng'] # Exclude voxels outside segmentation, due to binning, no negative values will be encountered inside the mask self.matrix[self.maskArray == 0] = -1 size = numpy.max(self.matrixCoordinates, 1) - numpy.min( self.matrixCoordinates, 1) + 1 angles = imageoperations.generateAngles(size, **self.kwargs) P_glcm = numpy.zeros((Ng, Ng, int(angles.shape[0])), dtype='float64') if self.verbose: bar = trange(Ng, desc='calculate GLCM') # iterate over gray levels for center voxel for i in range(1, Ng + 1): # give some progress if self.verbose: bar.update() # get the indices to all voxels which have the current gray level i i_indices = numpy.where(self.matrix == i) # iterate over gray levels for neighbouring voxel for j in range(1, Ng + 1): # get the indices to all voxels which have the current gray level j j_indices = set(zip(*numpy.where(self.matrix == j))) for a_idx, a in enumerate(angles): # get the corresponding indices of the neighbours for angle a neighbour_indices = set(zip(*(i_indices + a[:, None]))) # The following intersection yields the indices to voxels with gray level j # that are also a neighbour of a voxel with gray level i for angle a. # The number of indices is then equal to the total number of pairs with gray level i and j for angle a count = len(neighbour_indices.intersection(j_indices)) P_glcm[i - 1, j - 1, a_idx] = count if self.verbose: bar.close() P_glcm = self._applyMatrixOptions(P_glcm, angles) return P_glcm
def _calculateCMatrix(self): self.logger.debug('Calculating GLSZM matrix in C') size = numpy.max(self.matrixCoordinates, 1) - numpy.min( self.matrixCoordinates, 1) + 1 # Do not pass kwargs directly, as distances may be specified, which must be forced to [1] for this class angles = imageoperations.generateAngles( size, force2Dextraction=self.kwargs.get('force2D', False), force2Ddimension=self.kwargs.get('force2Ddimension', 0)) Ng = self.coefficients['Ng'] Ns = self.coefficients['Np'] return cMatrices.calculate_glszm(self.matrix, self.maskArray, angles, Ng, Ns)
def _calculateMatrix(self): r""" Compute GLCMs for the input image for every direction in 3D. Calculated GLCMs are placed in array P_glcm with shape (i/j, a) i/j = total gray-level bins for image array, a = directions in 3D (generated by imageoperations.generateAngles) """ self.logger.debug('Calculating GLCM matrix in Python') # Exclude voxels outside segmentation, due to binning, no negative values will be encountered inside the mask self.matrix[self.maskArray == 0] = -1 size = numpy.max(self.matrixCoordinates, 1) - numpy.min( self.matrixCoordinates, 1) + 1 angles = imageoperations.generateAngles(size, **self.kwargs) grayLevels = self.coefficients['grayLevels'] P_glcm = numpy.zeros( (len(grayLevels), len(grayLevels), int(angles.shape[0])), dtype='float64') # If verbosity > INFO, or no progress reporter is set in radiomics.progressReporter, _dummyProgressReporter is used, # which just iterates over the iterator without reporting progress with self.progressReporter(grayLevels, desc='calculate GLCM') as bar: # iterate over gray levels for center voxel for i_idx, i in enumerate(bar): # get the indices to all voxels which have the current gray level i i_indices = numpy.where(self.matrix == i) # iterate over gray levels for neighbouring voxel for j_idx, j in enumerate(grayLevels): # get the indices to all voxels which have the current gray level j j_indices = set(zip(*numpy.where(self.matrix == j))) for a_idx, a in enumerate(angles): # get the corresponding indices of the neighbours for angle a neighbour_indices = set(zip(*(i_indices + a[:, None]))) # The following intersection yields the indices to voxels with gray level j # that are also a neighbour of a voxel with gray level i for angle a. # The number of indices is then equal to the total number of pairs with gray level i and j for angle a count = len(neighbour_indices.intersection(j_indices)) P_glcm[i_idx, j_idx, a_idx] = count P_glcm = self._applyMatrixOptions(P_glcm, angles) return P_glcm
def _calculateCDiameters(self): """ Calculate maximum diameters in 2D and 3D using C extension. Function returns a tuple with 4 elements: 0. Maximum 2D diameter Slice (XY Plane, Axial) 1. Maximum 2D diameter Column (ZX Plane, Coronal) 2. Maximum 2D diameter Row (ZY Plane, Sagittal) 3. Maximum 3D diameter """ self.logger.debug('Calculating Maximum 3D diameter in C') Ns = self.targetVoxelArray.size size = numpy.max(self.matrixCoordinates, 1) - numpy.min( self.matrixCoordinates, 1) + 1 angles = imageoperations.generateAngles(size) return cShape.calculate_diameter(self.maskArray, self.pixelSpacing, angles, Ns)
def _calculateCMatrix(self): self.logger.debug('Calculating GLRLM matrix in C') Ng = self.coefficients['Ng'] Nr = self.coefficients['Nr'] # Do not pass kwargs directly, as distances may be specified, which must be forced to [1] for this class angles = imageoperations.generateAngles( self.boundingBoxSize, force2D=self.kwargs.get('force2D', False), force2Ddimension=self.kwargs.get('force2Ddimension', 0)) P_glrlm = cMatrices.calculate_glrlm(self.matrix, self.maskArray, angles, Ng, Nr) P_glrlm = self._applyMatrixOptions(P_glrlm, angles) return P_glrlm
def _calculateMatrix(self): """ Number of times a region with a gray level and voxel count occurs in an image. P_glszm[level, voxel_count] = # occurrences For 3D-images this concerns a 26-connected region, for 2D an 8-connected region """ self.logger.debug('Calculating GLSZM matrix in Python') Np = self.coefficients['Np'] # Do not pass kwargs directly, as distances may be specified, which must be forced to [1] for this class angles = imageoperations.generateAngles( self.boundingBoxSize, force2D=self.kwargs.get('force2D', False), force2Ddimension=self.kwargs.get('force2Ddimension', 0)) grayLevels = self.coefficients['grayLevels'] # Empty GLSZ matrix P_glszm = numpy.zeros((len(grayLevels), Np)) maxRegion = 0 # If verbosity > INFO, or no progress reporter is set in radiomics.progressReporter, _dummyProgressReporter is used, # which just iterates over the iterator without reporting progress with self.progressReporter(grayLevels, desc='calculate GLSZM') as bar: # Iterate over all gray levels in the image for i_idx, i in enumerate(bar): ind = zip(*numpy.where(self.matrix == i)) ind = list( set(ind).intersection( set(zip(*self.labelledVoxelCoordinates)))) while ind: # check if ind is not empty: unprocessed regions for current gray level # Pop first coordinate of an unprocessed zone, start new stack ind_region = [ind.pop()] # Define regionSize regionSize = 0 # Grow zone for item popped from stack of region indices, loop until stack of region indices is exhausted # Each loop represents one voxel belonging to current zone. Therefore, count number of loops as regionSize while ind_region: regionSize += 1 # Use pop to remove next node for set of unprocessed region indices ind_node = ind_region.pop() # get all coordinates in the 26-connected region, 2 voxels per angle region_full = [ tuple(sum(a) for a in zip(ind_node, angle_i)) for angle_i in angles ] region_full += [ tuple(sum(a) for a in zip(ind_node, angle_i)) for angle_i in angles * -1 ] # get all unprocessed coordinates in the 26-connected region with same gray level region_level = list( set(ind).intersection(set(region_full))) # Remove already processed indices to prevent reprocessing ind = list(set(ind) - set(region_level)) # Add all found neighbours to the total stack of unprocessed neighbours ind_region.extend(region_level) # Update the gray level size zone matrix P_glszm[i_idx, regionSize - 1] += 1 if maxRegion < regionSize: maxRegion = regionSize return P_glszm[:, 0:maxRegion]
def _calculateMatrix(self): Ng = self.coefficients['Ng'] self.matrix = self.matrix.astype('float') # Set voxels outside delineation to padding value padVal = numpy.nan self.matrix[(self.maskArray == 0)] = padVal angles = imageoperations.generateAngles(self.boundingBoxSize, **self.kwargs) angles = numpy.concatenate((angles, angles * -1)) dataTemp = numpy.zeros(self.matrix.shape, dtype='float') countMat = numpy.zeros(self.matrix.shape, dtype='int') with self.progressReporter(angles, desc='Calculate shifted matrices (NGTDM)') as bar: for a in bar: # create shifted array (by angle), so that for an index idx, angMat[idx] is the neigbour of self.matrix[idx] # for the current angle. angMat = numpy.roll(numpy.roll(numpy.roll(self.matrix, -a[0], 0), -a[1], 1), -a[2], 2) if a[0] > 0: angMat[-a[0]:, :, :] = padVal elif a[0] < 0: angMat[:-a[0], :, :] = padVal if a[1] > 0: angMat[:, -a[1]:, :] = padVal elif a[1] < 0: angMat[:, :-a[1], :] = padVal if a[2] > 0: angMat[:, :, -a[2]:] = padVal elif a[2] < 0: angMat[:, :, :-a[2]] = padVal nanmask = numpy.isnan(angMat) dataTemp[~nanmask] += angMat[~nanmask] countMat[~nanmask] += 1 # Average neighbourhood is the dataTemp (which is the sum of gray levels of neighbours that are non-NaN) divided by # the countMat (which is the number of neighbours that are non-NaN) nZeroMask = countMat > 0 # Prevent division by 0 dataTemp[nZeroMask] = dataTemp[nZeroMask] / countMat[nZeroMask] # Only consider voxels that are part of the Mask AND have a neighbourhood validMask = nZeroMask * self.maskArray.astype('bool') dataTemp[validMask] = numpy.abs(dataTemp[validMask] - self.matrix[validMask]) # Calculate the absolute difference P_ngtdm = numpy.zeros((Ng, 3), dtype='float') # For each gray level present in self.matrix: # element 0 = probability of gray level (p_i), # element 1 = sum of the absolute differences (s_i), # element 2 = gray level (i) grayLevels = self.coefficients['grayLevels'] with self.progressReporter(grayLevels, desc='Calculate NGTDM') as bar: for i in bar: if not numpy.isnan(i): i_ind = numpy.where(self.matrix == i) P_ngtdm[int(i - 1), 0] = len(i_ind[0]) P_ngtdm[int(i - 1), 1] = numpy.sum(dataTemp[i_ind]) # Fill in gray levels (needed as empty gray level slices will be deleted) P_ngtdm[:, 2] = numpy.arange(1, Ng + 1) # Delete empty grey levels P_ngtdm = numpy.delete(P_ngtdm, numpy.where(P_ngtdm[:, 0] == 0), 0) # Normalize P_ngtdm[:, 0] (= p_i) P_ngtdm[:, 0] = P_ngtdm[:, 0] / self.coefficients['Np'] return P_ngtdm
def _calculateMatrix(self): """ Number of times a region with a gray level and voxel count occurs in an image. P_glszm[level, voxel_count] = # occurrences For 3D-images this concerns a 26-connected region, for 2D an 8-connected region """ self.logger.debug('Calculating GLSZM matrix in Python') Ng = self.coefficients['Ng'] Np = self.coefficients['Np'] size = numpy.max(self.matrixCoordinates, 1) - numpy.min( self.matrixCoordinates, 1) + 1 angles = imageoperations.generateAngles(size, **self.kwargs) # Empty GLSZ matrix P_glszm = numpy.zeros((Ng, Np)) # If verbosity > INFO, or no progress reporter is set in radiomics.progressReporter, _dummyProgressReporter is used, # which just iterates over the iterator without reporting progress with self.progressReporter(range(1, Ng + 1), desc='calculate GLSZM') as bar: # Iterate over all gray levels in the image for i in bar: ind = zip(*numpy.where(self.matrix == i)) ind = list( set(ind).intersection(set(zip(*self.matrixCoordinates)))) while ind: # check if ind is not empty: unprocessed regions for current gray level # Pop first coordinate of an unprocessed zone, start new stack ind_region = [ind.pop()] # Define regionSize regionSize = 0 # Grow zone for item popped from stack of region indices, loop until stack of region indices is exhausted # Each loop represents one voxel belonging to current zone. Therefore, count number of loops as regionSize while ind_region: regionSize += 1 # Use pop to remove next node for set of unprocessed region indices ind_node = ind_region.pop() # get all coordinates in the 26-connected region, 2 voxels per angle region_full = [ tuple(sum(a) for a in zip(ind_node, angle_i)) for angle_i in angles ] region_full += [ tuple(sum(a) for a in zip(ind_node, angle_i)) for angle_i in angles * -1 ] # get all unprocessed coordinates in the 26-connected region with same gray level region_level = list( set(ind).intersection(set(region_full))) # Remove already processed indices to prevent reprocessing ind = list(set(ind) - set(region_level)) # Add all found neighbours to the total stack of unprocessed neighbours ind_region.extend(region_level) # Update the gray level size zone matrix P_glszm[i - 1, regionSize - 1] += 1 # Crop gray-level axis of GLSZM matrix to between minimum and maximum observed gray-levels # Crop size-zone area axis of GLSZM matrix up to maximum observed size-zone area self.logger.debug( 'Cropping calculated matrix to observed gray levels and maximum observed zone size' ) P_glszm_bounds = numpy.argwhere(P_glszm) (xstart, ystart), (xstop, ystop) = P_glszm_bounds.min( 0), P_glszm_bounds.max(0) + 1 # noqa: F841 return P_glszm[xstart:xstop, :ystop]
def _calculateGLSZM(self): """ Number of times a region with a gray level and voxel count occurs in an image. P_glszm[level, voxel_count] = # occurrences For 3D-images this concerns a 26-connected region, for 2D an 8-connected region """ size = numpy.max(self.matrixCoordinates, 1) - numpy.min( self.matrixCoordinates, 1) + 1 angles = imageoperations.generateAngles(size) # Empty GLSZ matrix P_glszm = numpy.zeros( (self.coefficients['Ng'], self.coefficients['Np'])) # Iterate over all gray levels in the image numGrayLevels = self.coefficients['Ng'] + 1 if self.verbose: bar = trange(numGrayLevels - 1, desc='calculate GLSZM') for i in range(1, numGrayLevels): # give some progress if self.verbose: bar.update() ind = zip(*numpy.where(self.matrix == i)) ind = list( set(ind).intersection(set(zip(*self.matrixCoordinates)))) while ind: # check if ind is not empty: unprocessed regions for current gray level # Pop first coordinate of an unprocessed zone, start new stack ind_region = [ind.pop()] # Define regionSize regionSize = 0 # Grow zone for item popped from stack of region indices, loop until stack of region indices is exhausted # Each loop represents one voxel belonging to current zone. Therefore, count number of loops as regionSize while ind_region: regionSize += 1 # Use pop to remove next node for set of unprocessed region indices ind_node = ind_region.pop() # get all coordinates in the 26-connected region, 2 voxels per angle region_full = [ tuple(sum(a) for a in zip(ind_node, angle_i)) for angle_i in angles ] region_full += [ tuple(sum(a) for a in zip(ind_node, angle_i)) for angle_i in angles * -1 ] # get all unprocessed coordinates in the 26-connected region with same gray level region_level = list( set(ind).intersection(set(region_full))) # Remove already processed indices to prevent reprocessing ind = list(set(ind) - set(region_level)) # Add all found neighbours to the total stack of unprocessed neighbours ind_region.extend(region_level) # Update the gray level size zone matrix P_glszm[i - 1, regionSize - 1] += 1 if self.verbose: bar.close() # Crop gray-level axis of GLSZM matrix to between minimum and maximum observed gray-levels # Crop size-zone area axis of GLSZM matrix up to maximum observed size-zone area P_glszm_bounds = numpy.argwhere(P_glszm) (xstart, ystart), (xstop, ystop) = P_glszm_bounds.min( 0), P_glszm_bounds.max(0) + 1 # noqa: F841 self.P_glszm = P_glszm[xstart:xstop, :ystop]
def _calculateMatrix(self): self.logger.debug('Calculating GLRLM matrix in Python') Ng = self.coefficients['Ng'] Nr = self.coefficients['Nr'] padVal = -2000 # use eps or NaN to pad matrix self.matrix[(self.maskArray == 0)] = padVal matrixDiagonals = [] size = numpy.max(self.matrixCoordinates, 1) - numpy.min(self.matrixCoordinates, 1) + 1 # Do not pass kwargs directly, as distances may be specified, which must be forced to [1] for this class angles = imageoperations.generateAngles(size, force2Dextraction=self.kwargs.get('force2D', False), force2Ddimension=self.kwargs.get('force2Ddimension', 0)) self.logger.debug('Calculating diagonals') for angle in angles: staticDims, = numpy.where(angle == 0) # indices for static dimensions for current angle (z, y, x) movingDims, = numpy.where(angle != 0) # indices for moving dimensions for current angle (z, y, x) if len(movingDims) == 1: # movement in one dimension, e.g. angle (0, 0, 1) T = tuple(numpy.append(staticDims, movingDims)) diags = chain.from_iterable(numpy.transpose(self.matrix, T)) elif len(movingDims) == 2: # movement in two dimension, e.g. angle (0, 1, 1) d1 = movingDims[0] d2 = movingDims[1] direction = numpy.where(angle < 0, -1, 1) diags = chain.from_iterable([self.matrix[::direction[0], ::direction[1], ::direction[2]].diagonal(a, d1, d2) for a in range(-self.matrix.shape[d1] + 1, self.matrix.shape[d2])]) else: # movement in 3 dimensions, e.g. angle (1, 1, 1)/ diags = [] direction = numpy.where(angle < 0, -1, 1) for h in [self.matrix[::direction[0], ::direction[1], ::direction[2]].diagonal(a, 0, 1) for a in range(-self.matrix.shape[0] + 1, self.matrix.shape[1])]: diags.extend([h.diagonal(b, 0, 1) for b in range(-h.shape[0] + 1, h.shape[1])]) matrixDiagonals.append(filter(lambda diag: numpy.any(diag != padVal), diags)) P_glrlm = numpy.zeros((Ng, Nr, int(len(matrixDiagonals)))) # Run-Length Encoding (rle) for the list of diagonals # (1 list per direction/angle) self.logger.debug('Calculating run lengths') for angle_idx, angle in enumerate(matrixDiagonals): P = P_glrlm[:, :, angle_idx] # Check whether delineation is 2D for current angle (all diagonals contain 0 or 1 non-pad value) isMultiElement = False for diagonal in angle: if not isMultiElement and numpy.sum(diagonal != padVal) > 1: isMultiElement = True pos, = numpy.where(numpy.diff(diagonal) != 0) pos = numpy.concatenate(([0], pos + 1, [len(diagonal)])) rle = zip([int(n) for n in diagonal[pos[:-1]]], pos[1:] - pos[:-1]) for level, run_length in rle: if level != padVal: P[level - 1, run_length - 1] += 1 if not isMultiElement: P[:] = 0 P_glrlm = self._applyMatrixOptions(P_glrlm, angles) return P_glrlm