def create_line(param, fname, coord, nz): """ Create vertical line in 3D volume :param param: :param fname: :param coord: :param nz: :return: """ # duplicate volume (assumes input file is nifti) copy(fname, 'line.nii', verbose=param.verbose) # set all voxels to zero img = Image('line.nii') data = get_data_or_scalar('0', img.data) data_concat = concatenate_along_4th_dimension(img.data, data) img.data = np.prod(data_concat, axis=3) img.save() labels = [] if isinstance(coord[0], Coordinate): for x, y, _, _ in coord: labels.extend([Coordinate([x, y, iz, 1]) for iz in range(nz)]) else: # backwards compat labels.extend( [Coordinate([coord[0], coord[1], iz, 1]) for iz in range(nz)]) create_labels(img, labels).save() return 'line.nii'
def create_line(param, fname, coord, nz): """ Create vertical line in 3D volume :param param: :param fname: :param coord: :param nz: :return: """ # duplicate volume (assumes input file is nifti) copy(fname, 'line.nii', verbose=param.verbose) # set all voxels to zero run_proc(['sct_maths', '-i', 'line.nii', '-mul', '0', '-o', 'line.nii'], param.verbose) labels = [] if isinstance(coord[0], Coordinate): for x, y, _, _ in coord: labels.extend([Coordinate([x, y, iz, 1]) for iz in range(nz)]) else: # backwards compat labels.extend([Coordinate([coord[0], coord[1], iz, 1]) for iz in range(nz)]) create_labels(Image("line.nii"), labels).save(path="line.nii") return 'line.nii'
def create_labels_along_segmentation( img: Image, labels: Sequence[Tuple[int, int]]) -> Image: """ Create an image with labels defined along the spinal cord segmentation (or centerline). Input image does **not** need to be RPI (re-orientation is done within this function). :param img: source segmentation :param labels: list of label tuples as (z_value, label_value) :returns: labeled segmentation (Image) """ og_orientation = img.orientation if og_orientation != "RPI": img.change_orientation("RPI") out = zeros_like(img) for idx_label, label in enumerate(labels): z, value = label # update z based on native image orientation (z should represent superior-inferior axis) coord = Coordinate( [z, z, z] ) # since we don't know which dimension corresponds to the superior-inferior # axis, we put z in all dimensions (we don't care about x and y here) _, _, z_rpi = coord.permute(img, 'RPI') # if z=-1, replace with nz/2 if z == -1: z_rpi = int(np.round(out.dim[2] / 2.0)) # get center of mass of segmentation at given z x, y = ndimage.measurements.center_of_mass( np.array(img.data[:, :, z_rpi])) # round values to make indices x, y = int(np.round(x)), int(np.round(y)) # display info logger.debug(f"Label # {idx_label}: {x}, {y}. {z_rpi} --> {value}") if len(out.data.shape) == 3: out.data[x, y, z_rpi] = value elif len(out.data.shape) == 2: if z != 0: raise ValueError( f"2D coordinates should have a Z value of 0! Current value: {coord.z}" ) out.data[x, y] = value if out.orientation != og_orientation: out.change_orientation(og_orientation) img.change_orientation(og_orientation) return out
def getNonZeroCoordinates(self, sorting=None, reverse_coord=False, coordValue=False): """ This function return all the non-zero coordinates that the image contains. Coordinate list can also be sorted by x, y, z, or the value with the parameter sorting='x', sorting='y', sorting='z' or sorting='value' If reverse_coord is True, coordinate are sorted from larger to smaller. """ n_dim = 1 if self.dim[3] == 1: n_dim = 3 else: n_dim = 4 if self.dim[2] == 1: n_dim = 2 try: if n_dim == 3: X, Y, Z = (self.data > 0).nonzero() list_coordinates = [Coordinate([X[i], Y[i], Z[i], self.data[X[i], Y[i], Z[i]]]) for i in range(0, len(X))] elif n_dim == 2: try: X, Y = (self.data > 0).nonzero() list_coordinates = [Coordinate([X[i], Y[i], 0, self.data[X[i], Y[i]]]) for i in range(0, len(X))] except ValueError: X, Y, Z = (self.data > 0).nonzero() list_coordinates = [Coordinate([X[i], Y[i], 0, self.data[X[i], Y[i], 0]]) for i in range(0, len(X))] except Exception as e: sct.printv('ERROR: Exception ' + str(e) + ' caught while geting non Zeros coordinates', 1, 'error') if coordValue: from spinalcordtoolbox.types import CoordinateValue if n_dim == 3: list_coordinates = [CoordinateValue([X[i], Y[i], Z[i], self.data[X[i], Y[i], Z[i]]]) for i in range(0, len(X))] else: list_coordinates = [CoordinateValue([X[i], Y[i], 0, self.data[X[i], Y[i]]]) for i in range(0, len(X))] if sorting is not None: if reverse_coord not in [True, False]: raise ValueError('reverse_coord parameter must be a boolean') if sorting == 'x': list_coordinates = sorted(list_coordinates, key=lambda obj: obj.x, reverse=reverse_coord) elif sorting == 'y': list_coordinates = sorted(list_coordinates, key=lambda obj: obj.y, reverse=reverse_coord) elif sorting == 'z': list_coordinates = sorted(list_coordinates, key=lambda obj: obj.z, reverse=reverse_coord) elif sorting == 'value': list_coordinates = sorted(list_coordinates, key=lambda obj: obj.value, reverse=reverse_coord) else: raise ValueError("sorting parameter must be either 'x', 'y', 'z' or 'value'") return list_coordinates
def test_create_labels(test_image): a = test_image.copy() labels = [Coordinate(l) for l in [[0, 1, 0, 99], [0, 1, 2, 5]]] b = sct_labels.create_labels(a, labels) assert b.data[0, 1, 0] == 99 assert b.data[0, 1, 2] == 5
def create_label_along_segmentation(self): """ Create an image with labels defined along the spinal cord segmentation (or centerline). Input image does **not** need to be RPI (re-orientation is done within this function). Example: object_define=ProcessLabels(fname_segmentation, coordinates=[coord_1, coord_2, coord_i]), where coord_i='z,value'. If z=-1, then use z=nz/2 (i.e. center of FOV in superior-inferior direction) Returns """ # reorient input image to RPI im_rpi = self.image_input.copy().change_orientation('RPI') im_output_rpi = zeros_like(im_rpi) # loop across labels for ilabel, coord in enumerate(self.coordinates): # split coord string list_coord = coord.split(',') # convert to int() and assign to variable z, value = [int(i) for i in list_coord] # update z based on native image orientation (z should represent superior-inferior axis) coord = Coordinate( [z, z, z] ) # since we don't know which dimension corresponds to the superior-inferior # axis, we put z in all dimensions (we don't care about x and y here) _, _, z_rpi = coord.permute(self.image_input, 'RPI') # if z=-1, replace with nz/2 if z == -1: z_rpi = int(np.round(im_output_rpi.dim[2] / 2.0)) # get center of mass of segmentation at given z x, y = ndimage.measurements.center_of_mass( np.array(im_rpi.data[:, :, z_rpi])) # round values to make indices x, y = int(np.round(x)), int(np.round(y)) # display info sct.printv( 'Label #' + str(ilabel) + ': ' + str(x) + ',' + str(y) + ',' + str(z_rpi) + ' --> ' + str(value), 1) if len(im_output_rpi.data.shape) == 3: im_output_rpi.data[x, y, z_rpi] = value elif len(im_output_rpi.data.shape) == 2: assert str( z ) == '0', "ERROR: 2D coordinates should have a Z value of 0. Z coordinate is :" + str( z) im_output_rpi.data[x, y] = value # change orientation back to native return im_output_rpi.change_orientation(self.image_input.orientation)
def test_sct_label_utils_cubic_to_point(): """Run the CLI script and verify the resulting center of mass coordinate.""" fname_out = 'test_centerofmass.nii.gz' sct_label_utils.main(argv=[ '-i', 't2/t2_seg-manual.nii.gz', '-cubic-to-point', '-o', fname_out ]) # Note: Old, broken 'sct_testing' test used '31,28,25,1' as ground truth. Is this a regression? assert Image(fname_out).getNonZeroCoordinates() == [ Coordinate([31, 27, 25, 1]) ]
def test_create_labels_empty(test_image): a = test_image.copy() expected = zeros_like(a) labels = [Coordinate(l) for l in [[0, 0, 0, 7], [0, 1, 2, 5]]] expected.data[0, 0, 0] = 7 expected.data[0, 1, 2] = 5 b = sct_labels.create_labels_empty(a, labels) diff = b.data == expected.data assert diff.all()