|
import nibabel as nib
|
|
import pydicom
|
|
import os
|
|
import glob
|
|
import numpy as np
|
|
from copy import deepcopy
|
|
from matplotlib.patches import Polygon
|
|
import warnings
|
|
from scipy.ndimage import find_objects
|
|
from scipy.ndimage.morphology import binary_fill_holes
|
|
from skimage import measure
|
|
from PIL import Image, ImageDraw
|
|
import scipy
|
|
import datetime
|
|
from dicom_to_nii import set_header_info
|
|
|
|
def convert_nii_to_dicom(dicomctdir, predictedNiiFile, predictedDicomFile, predicted_structures=[], rtstruct_colors=[], refCT = None):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct = RTstruct()
|
|
struct.load_from_nii(predictedNiiFile, predicted_structures, rtstruct_colors)
|
|
if not struct.Contours[0].Mask_PixelSpacing == refCT.PixelSpacing:
|
|
struct.resample_struct(refCT.PixelSpacing)
|
|
struct.export_Dicom(refCT, predictedDicomFile)
|
|
|
|
|
|
|
|
def integer_to_onehot(niiFile):
|
|
|
|
|
|
nnunet_integer_nib = nib.load(niiFile)
|
|
nnunet_integer_data = nnunet_integer_nib.get_fdata()
|
|
|
|
|
|
onehot_data = np.zeros(nnunet_integer_data.shape)
|
|
for i in np.unique(nnunet_integer_data):
|
|
onehot_data[nnunet_integer_data == i] = 2**i
|
|
|
|
|
|
contours_exist = np.ones(len(np.unique(onehot_data))).astype(bool).tolist()
|
|
|
|
|
|
image_nii = nib.Nifti1Image(onehot_data, affine=np.eye(4))
|
|
|
|
image_nii = set_header_info(image_nii, nnunet_integer_nib.header['pixdim'][1:4], [nnunet_integer_nib.header['qoffset_x'],nnunet_integer_nib.header['qoffset_y'],nnunet_integer_nib.header['qoffset_z']], contours_exist = contours_exist)
|
|
|
|
nib.save(image_nii,niiFile)
|
|
|
|
return
|
|
|
|
def save_nii_image(nib_img, nib_header,dst_dir, dst_filename, contours_exist = None):
|
|
|
|
image_nii = nib.Nifti1Image(nib_img, affine=np.eye(4))
|
|
|
|
if contours_exist is not None:
|
|
image_nii = set_header_info(image_nii, nib_header['pixdim'][1:4], [nib_header['qoffset_x'],nib_header['qoffset_y'],nib_header['qoffset_z']], contours_exist = contours_exist)
|
|
else:
|
|
image_nii = set_header_info(image_nii, nib_header['pixdim'][1:4], [nib_header['qoffset_x'],nib_header['qoffset_y'],nib_header['qoffset_z']])
|
|
|
|
nib.save(image_nii, os.path.join(dst_dir,dst_filename))
|
|
|
|
def Taubin_smoothing(contour):
|
|
""" Here, we do smoothing in 2D contours!
|
|
Parameters:
|
|
a Nx2 numpy array containing the contour to smooth
|
|
Returns:
|
|
a Nx2 numpy array containing the smoothed contour """
|
|
smoothingloops = 5
|
|
smoothed = [np.empty_like(contour) for i in range(smoothingloops+1)]
|
|
smoothed[0] = contour
|
|
for i in range(smoothingloops):
|
|
|
|
for vertex_i in range(smoothed[0].shape[0]):
|
|
if vertex_i == 0:
|
|
vertex_prev = smoothed[i].shape[0]-1
|
|
vertex_next = vertex_i+1
|
|
elif vertex_i == smoothed[i].shape[0]-1:
|
|
vertex_prev = vertex_i-1
|
|
vertex_next = 0
|
|
else:
|
|
vertex_prev = vertex_i -1
|
|
vertex_next = vertex_i +1
|
|
neighbours_x = np.array([smoothed[i][vertex_prev,0], smoothed[i][vertex_next,0]])
|
|
neighbours_y = np.array([smoothed[i][vertex_prev,1], smoothed[i][vertex_next,1]])
|
|
smoothed[i+1][vertex_i,0] = smoothed[i][vertex_i,0] - 0.3*(smoothed[i][vertex_i,0] - np.mean(neighbours_x))
|
|
smoothed[i+1][vertex_i,1] = smoothed[i][vertex_i,1] - 0.3*(smoothed[i][vertex_i,1] - np.mean(neighbours_y))
|
|
|
|
return np.round(smoothed[smoothingloops],3)
|
|
|
|
class RTstruct:
|
|
|
|
def __init__(self):
|
|
self.SeriesInstanceUID = ""
|
|
self.PatientInfo = {}
|
|
self.StudyInfo = {}
|
|
self.CT_SeriesInstanceUID = ""
|
|
self.DcmFile = ""
|
|
self.isLoaded = 0
|
|
self.Contours = []
|
|
self.NumContours = 0
|
|
|
|
|
|
def print_struct_info(self, prefix=""):
|
|
print(prefix + "Struct: " + self.SeriesInstanceUID)
|
|
print(prefix + " " + self.DcmFile)
|
|
|
|
|
|
def print_ROINames(self):
|
|
print("RT Struct UID: " + self.SeriesInstanceUID)
|
|
count = -1
|
|
for contour in self.Contours:
|
|
count += 1
|
|
print(' [' + str(count) + '] ' + contour.ROIName)
|
|
|
|
def resample_struct(self, newvoxelsize):
|
|
|
|
if newvoxelsize is not None:
|
|
for i, Contour in enumerate(self.Contours):
|
|
source_shape = Contour.Mask_GridSize
|
|
voxelsize = Contour.Mask_PixelSpacing
|
|
VoxelX_source = Contour.Mask_Offset[0] + np.arange(source_shape[0])*voxelsize[0]
|
|
VoxelY_source = Contour.Mask_Offset[1] + np.arange(source_shape[1])*voxelsize[1]
|
|
VoxelZ_source = Contour.Mask_Offset[2] + np.arange(source_shape[2])*voxelsize[2]
|
|
|
|
target_shape = np.ceil(np.array(source_shape).astype(float)*np.array(voxelsize).astype(float)/newvoxelsize).astype(int)
|
|
VoxelX_target = Contour.Mask_Offset[0] + np.arange(target_shape[0])*newvoxelsize[0]
|
|
VoxelY_target = Contour.Mask_Offset[1] + np.arange(target_shape[1])*newvoxelsize[1]
|
|
VoxelZ_target = Contour.Mask_Offset[2] + np.arange(target_shape[2])*newvoxelsize[2]
|
|
|
|
contour = Contour.Mask
|
|
|
|
if(all(source_shape == target_shape) and np.linalg.norm(np.subtract(voxelsize, newvoxelsize) < 0.001)):
|
|
print("! Image does not need filtering")
|
|
else:
|
|
|
|
sigma = [0, 0, 0]
|
|
if(newvoxelsize[0] > voxelsize[0]): sigma[0] = 0.4 * (newvoxelsize[0]/voxelsize[0])
|
|
if(newvoxelsize[1] > voxelsize[1]): sigma[1] = 0.4 * (newvoxelsize[1]/voxelsize[1])
|
|
if(newvoxelsize[2] > voxelsize[2]): sigma[2] = 0.4 * (newvoxelsize[2]/voxelsize[2])
|
|
|
|
if(sigma != [0, 0, 0]):
|
|
contour = scipy.ndimage.gaussian_filter(contour.astype(float), sigma)
|
|
|
|
contour[np.where(contour>=0.5)] = 1
|
|
contour[np.where(contour<0.5)] = 0
|
|
|
|
xi = np.array(np.meshgrid(VoxelX_target, VoxelY_target, VoxelZ_target))
|
|
xi = np.rollaxis(xi, 0, 4)
|
|
xi = xi.reshape((xi.size // 3, 3))
|
|
|
|
|
|
contour = scipy.interpolate.interpn((VoxelX_source,VoxelY_source,VoxelZ_source), contour, xi, method='nearest', fill_value=0, bounds_error=False).astype(bool).reshape(target_shape).transpose(1,0,2)
|
|
Contour.Mask_PixelSpacing = newvoxelsize
|
|
Contour.Mask_GridSize = list(contour.shape)
|
|
Contour.NumVoxels = Contour.Mask_GridSize[0] * Contour.Mask_GridSize[1] * Contour.Mask_GridSize[2]
|
|
Contour.Mask = contour
|
|
self.Contours[i]=Contour
|
|
|
|
|
|
def import_Dicom_struct(self, CT):
|
|
if(self.isLoaded == 1):
|
|
print("Warning: RTstruct " + self.SeriesInstanceUID + " is already loaded")
|
|
return
|
|
dcm = pydicom.dcmread(self.DcmFile)
|
|
|
|
self.CT_SeriesInstanceUID = CT.SeriesInstanceUID
|
|
|
|
for dcm_struct in dcm.StructureSetROISequence:
|
|
ReferencedROI_id = next((x for x, val in enumerate(dcm.ROIContourSequence) if val.ReferencedROINumber == dcm_struct.ROINumber), -1)
|
|
dcm_contour = dcm.ROIContourSequence[ReferencedROI_id]
|
|
|
|
Contour = ROIcontour()
|
|
Contour.SeriesInstanceUID = self.SeriesInstanceUID
|
|
Contour.ROIName = dcm_struct.ROIName
|
|
Contour.ROIDisplayColor = dcm_contour.ROIDisplayColor
|
|
|
|
|
|
|
|
Contour.Mask = np.zeros((CT.GridSize[0], CT.GridSize[1], CT.GridSize[2]), dtype=np.bool)
|
|
Contour.Mask_GridSize = CT.GridSize
|
|
Contour.Mask_PixelSpacing = CT.PixelSpacing
|
|
Contour.Mask_Offset = CT.ImagePositionPatient
|
|
Contour.Mask_NumVoxels = CT.NumVoxels
|
|
Contour.ContourMask = np.zeros((CT.GridSize[0], CT.GridSize[1], CT.GridSize[2]), dtype=np.bool)
|
|
|
|
SOPInstanceUID_match = 1
|
|
|
|
if not hasattr(dcm_contour, 'ContourSequence'):
|
|
print("This structure has no attribute ContourSequence. Skipping ...")
|
|
continue
|
|
|
|
for dcm_slice in dcm_contour.ContourSequence:
|
|
Slice = {}
|
|
|
|
|
|
Slice["XY_dcm"] = list(zip( np.array(dcm_slice.ContourData[0::3]), np.array(dcm_slice.ContourData[1::3]) ))
|
|
Slice["Z_dcm"] = float(dcm_slice.ContourData[2])
|
|
|
|
|
|
Slice["XY_img"] = list(zip( ((np.array(dcm_slice.ContourData[0::3]) - CT.ImagePositionPatient[0]) / CT.PixelSpacing[0]), ((np.array(dcm_slice.ContourData[1::3]) - CT.ImagePositionPatient[1]) / CT.PixelSpacing[1]) ))
|
|
Slice["Z_img"] = (Slice["Z_dcm"] - CT.ImagePositionPatient[2]) / CT.PixelSpacing[2]
|
|
Slice["Slice_id"] = int(round(Slice["Z_img"]))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
img = Image.new('L', (CT.GridSize[0], CT.GridSize[1]), 0)
|
|
if(len(Slice["XY_img"]) > 1): ImageDraw.Draw(img).polygon(Slice["XY_img"], outline=1, fill=1)
|
|
mask = np.array(img)
|
|
Contour.Mask[:,:,Slice["Slice_id"]] = np.logical_or(Contour.Mask[:,:,Slice["Slice_id"]], mask)
|
|
|
|
|
|
img = Image.new('L', (CT.GridSize[0], CT.GridSize[1]), 0)
|
|
if(len(Slice["XY_img"]) > 1): ImageDraw.Draw(img).polygon(Slice["XY_img"], outline=1, fill=0)
|
|
mask = np.array(img)
|
|
Contour.ContourMask[:,:,Slice["Slice_id"]] = np.logical_or(Contour.ContourMask[:,:,Slice["Slice_id"]], mask)
|
|
|
|
Contour.ContourSequence.append(Slice)
|
|
|
|
|
|
if(hasattr(dcm_slice, 'ContourImageSequence') and CT.SOPInstanceUIDs[Slice["Slice_id"]] != dcm_slice.ContourImageSequence[0].ReferencedSOPInstanceUID):
|
|
SOPInstanceUID_match = 0
|
|
|
|
if SOPInstanceUID_match != 1:
|
|
print("WARNING: some SOPInstanceUIDs don't match during importation of " + Contour.ROIName + " contour on CT image")
|
|
|
|
self.Contours.append(Contour)
|
|
self.NumContours += 1
|
|
|
|
self.isLoaded = 1
|
|
|
|
def load_from_nii(self, struct_nii_path, rtstruct_labels, rtstruct_colors):
|
|
|
|
|
|
struct_nib = nib.load(struct_nii_path)
|
|
struct_data = struct_nib.get_fdata()
|
|
|
|
|
|
if len(struct_nib.header.extensions)==0:
|
|
contoursexist = []
|
|
else:
|
|
|
|
contoursexist = list(struct_nib.header.extensions[0].get_content())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if not len(rtstruct_labels) == len(contoursexist) :
|
|
|
|
|
|
self.NumContours = len(rtstruct_labels)
|
|
else:
|
|
self.NumContours = len(rtstruct_labels)
|
|
print("num contours", self.NumContours, len(rtstruct_labels) , len(contoursexist))
|
|
|
|
|
|
for c in range(self.NumContours):
|
|
|
|
Contour = ROIcontour()
|
|
Contour.SeriesInstanceUID = self.SeriesInstanceUID
|
|
Contour.ROIName = rtstruct_labels[c]
|
|
if rtstruct_colors[c] == None:
|
|
Contour.ROIDisplayColor = [0, 0, 255]
|
|
else:
|
|
Contour.ROIDisplayColor = rtstruct_colors[c]
|
|
if len(contoursexist)!=0 and contoursexist[c] == 0:
|
|
Contour.Mask = np.zeros((struct_nib.header['dim'][1], struct_nib.header['dim'][2], struct_nib.header['dim'][3]), dtype=np.bool_)
|
|
else:
|
|
Contour.Mask = np.bitwise_and(struct_data.astype(int), 2 ** c).astype(bool)
|
|
|
|
Contour.Mask_GridSize = [struct_nib.header['dim'][1], struct_nib.header['dim'][2], struct_nib.header['dim'][3]]
|
|
Contour.Mask_PixelSpacing = [struct_nib.header['pixdim'][1], struct_nib.header['pixdim'][2], struct_nib.header['pixdim'][3]]
|
|
Contour.Mask_Offset = [struct_nib.header['qoffset_x'], struct_nib.header['qoffset_y'], struct_nib.header['qoffset_z']]
|
|
Contour.Mask_NumVoxels = struct_nib.header['dim'][1].astype(int) * struct_nib.header['dim'][2].astype(int) * struct_nib.header['dim'][3].astype(int)
|
|
|
|
|
|
|
|
self.Contours.append(Contour)
|
|
|
|
|
|
def export_Dicom(self, refCT, outputFile):
|
|
print("EXPORT DICOM")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SOPInstanceUID = pydicom.uid.generate_uid()
|
|
|
|
|
|
meta = pydicom.dataset.FileMetaDataset()
|
|
meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.481.3'
|
|
meta.MediaStorageSOPInstanceUID = SOPInstanceUID
|
|
|
|
meta.ImplementationClassUID = '1.2.250.1.59.3.0.3.5.0'
|
|
meta.TransferSyntaxUID = '1.2.840.10008.1.2'
|
|
meta.FileMetaInformationGroupLength = 188
|
|
|
|
|
|
|
|
|
|
ds = pydicom.dataset.FileDataset(outputFile, {}, file_meta=meta, preamble=b"\0" * 128)
|
|
|
|
|
|
ds.PatientName = refCT.PatientInfo.PatientName
|
|
ds.PatientID = refCT.PatientInfo.PatientID
|
|
ds.PatientBirthDate = refCT.PatientInfo.PatientBirthDate
|
|
ds.PatientSex = refCT.PatientInfo.PatientSex
|
|
|
|
|
|
dt = datetime.datetime.now()
|
|
ds.StudyDate = dt.strftime('%Y%m%d')
|
|
ds.StudyTime = dt.strftime('%H%M%S.%f')
|
|
ds.AccessionNumber = '1'
|
|
ds.ReferringPhysicianName = 'NA'
|
|
ds.StudyInstanceUID = refCT.StudyInfo.StudyInstanceUID
|
|
ds.StudyID = refCT.StudyInfo.StudyID
|
|
|
|
|
|
|
|
|
|
ds.Modality = 'RTSTRUCT'
|
|
ds.SeriesDescription = 'AI-predicted' + dt.strftime('%Y%m%d') + dt.strftime('%H%M%S.%f')
|
|
ds.OperatorsName = 'MIRO AI team'
|
|
ds.SeriesInstanceUID = pydicom.uid.generate_uid()
|
|
ds.SeriesNumber = '1'
|
|
|
|
|
|
ds.Manufacturer = 'MIRO lab'
|
|
|
|
|
|
|
|
|
|
|
|
ds.FrameOfReferenceUID = refCT.FrameOfReferenceUID
|
|
ds.PositionReferenceIndicator = ''
|
|
|
|
|
|
ds.StructureSetLabel = 'AI predicted'
|
|
|
|
|
|
ds.StructureSetDate = dt.strftime('%Y%m%d')
|
|
ds.StructureSetTime = dt.strftime('%H%M%S.%f')
|
|
ds.ReferencedFrameOfReferenceSequence = pydicom.Sequence()
|
|
|
|
dssr = pydicom.Dataset()
|
|
dssr.FrameOfReferenceUID = refCT.FrameOfReferenceUID
|
|
dssr.RTReferencedStudySequence = pydicom.Sequence()
|
|
|
|
dssr_refStudy = pydicom.Dataset()
|
|
dssr_refStudy.ReferencedSOPClassUID = '1.2.840.10008.3.1.2.3.1'
|
|
dssr_refStudy.ReferencedSOPInstanceUID = refCT.StudyInfo.StudyInstanceUID
|
|
dssr_refStudy.RTReferencedSeriesSequence = pydicom.Sequence()
|
|
|
|
dssr_refStudy_series = pydicom.Dataset()
|
|
dssr_refStudy_series.SeriesInstanceUID = refCT.SeriesInstanceUID
|
|
dssr_refStudy_series.ContourImageSequence = pydicom.Sequence()
|
|
|
|
for slc in range(len(refCT.SOPInstanceUIDs)):
|
|
dssr_refStudy_series_slc = pydicom.Dataset()
|
|
dssr_refStudy_series_slc.ReferencedSOPClassUID = refCT.SOPClassUID
|
|
dssr_refStudy_series_slc.ReferencedSOPInstanceUID = refCT.SOPInstanceUIDs[slc]
|
|
|
|
dssr_refStudy_series.ContourImageSequence.append(dssr_refStudy_series_slc)
|
|
|
|
|
|
dssr_refStudy.RTReferencedSeriesSequence.append(dssr_refStudy_series)
|
|
|
|
dssr.RTReferencedStudySequence.append(dssr_refStudy)
|
|
|
|
ds.ReferencedFrameOfReferenceSequence.append(dssr)
|
|
|
|
ds.StructureSetROISequence = pydicom.Sequence()
|
|
|
|
for iroi in range(self.NumContours):
|
|
|
|
dssr = pydicom.Dataset()
|
|
dssr.ROINumber = iroi + 1
|
|
dssr.ReferencedFrameOfReferenceUID = ds.FrameOfReferenceUID
|
|
dssr.ROIName = self.Contours[iroi].ROIName
|
|
|
|
dssr.ROIGenerationAlgorithm = 'AUTOMATIC'
|
|
|
|
ds.StructureSetROISequence.append(dssr)
|
|
|
|
|
|
del dssr
|
|
|
|
|
|
|
|
|
|
ds.ROIContourSequence = pydicom.Sequence()
|
|
|
|
for iroi in range(self.NumContours):
|
|
|
|
dssr = pydicom.Dataset()
|
|
dssr.ROIDisplayColor = self.Contours[iroi].ROIDisplayColor
|
|
dssr.ReferencedROINumber = iroi + 1
|
|
dssr.ContourSequence = pydicom.Sequence()
|
|
|
|
polygonMeshList = self.Contours[iroi].getROIContour()
|
|
|
|
z_coords = list(np.arange(self.Contours[iroi].Mask_Offset[2],self.Contours[iroi].Mask_Offset[2]+self.Contours[iroi].Mask_GridSize[2]*self.Contours[iroi].Mask_PixelSpacing[2], self.Contours[iroi].Mask_PixelSpacing[2]))
|
|
|
|
for polygon in polygonMeshList:
|
|
|
|
|
|
dssr_slc = pydicom.Dataset()
|
|
dssr_slc.ContourGeometricType = 'CLOSED_PLANAR'
|
|
|
|
|
|
|
|
dssr_slc.NumberOfContourPoints = len(polygon[0::3])
|
|
|
|
|
|
smoothed_array_2D = Taubin_smoothing(np.transpose(np.array([polygon[0::3],polygon[1::3]])))
|
|
|
|
polygon[0::3] = smoothed_array_2D[:,0]
|
|
polygon[1::3] = smoothed_array_2D[:,1]
|
|
dssr_slc.ContourData = polygon
|
|
|
|
|
|
polygon_z = polygon[2]
|
|
slc = z_coords.index(polygon_z)
|
|
|
|
dssr_slc.ContourImageSequence = pydicom.Sequence()
|
|
|
|
dssr_slc_ref = pydicom.Dataset()
|
|
dssr_slc_ref.ReferencedSOPClassUID = refCT.SOPClassUID
|
|
dssr_slc_ref.ReferencedSOPInstanceUID = refCT.SOPInstanceUIDs[slc]
|
|
dssr_slc.ContourImageSequence.append(dssr_slc_ref)
|
|
|
|
|
|
dssr.ContourSequence.append(dssr_slc)
|
|
|
|
|
|
ds.ROIContourSequence.append(dssr)
|
|
|
|
|
|
ds.RTROIObservationsSequence = pydicom.Sequence()
|
|
|
|
for iroi in range(self.NumContours):
|
|
|
|
dssr = pydicom.Dataset()
|
|
dssr.ObservationNumber = iroi + 1
|
|
dssr.ReferencedROINumber = iroi + 1
|
|
dssr.ROIObservationLabel = self.Contours[iroi].ROIName
|
|
dssr.RTROIInterpretedType = 'ORGAN'
|
|
|
|
dssr.ROIInterpreter = ''
|
|
|
|
ds.RTROIObservationsSequence.append(dssr)
|
|
|
|
|
|
ds.ApprovalStatus = 'UNAPPROVED'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ds.SpecificCharacterSet = 'ISO_IR 100'
|
|
|
|
|
|
ds.SOPClassUID = '1.2.840.10008.5.1.4.1.1.481.3'
|
|
ds.SOPInstanceUID = SOPInstanceUID
|
|
|
|
|
|
|
|
print("Export dicom RTSTRUCT: " + outputFile)
|
|
ds.save_as(outputFile)
|
|
|
|
|
|
|
|
|
|
class ROIcontour:
|
|
|
|
def __init__(self):
|
|
self.SeriesInstanceUID = ""
|
|
self.ROIName = ""
|
|
self.ContourSequence = []
|
|
|
|
def getROIContour(self):
|
|
|
|
try:
|
|
from skimage.measure import label, find_contours
|
|
from skimage.segmentation import find_boundaries
|
|
except:
|
|
print('Module skimage (scikit-image) not installed, ROIMask cannot be converted to ROIContour')
|
|
return 0
|
|
|
|
polygonMeshList = []
|
|
for zSlice in range(self.Mask.shape[2]):
|
|
|
|
labeledImg, numberOfLabel = label(self.Mask[:, :, zSlice], return_num=True)
|
|
|
|
for i in range(1, numberOfLabel + 1):
|
|
|
|
singleLabelImg = labeledImg == i
|
|
contours = find_contours(singleLabelImg.astype(np.uint8), level=0.6)
|
|
|
|
if len(contours) > 0:
|
|
|
|
if len(contours) == 2:
|
|
|
|
|
|
contours2 = find_contours(singleLabelImg.astype(np.uint8), level=0.4)
|
|
|
|
interiorContour = contours2[1]
|
|
polygonMesh = []
|
|
for point in interiorContour:
|
|
|
|
xCoord = np.round(point[1]) * self.Mask_PixelSpacing[1] + self.Mask_Offset[1]
|
|
yCoord = np.round(point[0]) * self.Mask_PixelSpacing[0] + self.Mask_Offset[0]
|
|
|
|
|
|
zCoord = zSlice * self.Mask_PixelSpacing[2] + self.Mask_Offset[2]
|
|
|
|
polygonMesh.append(yCoord)
|
|
polygonMesh.append(xCoord)
|
|
|
|
|
|
polygonMesh.append(zCoord)
|
|
|
|
polygonMeshList.append(polygonMesh)
|
|
|
|
contour = contours[0]
|
|
|
|
polygonMesh = []
|
|
for point in contour:
|
|
|
|
|
|
|
|
xCoord = np.round(point[1]) * self.Mask_PixelSpacing[0] + self.Mask_Offset[0]
|
|
yCoord = np.round(point[0]) * self.Mask_PixelSpacing[1] + self.Mask_Offset[1]
|
|
zCoord = zSlice * self.Mask_PixelSpacing[2] + self.Mask_Offset[2]
|
|
|
|
polygonMesh.append(xCoord)
|
|
polygonMesh.append(yCoord)
|
|
|
|
|
|
polygonMesh.append(zCoord)
|
|
|
|
polygonMeshList.append(polygonMesh)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return polygonMeshList
|
|
|