def FScore(organ, threshold): model = Model.UNet() model.load_state_dict(torch.load(os.path.join(pathlib.Path(__file__).parent.absolute(), "Models/Model_" + organ.replace(" ", "") + ".pt"))) model = model.cuda() model = model.eval() dataPath = 'Processed_Data/' + organ + "_Val/" dataFolder = os.path.join(pathlib.Path(__file__).parent.absolute(), dataPath) dataFiles = sorted(os.listdir(dataFolder)) d = 0 print('Calculating Statistics') TP = 0 FP = 0 FN = 0 xLen,yLen = [0,0] while d < 150:#len(dataFiles): numStack = min(3, len(dataFiles) - 1 - d) #loading 1400 images at a time (takes about 20GB RAM) p = 0 concatList = [] while p < numStack: imagePath = dataFiles[d] image = pickle.load(open(os.path.join(dataFolder, imagePath), 'rb')) image = image.reshape((2,1,image.shape[2], image.shape[2])) concatList.append(image) p+=1 d+=1 if len(concatList) == 0: break data = np.concatenate(concatList, axis=1) numSlices = data.shape[1] for sliceNum in range(numSlices): x = torch.from_numpy(data[0, sliceNum, :, :]).cuda() y = torch.from_numpy(data[1:2, sliceNum, :,:]).cuda() xLen, yLen = x.shape #need to reshape x.requires_grad = True y.requires_grad = True x = torch.reshape(x, (1,1,xLen,yLen)).float() y = torch.reshape(y, (1,1,xLen,yLen)).float() predictionRaw = (model(x)).cpu().detach().numpy() prediction = PostProcessing.Process(predictionRaw, threshold) for x_idx in range(xLen): for y_idx in range(yLen): if prediction[0,0,x_idx,y_idx] == 1: if y[0,0,x_idx,y_idx] == 1: TP += 1 else: FP += 1 elif y[0,0,x_idx,y_idx] == 1: FN += 1 print("Finished a patient") print("Finished " + str(d) + "patients...") totalPoints = d * xLen * yLen recall = TP / (TP + FN) precision = TP / (TP + FP) F_Score = 2 / (recall**(-1) + precision ** (-1)) accuracy = (totalPoints - FP - FN) / totalPoints return F_Score, recall, precision, accuracy
def TestPlot(organ, threshold): model = Model.UNet() model.load_state_dict(torch.load(os.path.join(pathlib.Path(__file__).parent.absolute(), "Models/Model_" + organ.replace(" ", "") + ".pt"))) model = model.cuda() model.eval() dataPath = 'Processed_Data/' + organ + "_Val/" dataFolder = os.path.join(pathlib.Path(__file__).parent.absolute(), dataPath) dataFiles = os.listdir(dataFolder) filesRange = list(range(len(dataFiles))) random.shuffle(filesRange) for d in filesRange: imagePath = dataFiles[d] #data has 4 dimensions, first is the type (image, contour, background), then slice, and then the pixels. data = pickle.load(open(os.path.join(dataFolder, imagePath), 'rb')) print("Validating with " + dataFiles[d]) x = torch.from_numpy(data[0, :, :]).cuda() y = torch.from_numpy(data[1:2, :,:]).cuda() xLen, yLen = x.shape #need to reshape x = torch.reshape(x, (1,1,xLen,yLen)).float() y = torch.reshape(y, (1,1,xLen,yLen)).float() predictionRaw = (model(x)).cpu().detach().numpy() prediction = PostProcessing.Process(predictionRaw, threshold) x = x.cpu().numpy() y = y.cpu().numpy() maskOnImage = MaskOnImage(x[0,0,:,:], prediction[0,0,:,:]) ROIOnImage = MaskOnImage(x[0,0,:,:], y[0,0,:,:]) fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(15, 15)) #predictedContour = PostProcessing.MaskToContour(prediction[0,0,:,:]) #contour = PostProcessing.MaskToContourImage(y[0,0,:,:]) axs[0,0].imshow(ROIOnImage, cmap = "gray") axs[0,0].set_title("Original Image") #axs[0,1].hist(predictionRaw[0,0,:,:], bins=5) axs[0,1].imshow(maskOnImage, cmap = "gray") axs[0,1].set_title("Mask on Image") axs[1,0].imshow(y[0,0, :,:], cmap = "gray") axs[1,0].set_title("Original Mask") axs[1,1].imshow(prediction[0,0, :,:], cmap="gray") axs[1,1].set_title("Predicted Mask") # axs[2,0].imshow(y[0,1, :,:], cmap = "gray") # axs[2,0].set_title("Original Background") # axs[2,1].imshow(prediction[0,1, :,:], cmap = "gray") # axs[2,1].set_title("Predicted Background") plt.show()
def GetMasks(organ, patientName, path, threshold): #Returns a 3d array of predicted masks for a given patient name. if path == None: path = pathlib.Path(__file__).parent.absolute() device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = Model.UNet() model.load_state_dict( torch.load( os.path.join(path, "Models/Model_" + organ.replace(" ", "") + ".pt"))) model = model.to(device) model.eval() dataPath = 'Processed_Data/' + organ #+ "_Val/" #Currently looking for patients in the test folder. dataFolder = os.path.join(path, dataPath) dataFiles = os.listdir(dataFolder) filesRange = list(range(len(dataFiles))) patientImages = [] for d in filesRange: #First get the files for the patientName given if patientName in dataFiles[d]: patientImages.append(dataFiles[d]) patientImages.sort() predictions = [ ] #First put 2d masks into predictions list and then at the end stack into a 3d array originalMasks = [] for image in patientImages: #data has 4 dimensions, first is the type (image, contour, background), then slice, and then the pixels. data = pickle.load(open(os.path.join(dataFolder, image), 'rb')) x = torch.from_numpy(data[0, :, :]) y = data[1, :, :] x = x.to(device) xLen, yLen = x.shape #need to reshape x = torch.reshape(x, (1, 1, xLen, yLen)).float() predictionRaw = (model(x)).cpu().detach().numpy() #now post-process the image x = x.cpu() x = x.numpy() prediction = PostProcessing.Process(predictionRaw[0, 0, :, :], threshold) predictions.append(prediction) originalMasks.append(y) #Stack into 3d array predictionsArray = np.stack(predictions, axis=0) originalsArray = np.stack(originalMasks, axis=0) return predictionsArray, originalsArray
def Best_Threshold(organ, testSize=10e6, onlyMasks=False, onlyBackground=False): #return the model output threshold that maximizes accuracy. onlyMasks if true will calculate statistics #only for images that have a mask on the plane, and onlyBackground will do it for images without masks. print("Determining most accurate threshold...") model = Model.UNet() model.load_state_dict(torch.load(os.path.join(pathlib.Path(__file__).parent.absolute(), "Models/Model_" + organ.replace(" ", "") + ".pt"))) model = model.cuda() model.eval() dataPath = 'Processed_Data/' + organ + "_Val/" dataFolder = os.path.join(pathlib.Path(__file__).parent.absolute(), dataPath) dataFiles = os.listdir(dataFolder) #also get the bools to find if masks are on image plane boolPath = 'Processed_Data/' + organ + " bools" boolFolder = os.path.join(pathlib.Path(__file__).parent.absolute(), boolPath) boolFiles = os.listdir(boolFolder) #shuffle the order (boolFiles is in same order) filesRange = list(range(len(dataFiles))) random.shuffle(filesRange) accuracies = [] #make a list of average accuracies calculated using different thresholds falsePos = [] falseNeg = [] thresholds = np.linspace(0.05,0.7,15) for thres in thresholds: print("Checking Threshold: %0.3f"%(thres)) d = 0 #get the accuracy for the current threshold value thresAccuracy = [] thresFP = [] thresFN = [] #false pos, neg testSize = min(testSize, len(filesRange)) while d < testSize: numStack = min(200, len(filesRange) - 1 - d) p = 0 concatList = [] while p < numStack: imagePath = dataFiles[d] if onlyMasks: boolMask = pickle.load(open(os.path.join(boolFolder, boolFiles[d]), 'rb')) if not boolMask: p+=1 d+=1 continue elif onlyBackground: boolMask = pickle.load(open(os.path.join(boolFolder, boolFiles[d]), 'rb')) if boolMask: p+=1 d+=1 continue image = pickle.load(open(os.path.join(dataFolder, imagePath), 'rb')) image = image.reshape((2,1,image.shape[1], image.shape[2])) concatList.append(image) p+=1 d+=1 if len(concatList) == 0: break data = np.concatenate(concatList, axis=1) #data has 4 dimensions, first is the type (image, contour, background), then slice, and then the pixels. print('Loaded ' + str(data.shape[1]) + ' images. Proceeding...') data = torch.from_numpy(data) numSlices = data.shape[1] slices = list(range(numSlices)) random.shuffle(slices) for sliceNum in slices: x = data[0, sliceNum, :, :].cuda() y = data[1, sliceNum, :, :] xLen, yLen = x.shape #need to reshape x = torch.reshape(x, (1,1,xLen,yLen)).float() y = torch.reshape(y, (xLen,yLen)).float() predictionRaw = (model(x)).cpu().detach().numpy() prediction = PostProcessing.Process(predictionRaw,thres) prediction = np.reshape(prediction, (xLen, yLen)) #now judge accuracy: thresPrediction = (prediction > thres) imageAccuracy = np.sum(thresPrediction == y.numpy()) imageFP = np.sum(np.logical_and(thresPrediction != y.numpy(), thresPrediction == 1)) imageFN = np.sum(np.logical_and(thresPrediction != y.numpy(), thresPrediction == 0)) imageAccuracy *= 100/float(prediction.size) imageFN *= 100 / float(prediction.size) imageFP *= 100 / float(prediction.size) thresAccuracy.append(imageAccuracy) thresFP.append(imageFP) thresFN.append(imageFN) #false pos, neg accuracies.append(sum(thresAccuracy) / len(thresAccuracy)) falseNeg.append(sum(thresFN)/len(thresFN)) falsePos.append(sum(thresFP)/len(thresFP)) #Now need to determine what the best threshold to use is. Also plot the accuracy, FP, FN: fig, axs = plt.subplots(nrows=3, ncols=1, figsize=(15, 15)) axs[0].scatter(thresholds, accuracies) axs[0].plot(thresholds, accuracies) axs[0].set_title("Accuracy vs Threshold") axs[1].scatter(thresholds, falsePos) axs[1].plot(thresholds, falsePos) axs[1].set_title("False Positives vs Threshold") axs[2].scatter(thresholds, falseNeg) axs[2].plot(thresholds, falseNeg) axs[2].set_title("False Negatives vs Threshold") plt.show() #Get maximimum accuracy maxAccuracy = max(accuracies) maxAccuracyIndices = [i for i, j in enumerate(accuracies) if j == maxAccuracy] bestThreshold = thresholds[maxAccuracyIndices[0]] #save this threshold thresFile = open(os.path.join(pathlib.Path(__file__).parent.absolute(), "Models/Model_" + organ.replace(" ", "") + "_Thres.txt"),'w') thresFile.write(str(bestThreshold)) thresFile.close() with open(os.path.join(pathlib.Path(__file__).parent.absolute(), "Models/Model_" + organ.replace(" ", "") + "_Accuracy.txt"),'wb') as fp: pickle.dump(accuracies, fp) with open(os.path.join(pathlib.Path(__file__).parent.absolute(), "Models/Model_" + organ.replace(" ", "") + "_FalseNeg.txt"),'wb') as fp: pickle.dump(falseNeg, fp) with open(os.path.join(pathlib.Path(__file__).parent.absolute(), "Models/Model_" + organ.replace(" ", "") + "_FalsePos.txt"),'wb') as fp: pickle.dump(falsePos, fp) return bestThreshold
def FScore(organ, path, threshold): if path == None: #if no path supplied, assume that data folders are set up as default in the working directory. path = pathlib.Path(__file__).parent.absolute() device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') print("Device being used for training: " + device.type) model = Model.UNet() model.load_state_dict( torch.load( os.path.join(path, "Models/Model_" + organ.replace(" ", "") + ".pt"))) model = model.to(device) model = model.eval() dataPath = 'Processed_Data/' + organ + "_Val/" dataFolder = os.path.join(path, dataPath) dataFiles = sorted(os.listdir(dataFolder)) d = 0 print('Calculating Fscore Statistics') TP = 0 FP = 0 FN = 0 xLen, yLen = [0, 0] while d < len(dataFiles): numStack = min( 3, len(dataFiles) - 1 - d) #loading 1400 images at a time (takes about 20GB RAM) p = 0 concatList = [] while p < numStack: imagePath = dataFiles[d] image = pickle.load(open(os.path.join(dataFolder, imagePath), 'rb')) image = image.reshape((2, 1, image.shape[2], image.shape[2])) concatList.append(image) p += 1 d += 1 if len(concatList) == 0: break data = np.concatenate(concatList, axis=1) numSlices = data.shape[1] for sliceNum in range(numSlices): x = torch.from_numpy(data[0, sliceNum, :, :]) y = torch.from_numpy(data[1:2, sliceNum, :, :]) x = x.to(device) y = y.to(device) xLen, yLen = x.shape #need to reshape x.requires_grad = True y.requires_grad = True x = torch.reshape(x, (1, 1, xLen, yLen)).float() y = y.cpu().detach().numpy() y = np.reshape(y, (xLen, yLen)) predictionRaw = (model(x)).cpu().detach().numpy() predictionRaw = np.reshape(predictionRaw, (xLen, yLen)) prediction = PostProcessing.Process(predictionRaw, threshold) for x_idx in range(xLen): for y_idx in range(yLen): if prediction[x_idx, y_idx] == 1: if y[x_idx, y_idx] == 1: TP += 1 else: FP += 1 elif y[x_idx, y_idx] == 1: FN += 1 print("Finished " + str(d) + " out of " + str(len(dataFiles)) + " contours...") totalPoints = d * xLen * yLen recall = TP / (TP + FN) precision = TP / (TP + FP) F_Score = 2 / (recall**(-1) + precision**(-1)) accuracy = (totalPoints - FP - FN) / totalPoints return F_Score, recall, precision, accuracy
def TestPlot(organ, path, threshold): if path == None: #if no path supplied, assume that data folders are set up as default in the working directory. path = pathlib.Path(__file__).parent.absolute() device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = Model.UNet() model.load_state_dict( torch.load( os.path.join(path, "Models/Model_" + organ.replace(" ", "") + ".pt"))) model = model.to(device) model.eval() dataPath = 'Processed_Data/' + organ + "_Val/" dataFolder = os.path.join(path, dataPath) dataFiles = os.listdir(dataFolder) filesRange = list(range(len(dataFiles))) random.shuffle(filesRange) for d in filesRange: imagePath = dataFiles[d] #data has 4 dimensions, first is the type (image, contour, background), then slice, and then the pixels. data = pickle.load(open(os.path.join(dataFolder, imagePath), 'rb')) x = torch.from_numpy(data[0, :, :]) y = torch.from_numpy(data[1:2, :, :]) x = x.to(device) y = y.to(device) xLen, yLen = x.shape #need to reshape x = torch.reshape(x, (1, 1, xLen, yLen)).float() y = torch.reshape(y, (1, 1, xLen, yLen)).float() predictionRaw = (model(x)).cpu().detach().numpy() #now post-process the image x = x.cpu() y = y.cpu() x = x.numpy() y = y.numpy() print(np.amax(y)) print(np.amin(y)) prediction = PostProcessing.Process(predictionRaw[0, 0, :, :], threshold) maskOnImage = MaskOnImage( x[0, 0, :, :], prediction) #this puts the mask ontop of the CT image ROIOnImage = MaskOnImage(x[0, 0, :, :], y[0, 0, :, :]) fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(15, 15)) #predictedContour = PostProcessing.MaskToContour(prediction[0,0,:,:]) #contour = PostProcessing.MaskToContourImage(y[0,0,:,:]) axs[0, 0].imshow(ROIOnImage, cmap="gray") axs[0, 0].set_title("Original Image") #axs[0,1].hist(predictionRaw[0,0,:,:], bins=5) axs[0, 1].imshow(maskOnImage, cmap="gray") axs[0, 1].set_title("Mask on Image") axs[1, 0].imshow(y[0, 0, :, :], cmap="gray") axs[1, 0].set_title("Original Mask") axs[1, 1].imshow(prediction, cmap="gray") axs[1, 1].set_title("Predicted Mask") # axs[2,0].imshow(y[0,1, :,:], cmap = "gray") # axs[2,0].set_title("Original Background") # axs[2,1].imshow(prediction[0,1, :,:], cmap = "gray") # axs[2,1].set_title("Predicted Background") plt.show()
def GetContours(organ, patientFileName, threshold, withReal=True, tryLoad=True): #with real loads pre=existing DICOM roi to compare the prediction with model = Model.UNet() model.load_state_dict( torch.load( os.path.join( pathlib.Path(__file__).parent.absolute(), "Models/Model_" + organ.replace(" ", "") + ".pt"))) model = model.cuda() model.eval() #Make a list for all the contour images try: CTs = pickle.load( open( os.path.join( pathlib.Path(__file__).parent.absolute(), str("Prediction_Patients/" + patientFileName + "_Processed.txt")), 'rb')) except: CTs = DicomParsing.GetPredictionData(patientFileName, organ) if tryLoad: try: contourImages, contours = pickle.load( open( os.path.join( pathlib.Path(__file__).parent.absolute(), str("Prediction_Patients/" + organ + "/" + patientFileName + "_predictedContours.txt")), 'rb')) except: contours = [] zValues = [] #keep track of z position to add to contours after contourImages = [] exportNum = 0 for CT in CTs: ipp = CT[1] zValues.append(float(ipp[2])) pixelSpacing = CT[2] sliceThickness = CT[3] x = torch.from_numpy(CT[0]).cuda() xLen, yLen = x.shape #need to reshape x = torch.reshape(x, (1, 1, xLen, yLen)).float() predictionRaw = (model(x)).cpu().detach().numpy() prediction = PostProcessing.Process(predictionRaw, threshold) contourImage, contourPoints = PostProcessing.MaskToContour( prediction[0, 0, :, :]) contourImages.append(contourImage) contours.append(contourPoints) contours = PostProcessing.FixContours(contours) contours = PostProcessing.AddZToContours(contours, zValues) contours = DicomParsing.PixelToContourCoordinates( contours, ipp, zValues, pixelSpacing, sliceThickness) with open( os.path.join( pathlib.Path(__file__).parent.absolute(), str("Prediction_Patients/" + organ + "/" + patientFileName + "_predictedContours.txt")), "wb") as fp: pickle.dump([contourImages, contours], fp) else: contours = [] zValues = [] #keep track of z position to add to contours after contourImages = [] for CT in CTs: ipp = CT[1] zValues.append(float(ipp[2])) pixelSpacing = CT[2] sliceThickness = CT[3] x = torch.from_numpy(CT[0]).cuda() xLen, yLen = x.shape #need to reshape x = torch.reshape(x, (1, 1, xLen, yLen)).float() predictionRaw = (model(x)).cpu().detach().numpy() prediction = PostProcessing.Process(predictionRaw, threshold) contourImage, contourPoints = PostProcessing.MaskToContour( prediction[0, 0, :, :]) contourImages.append(contourImage) contours.append(contourPoints) contours = PostProcessing.FixContours(contours) contours = PostProcessing.AddZToContours(contours, zValues) contours = DicomParsing.PixelToContourCoordinates( contours, ipp, zValues, pixelSpacing, sliceThickness) with open( os.path.join( pathlib.Path(__file__).parent.absolute(), str("Prediction_Patients/" + organ + "/" + patientFileName + "_predictedContours.txt")), "wb") as fp: pickle.dump([contourImages, contours], fp) existingContours = [] if withReal: try: existingContours = pickle.load( open( os.path.join( pathlib.Path(__file__).parent.absolute(), str("Prediction_Patients/" + organ + "/" + patientFileName + "_ExistingContours.txt")), "rb")) except: pass Test.PlotPatientContours(contours, existingContours)
def GetContours(organ, patientFileName, path, threshold, withReal=True, tryLoad=True, plot=True): #with real loads pre=existing DICOM roi to compare the prediction with if path == None: #if no path supplied, assume that data folders are set up as default in the working directory. path = pathlib.Path(__file__).parent.absolute() device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') print("Device being used for predicting: " + device.type) model = Model.UNet() model.load_state_dict( torch.load( os.path.join(path, "Models/Model_" + organ.replace(" ", "") + ".pt"))) model = model.to(device) model.eval() contoursList = [] #The 1d contours list to be returned existingContoursList = [] #Make a list for all the contour images try: CTs = pickle.load( open( os.path.join( path, str("Predictions_Patients/" + patientFileName + "_Processed.txt")), 'rb')) except: CTs = DicomParsing.GetPredictionCTs(patientFileName, path) if tryLoad: try: contourImages, contours = pickle.load( open( os.path.join( path, str("Predictions_Patients/" + organ + "/" + patientFileName + "_predictedContours.txt")), 'rb')) except: contours = [] zValues = [] #keep track of z position to add to contours after contourImages = [] exportNum = 0 for CT in CTs: ipp = CT[1] zValues.append(float(ipp[2])) pixelSpacing = CT[2] sliceThickness = CT[3] x = torch.from_numpy(CT[0]).to(device) xLen, yLen = x.shape #need to reshape x = torch.reshape(x, (1, 1, xLen, yLen)).float() predictionRaw = (model(x)).cpu().detach().numpy() prediction = PostProcessing.Process(predictionRaw, threshold) contourImage, contourPoints = PostProcessing.MaskToContour( prediction[0, 0, :, :]) contourImages.append(contourImage) contours.append(contourPoints) contours = PostProcessing.FixContours(contours) contours = PostProcessing.AddZToContours(contours, zValues) contours = DicomParsing.PixelToContourCoordinates( contours, ipp, zValues, pixelSpacing, sliceThickness) for layer_idx in range(len(contours)): if len(contours[layer_idx]) > 0: for point_idx in range(len(contours[layer_idx])): x = contours[layer_idx][point_idx][0] y = contours[layer_idx][point_idx][1] z = contours[layer_idx][point_idx][2] contoursList.append(x) contoursList.append(y) contoursList.append(z) with open( os.path.join( path, str("Predictions_Patients/" + organ + "/" + patientFileName + "_predictedContours.txt")), "wb") as fp: pickle.dump([contourImages, contours], fp) else: contours = [] zValues = [] #keep track of z position to add to contours after contourImages = [] for CT in CTs: ipp = CT[1] zValues.append(float(ipp[2])) pixelSpacing = CT[2] sliceThickness = CT[3] x = torch.from_numpy(CT[0]).to(device) xLen, yLen = x.shape #need to reshape x = torch.reshape(x, (1, 1, xLen, yLen)).float() predictionRaw = (model(x)).cpu().detach().numpy() prediction = predictionRaw prediction[0, 0, :, :] = PostProcessing.Process( predictionRaw[0, 0, :, :], threshold) contourImage, contourPoints = PostProcessing.MaskToContour( prediction[0, 0, :, :]) contourImages.append(contourImage) contours.append(contourPoints) contours = PostProcessing.FixContours(contours) contours = PostProcessing.AddZToContours(contours, zValues) contours = DicomParsing.PixelToContourCoordinates( contours, ipp, zValues, pixelSpacing, sliceThickness) for layer_idx in range(len(contours)): if len(contours[layer_idx]) > 0: for point_idx in range(len(contours[layer_idx])): x = contours[layer_idx][point_idx][0] y = contours[layer_idx][point_idx][1] z = contours[layer_idx][point_idx][2] contoursList.append(x) contoursList.append(y) contoursList.append(z) with open( os.path.join( path, str("Predictions_Patients/" + organ + "/" + patientFileName + "_predictedContours.txt")), "wb") as fp: pickle.dump([contourImages, contours], fp) existingContours = [] if withReal: try: existingContours = pickle.load( open( os.path.join( path, str("Predictions_Patients/" + organ + "/" + patientFileName + "_ExistingContours.txt")), "rb")) for layer_idx in range(len(existingContours)): if len(existingContours[layer_idx]) > 0: for point_idx in range(len(existingContours[layer_idx])): x = existingContours[layer_idx][point_idx][0] y = existingContours[layer_idx][point_idx][1] z = existingContours[layer_idx][point_idx][2] existingContoursList.append(x) existingContoursList.append(y) existingContoursList.append(z) except: existingContours = DicomParsing.GetDICOMContours( patientFileName, organ, path) for layer_idx in range(len(existingContours)): if len(existingContours[layer_idx]) > 0: for point_idx in range(len(existingContours[layer_idx])): x = existingContours[layer_idx][point_idx][0] y = existingContours[layer_idx][point_idx][1] z = existingContours[layer_idx][point_idx][2] existingContoursList.append(x) existingContoursList.append(y) existingContoursList.append(z) if plot == True: Test.PlotPatientContours(contours, existingContours) return contoursList, existingContoursList