Browse Source

Major improvements and more info.

Luka 1 week ago
parent
commit
3bece3a8e9
1 changed files with 189 additions and 81 deletions
  1. 189 81
      SeekTransformModule/SeekTransformModule.py

+ 189 - 81
SeekTransformModule/SeekTransformModule.py

@@ -4,6 +4,7 @@ import scipy
 from scipy.spatial.distance import cdist
 from scipy.spatial.distance import cdist
 from scipy.spatial.transform import Rotation as R
 from scipy.spatial.transform import Rotation as R
 import slicer
 import slicer
+import itertools
 from DICOMLib import DICOMUtils
 from DICOMLib import DICOMUtils
 from collections import deque
 from collections import deque
 import vtk
 import vtk
@@ -11,6 +12,7 @@ from slicer.ScriptedLoadableModule import *
 import qt
 import qt
 import matplotlib.pyplot as plt
 import matplotlib.pyplot as plt
 import csv
 import csv
+import time
 
 
 #exec(open("C:/Users/lkomar/Documents/Prostata/FirstTryRegister.py").read())
 #exec(open("C:/Users/lkomar/Documents/Prostata/FirstTryRegister.py").read())
 
 
@@ -87,6 +89,7 @@ class MyTransformModuleLogic(ScriptedLoadableModuleLogic):
     
     
     
     
     def run(self, selectedMethod, applyRotation, applyTranslation, applyScaling, writefilecheck):
     def run(self, selectedMethod, applyRotation, applyTranslation, applyScaling, writefilecheck):
+        start_time = time.time()
         print("Calculating...")
         print("Calculating...")
         
         
         def group_points(points, threshold):
         def group_points(points, threshold):
@@ -329,6 +332,21 @@ class MyTransformModuleLogic(ScriptedLoadableModuleLogic):
 
 
             return moving, R, t
             return moving, R, t
 
 
+        def match_points(cbct_points, ct_points):
+            """
+            Vrne cbct_points permutirane tako, da so najbolje poravnani s ct_points
+            glede na vsoto evklidskih razdalj.
+            """
+            best_perm = None
+            min_total_dist = float('inf')
+
+            for perm in itertools.permutations(cbct_points):
+                total_dist = sum(np.linalg.norm(np.array(p1) - np.array(p2)) for p1, p2 in zip(perm, ct_points))
+                if total_dist < min_total_dist:
+                    min_total_dist = total_dist
+                    best_perm = perm
+
+            return list(best_perm)
 
 
         def compute_translation(moving_points, fixed_points, rotation_matrix):
         def compute_translation(moving_points, fixed_points, rotation_matrix):
             """
             """
@@ -369,16 +387,16 @@ class MyTransformModuleLogic(ScriptedLoadableModuleLogic):
             for i in range(4):
             for i in range(4):
                 for j in range(4):
                 for j in range(4):
                     vtk_matrix.SetElement(i, j, transform_matrix[i, j])
                     vtk_matrix.SetElement(i, j, transform_matrix[i, j])
-            #print("Transform matrix:")
-            #for i in range(4):
-            #    print(" ".join(f"{vtk_matrix.GetElement(i, j):.6f}" for j in range(4)))
+            print("Transform matrix:")
+            for i in range(4):
+               print(" ".join(f"{vtk_matrix.GetElement(i, j):.6f}" for j in range(4)))
             # Create vtkTransform and set the matrix
             # Create vtkTransform and set the matrix
             transform = vtk.vtkTransform()
             transform = vtk.vtkTransform()
             transform.SetMatrix(vtk_matrix)
             transform.SetMatrix(vtk_matrix)
             return transform
             return transform
         
         
         
         
-        def detect_points_region_growing(volume_name, yesCbct, intensity_threshold=3000, x_min=90, x_max=380, y_min=190, y_max=380, z_min=80, z_max=140, max_distance=9, centroid_merge_threshold=5):
+        def detect_points_region_growing(volume_name, yesCbct, create_marker, intensity_threshold=3000, x_min=90, x_max=380, y_min=190, y_max=380, z_min=80, z_max=140, max_distance=9, centroid_merge_threshold=5):
             volume_node = slicer.util.getNode(volume_name)
             volume_node = slicer.util.getNode(volume_name)
             if not volume_node:
             if not volume_node:
                 raise RuntimeError(f"Volume {volume_name} not found.")
                 raise RuntimeError(f"Volume {volume_name} not found.")
@@ -439,12 +457,13 @@ class MyTransformModuleLogic(ScriptedLoadableModuleLogic):
             for centroid, intensity in centroids:
             for centroid, intensity in centroids:
                 if not any(np.linalg.norm(centroid - existing_centroid) < centroid_merge_threshold for existing_centroid, _ in unique_centroids):
                 if not any(np.linalg.norm(centroid - existing_centroid) < centroid_merge_threshold for existing_centroid, _ in unique_centroids):
                     unique_centroids.append((centroid, intensity))
                     unique_centroids.append((centroid, intensity))
-                    
-            markups_node = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsFiducialNode", f"Markers_{volume_name}")
-            for centroid, intensity in unique_centroids:
-                markups_node.AddControlPoint(*centroid)
-                markups_node.SetDisplayVisibility(False)
-                #print(f"Detected Centroid (RAS): {centroid}, Max Intensity: {intensity}")
+            
+            if create_marker:        
+                markups_node = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsFiducialNode", f"Markers_{volume_name}")
+                for centroid, intensity in unique_centroids:
+                    markups_node.AddControlPoint(*centroid)
+                    markups_node.SetDisplayVisibility(False)
+                    #print(f"Detected Centroid (RAS): {centroid}, Max Intensity: {intensity}")
 
 
             return unique_centroids
             return unique_centroids
 
 
@@ -526,28 +545,83 @@ class MyTransformModuleLogic(ScriptedLoadableModuleLogic):
             table_ras = np.array(ijkToRasMatrix.MultiplyPoint([*table_ijk, 1]))[:3]
             table_ras = np.array(ijkToRasMatrix.MultiplyPoint([*table_ijk, 1]))[:3]
 
 
             # Doda marker za višino mize
             # Doda marker za višino mize
-            table_node = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsFiducialNode", f"VišinaMize_{ct_volume_name}")
-            table_node.AddControlPoint(table_ras)
-            table_node.SetDisplayVisibility(False)
+            #table_node = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsFiducialNode", f"VišinaMize_{ct_volume_name}")
+            #table_node.AddControlPoint(table_ras)
+            #table_node.SetDisplayVisibility(False)
 
 
             # Izračun višine v mm
             # Izračun višine v mm
-            image_center_y = dims[1] // 2
-            pixel_offset = table_top_y - image_center_y
-            mm_offset = pixel_offset * spacing[1]
+            #image_center_y = dims[1] // 2
+            pixel_offset = table_top_y
+            #mm_offset = pixel_offset * spacing[1]
 
 
             #print(f"📏 Miza je {abs(mm_offset):.2f} mm {'nižja' if mm_offset > 0 else 'višja'} od središča.")
             #print(f"📏 Miza je {abs(mm_offset):.2f} mm {'nižja' if mm_offset > 0 else 'višja'} od središča.")
-            #print(f"📏 Miza je {abs(pixel_offset)} pixel {'nižja' if pixel_offset > 0 else 'višja'} od središča.")
+            #print(f"📏 Miza je {abs(pixel_offset)} pixlov {'nižja' if pixel_offset > 0 else 'višja'} od središča.")
+            #print(f"📏 CBCT table_top_y: {table_top_y}, image_center_y: {image_center_y}, pixel_offset: {pixel_offset}, mm_offset: {mm_offset}")
+            #print(f"🛏 Absolutna višina mize (RAS Y): {table_ras[1]} mm")
             # Shrani v CSV
             # Shrani v CSV
             if writefilecheck:
             if writefilecheck:
                 file_path = os.path.join(os.path.dirname(__file__), "heightdata.csv")
                 file_path = os.path.join(os.path.dirname(__file__), "heightdata.csv")
                 with open(file_path, mode='a', newline='') as file:
                 with open(file_path, mode='a', newline='') as file:
                     writer = csv.writer(file)
                     writer = csv.writer(file)
                     modality = "CBCT " if yesCbct else "CT "
                     modality = "CBCT " if yesCbct else "CT "
-                    writer.writerow([modality, ct_volume_name, f" Upper part of table detected at Z = {mm_offset:.2f} mm, {pixel_offset} pixels"])
+                    writer.writerow([modality, ct_volume_name, f" Upper part of table detected at Z = {table_ras[1]:.2f} mm"])
 
 
-            return mm_offset, pixel_offset
-        
+            return table_ras[1], pixel_offset
         
         
+        def align_cbct_to_ct(volumeNode, scan_type, offset, CT_offset=None, CT_spacing=None):
+            """
+            Aligns CBCT volume to CT volume based on height offset.
+
+            Args:
+                volumeNode (vtkMRMLScalarVolumeNode): The volume node to be aligned.
+                scan_type (str): The type of scan ("CT" or "CBCT").
+                offset (float): The height offset of the current volume from the center in mm.
+                CT_offset (float, optional): The height offset of the CT volume from the center. Required for CBCT alignment.
+                CT_spacing (float, optional): The voxel spacing of the CT volume in mm (for scaling the offset).
+
+            Returns:
+                float: The alignment offset applied to the CBCT volume (if applicable).
+            """
+            if scan_type == "CT":
+                CT_offset = offset
+                CT_spacing = volumeNode.GetSpacing()[1]
+                #print(f"CT offset set to: {CT_offset}, CT spacing: {CT_spacing} mm/voxel")
+                return CT_offset, CT_spacing
+            else:
+                if CT_offset is None or CT_spacing is None:
+                    raise ValueError("CT_offset and CT_spacing must be provided to align CBCT to CT.")
+
+                CBCT_offset = offset
+
+                # Razlika v mm brez skaliranja na CBCT_spacing
+                alignment_offset_mm = CT_offset - CBCT_offset
+
+                #print(f"CT offset: {CT_offset}, CBCT offset: {CBCT_offset}")
+                #print(f"CT spacing: {CT_spacing} mm/voxel, CBCT spacing: {volumeNode.GetSpacing()[1]} mm/voxel")
+                #print(f"Aligning CBCT with CT. Offset in mm: {alignment_offset_mm}")
+
+                # Uporabi transformacijo
+                transform = vtk.vtkTransform()
+                transform.Translate(0, alignment_offset_mm, 0)
+
+                transformNode = slicer.vtkMRMLTransformNode()
+                slicer.mrmlScene.AddNode(transformNode)
+                transformNode.SetAndObserveTransformToParent(transform)
+                volumeNode.SetAndObserveTransformNodeID(transformNode.GetID())
+                slicer.vtkSlicerTransformLogic().hardenTransform(volumeNode)
+                slicer.mrmlScene.RemoveNode(transformNode)
+
+                return alignment_offset_mm
+    
+        def print_orientation(volume_name):
+            node = slicer.util.getNode(volume_name)
+            matrix = vtk.vtkMatrix4x4()
+            node.GetIJKToRASMatrix(matrix)
+            print(f"{volume_name} IJK→RAS:")
+            for i in range(3):
+                print([matrix.GetElement(i, j) for j in range(3)])
+
+
         # Globalni seznami za končno statistiko
         # Globalni seznami za končno statistiko
         prostate_size_est = []
         prostate_size_est = []
         ctcbct_distance = []
         ctcbct_distance = []
@@ -560,7 +634,8 @@ class MyTransformModuleLogic(ScriptedLoadableModuleLogic):
 
 
         for i in range(studyItems.GetNumberOfIds()):
         for i in range(studyItems.GetNumberOfIds()):
             studyItem = studyItems.GetId(i)
             studyItem = studyItems.GetId(i)
-            
+            studyName = shNode.GetItemName(studyItem)
+            print(f"\nProcessing study: {studyName}")
             # **LOKALNI** seznami, resetirajo se pri vsakem study-ju
             # **LOKALNI** seznami, resetirajo se pri vsakem study-ju
             cbct_list = []
             cbct_list = []
             ct_list = []
             ct_list = []
@@ -630,31 +705,11 @@ class MyTransformModuleLogic(ScriptedLoadableModuleLogic):
                     if not volumeNode or not volumeNode.IsA("vtkMRMLScalarVolumeNode"):
                     if not volumeNode or not volumeNode.IsA("vtkMRMLScalarVolumeNode"):
                         print("Can't find volumeNode")
                         print("Can't find volumeNode")
                         #continue  # Preskoči, če ni veljaven volume
                         #continue  # Preskoči, če ni veljaven volume
-                    
-                    mm_offset, pixel_offset = find_table_top_z(volumeName, writefilecheck, yesCbct)
-                        
-                    if scan_type == "CT":
-                        CT_offset = pixel_offset #Določi koliko je višina CT slike od središča
-                    else:
-                        # Poravnava CBCT z višino CT
-                        CBCT_offset = pixel_offset
-                        alignment_offset = CT_offset - CBCT_offset
-                        print(f"Poravnavam CBCT z CT. Offset: {alignment_offset}")
-
-                        # Uporabi alignment_offset za premik CBCT
-                        transform = vtk.vtkTransform()
-                        transform.Translate(0, alignment_offset, 0)  # Premik samo po y-osi (višina)
-                        
-                        #transformacija volumna oziroma poravnava po višini
-                        transformNode = slicer.vtkMRMLTransformNode()
-                        slicer.mrmlScene.AddNode(transformNode)
-                        transformNode.SetAndObserveTransformToParent(transform)
-                        volumeNode.SetAndObserveTransformNodeID(transformNode.GetID())  # Prilepi transformacijo na CBCT
-                        slicer.mrmlScene.RemoveNode(transformNode)  # Odstrani transformacijo iz scene
                         
                         
                         
                         
                     # Detekcija točk v volumnu
                     # Detekcija točk v volumnu
-                    grouped_points = detect_points_region_growing(volumeName, yesCbct, intensity_threshold=3000)
+                    ustvari_marker = False  # Ustvari markerje
+                    grouped_points = detect_points_region_growing(volumeName, yesCbct, ustvari_marker, intensity_threshold=3000)
                     #print(f"Populating volume_points_dict with key ('{scan_type}', '{volumeName}')")
                     #print(f"Populating volume_points_dict with key ('{scan_type}', '{volumeName}')")
                     volume_points_dict[(scan_type, volumeName)] = grouped_points
                     volume_points_dict[(scan_type, volumeName)] = grouped_points
                     #print(volume_points_dict)  # Check if the key is correctly added
                     #print(volume_points_dict)  # Check if the key is correctly added
@@ -663,40 +718,66 @@ class MyTransformModuleLogic(ScriptedLoadableModuleLogic):
             if cbct_list and ct_list:
             if cbct_list and ct_list:
                 ct_volume_name = ct_list[0]  # Uporabi prvi CT kot referenco
                 ct_volume_name = ct_list[0]  # Uporabi prvi CT kot referenco
                 print(f"\nProcessing CT: {ct_volume_name}")
                 print(f"\nProcessing CT: {ct_volume_name}")
-                #yesCbct = False
-                    
+                yesCbct = False
+                                               
+                mm_offset, pixel_offset = find_table_top_z(ct_volume_name, writefilecheck, yesCbct)
+                CT_offset, CT_spacing = align_cbct_to_ct(slicer.util.getNode(ct_volume_name), "CT", mm_offset)                                    
+                     
                 
                 
                 ct_points = [centroid for centroid, _ in volume_points_dict[("CT", ct_volume_name)]]
                 ct_points = [centroid for centroid, _ in volume_points_dict[("CT", ct_volume_name)]]
-
+                
+                print(f"CT points: {ct_points}")
                 if len(ct_points) < 3:
                 if len(ct_points) < 3:
-                    print(f"CT volume {ct_volume_name} doesn't have enough points for registration.")
+                    print(f"CT volume {ct_volume_name} doesn't have enough points for registration. Points: {len(ct_points)}")
+                    continue
                 else:
                 else:
                     for cbct_volume_name in cbct_list:
                     for cbct_volume_name in cbct_list:
-                        print(f"\nProcessing CBCT Volume: {cbct_volume_name}")
-                        #yesCbct = True
-                        cbct_points = [centroid for centroid, _ in volume_points_dict[("CBCT", cbct_volume_name)]]
+                        print(f"\nProcessing CBCT Volume: {cbct_volume_name}")                        
+                        yesCbct = True
+                        scan_type = "CBCT" #redundant but here we are
+                        cbct_volume_node = slicer.util.getNode(cbct_volume_name)
+                        
+                        mm_offset, pixel_offset = find_table_top_z(cbct_volume_name, writefilecheck, yesCbct)                        
+                                            
+                        cbct_points = [centroid for centroid, _ in volume_points_dict[("CBCT", cbct_volume_name)]] #zastareli podatki
+                        cbct_points_array = np.array(cbct_points)  # Pretvorba v numpy array
+                        
+                        #print_orientation(ct_volume_name)
+                        #print_orientation(cbct_volume_name)
+                        
+                        #for i, (cb, ct) in enumerate(zip(cbct_points, ct_points)):
+                        #    print(f"Pair {i}: CBCT {cb}, CT {ct}, diff: {np.linalg.norm(cb - ct):.2f}")
+                        
+                        align_cbct_to_ct(cbct_volume_node, scan_type, mm_offset, CT_offset, CT_spacing)
+                        #cbct_points = [centroid for centroid, _ in volume_points_dict[("CBCT", cbct_volume_name)]] #zastareli podatki
+                        # for i in range(len(cbct_points)):
+                        #     x, y, z = cbct_points[i]
+                        #     y_new = y + mm_offset  # Upoštevaj premik zaradi poravnave mize
+                        #     cbct_points[i] = (x, y_new, z)  # Posodobi marker s premaknjeno višino
+                        ustvari_marker = False  # Ustvari markerje
+                        cbct_points = [centroid for centroid, _ in detect_points_region_growing(cbct_volume_name, yesCbct, ustvari_marker, intensity_threshold=3000)]                     
+                        #cbct_points = detect_points_region_growing(cbct_volume_name, yesCbct, intensity_threshold=3000)   
+                        #cbct_points = [centroid for centroid, _ in volume_points_dict[("CBCT", cbct_volume_name)]] #zastareli podatki
+                                                
                                                 
                                                 
-                        #find_table_top_z(cbct_volume_name, writefilecheck, yesCbct)
-
                         if len(cbct_points) < 3:
                         if len(cbct_points) < 3:
-                            print(f"CBCT Volume '{cbct_volume_name}' doesn't have enough points for registration.")
+                            print(f"CBCT Volume '{cbct_volume_name}' doesn't have enough points for registration. Points: {len(cbct_points)}")
                             continue
                             continue
-
+                        
+                        
+                        
+                        
+                        #Sortiramo točke po X/Y/Z da se izognemo težavam pri poravnavi
+                        cbct_points = match_points(cbct_points, ct_points)
+                        
+                        #for i, (cb, ct) in enumerate(zip(cbct_points, ct_points)):
+                        #    print(f"Pair {i}: CBCT {cb}, CT {ct}, diff: {np.linalg.norm(cb - ct):.2f}")
+                        
                         # Shranjevanje razdalj
                         # Shranjevanje razdalj
                         distances_ct_cbct = []
                         distances_ct_cbct = []
                         distances_internal = {"A-B": [], "B-C": [], "C-A": []}
                         distances_internal = {"A-B": [], "B-C": [], "C-A": []}
 
 
-                        cbct_points_array = np.array(cbct_points)  # Pretvorba v numpy array
-
-                        ct_volume_node = slicer.util.getNode(ct_volume_name)
                         cbct_volume_node = slicer.util.getNode(cbct_volume_name)
                         cbct_volume_node = slicer.util.getNode(cbct_volume_name)
-                        #ct_spacing = ct_volume_node.GetSpacing()  # (x_spacing, y_spacing, z_spacing)
-                        #cbct_spacing = cbct_volume_node.GetSpacing()  # (x_spacing, y_spacing, z_spacing)
-                        
-                        #ct_scale_factor = np.array(ct_spacing)  # Spacing za CT (x, y, z)                        
-                        #cbct_scale_factor = np.array(cbct_spacing)  # Spacing za CBCT (x, y, z)
-                        #print(ct_scale_factor, cbct_scale_factor)
-
                         
                         
                         # Sortiramo točke po Z-koordinati (ali X/Y, če raje uporabljaš drugo os)
                         # Sortiramo točke po Z-koordinati (ali X/Y, če raje uporabljaš drugo os)
                         cbct_points_sorted = cbct_points_array[np.argsort(cbct_points_array[:, 2])]
                         cbct_points_sorted = cbct_points_array[np.argsort(cbct_points_array[:, 2])]
@@ -727,10 +808,10 @@ class MyTransformModuleLogic(ScriptedLoadableModuleLogic):
                         # Izberi metodo glede na uporabnikov izbor
                         # Izberi metodo glede na uporabnikov izbor
                         chosen_rotation_matrix = np.eye(3)
                         chosen_rotation_matrix = np.eye(3)
                         chosen_translation_vector = np.zeros(3)
                         chosen_translation_vector = np.zeros(3)
-
+                        print("Markerji pred transformacijo:", cbct_points, ct_points)
                         if applyScaling:
                         if applyScaling:
                             scaling_factors = compute_optimal_scaling_per_axis(cbct_points, ct_points)
                             scaling_factors = compute_optimal_scaling_per_axis(cbct_points, ct_points)
-                            print("Scaling factors: ", scaling_factors)
+                            #print("Scaling factors: ", scaling_factors)
                             cbct_points = compute_scaling(cbct_points, scaling_factors)
                             cbct_points = compute_scaling(cbct_points, scaling_factors)
 
 
                         if applyRotation:
                         if applyRotation:
@@ -740,12 +821,13 @@ class MyTransformModuleLogic(ScriptedLoadableModuleLogic):
                                 chosen_rotation_matrix = compute_Horn_rotation(cbct_points, ct_points)
                                 chosen_rotation_matrix = compute_Horn_rotation(cbct_points, ct_points)
                             elif selectedMethod == "Iterative Closest Point (Kabsch)":
                             elif selectedMethod == "Iterative Closest Point (Kabsch)":
                                 _, chosen_rotation_matrix, _ = icp_algorithm(cbct_points, ct_points)
                                 _, chosen_rotation_matrix, _ = icp_algorithm(cbct_points, ct_points)
-                            print("Rotation Matrix:\n", chosen_rotation_matrix)
+                            #print("Rotation Matrix:\n", chosen_rotation_matrix)
 
 
                         if applyTranslation:
                         if applyTranslation:
                             chosen_translation_vector = compute_translation(cbct_points, ct_points, chosen_rotation_matrix)
                             chosen_translation_vector = compute_translation(cbct_points, ct_points, chosen_rotation_matrix)
-                            print("Translation Vector:\n", chosen_translation_vector)
-
+                            #print("Translation Vector:\n", chosen_translation_vector)
+                        
+                        
                         # Ustvari vtkTransformNode in ga poveži z CBCT volumenom
                         # Ustvari vtkTransformNode in ga poveži z CBCT volumenom
                         imeTransformNoda = cbct_volume_name + " Transform"
                         imeTransformNoda = cbct_volume_name + " Transform"
                         transform_node = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLTransformNode", imeTransformNoda)
                         transform_node = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLTransformNode", imeTransformNoda)
@@ -761,7 +843,17 @@ class MyTransformModuleLogic(ScriptedLoadableModuleLogic):
                         # Uporabi transformacijo na volumnu (fizična aplikacija)
                         # Uporabi transformacijo na volumnu (fizična aplikacija)
                         slicer.vtkSlicerTransformLogic().hardenTransform(cbct_volume_node) #aplicira transformacijo na volumnu
                         slicer.vtkSlicerTransformLogic().hardenTransform(cbct_volume_node) #aplicira transformacijo na volumnu
                         slicer.mrmlScene.RemoveNode(transform_node) # Odstrani transformacijo iz scene
                         slicer.mrmlScene.RemoveNode(transform_node) # Odstrani transformacijo iz scene
-                        print("Transform successful on ", cbct_volume_name)
+                        #print("Transform successful on ", cbct_volume_name)
+                        ustvari_marker = True  # Ustvari markerje
+                        cbct_points = [centroid for centroid, _ in detect_points_region_growing(cbct_volume_name, yesCbct, ustvari_marker, intensity_threshold=3000)]
+                        print("Markerji po transformaciji:\n", cbct_points, ct_points)
+                        
+                        # Izračun razdalj
+                        errors = [np.linalg.norm(cbct - ct) for cbct, ct in zip(cbct_points, ct_points)]
+                        mean_error = np.mean(errors)
+
+                        print("Individualne napake:", errors)
+                        print("📏 Povprečna napaka poravnave: {:.2f} mm".format(mean_error))
 
 
             else:
             else:
                 print(f"Study {studyItem} doesn't have any appropriate CT or CBCT volumes.")
                 print(f"Study {studyItem} doesn't have any appropriate CT or CBCT volumes.")
@@ -769,19 +861,35 @@ class MyTransformModuleLogic(ScriptedLoadableModuleLogic):
         # Izpis globalne statistike
         # Izpis globalne statistike
         
         
         
         
-        if(writefilecheck):
+        if writefilecheck:
             print("Distances between CT & CBCT markers: ", ctcbct_distance)
             print("Distances between CT & CBCT markers: ", ctcbct_distance)
             print("Distances between pairs of markers for each volume: ", prostate_size_est)
             print("Distances between pairs of markers for each volume: ", prostate_size_est)
-            # Define the file path for the CSV file
-            file_path = os.path.join(os.path.dirname(__file__), "study_data.csv")
+            
+            # Define file paths
+            prostate_size_file = os.path.join(os.path.dirname(__file__), "prostate_size.csv")
+            ctcbct_distance_file = os.path.join(os.path.dirname(__file__), "ct_cbct_distance.csv")
 
 
-            # Write lists to the CSV file
-            with open(file_path, mode='w', newline='') as file: #w za write, a za append
+            # Write prostate size data
+            with open(prostate_size_file, mode='w', newline='') as file:
                 writer = csv.writer(file)
                 writer = csv.writer(file)
-                # Write headers
-                writer.writerow(["Prostate Size", "CT-CBCT Distance"])
-                # Write data rows
-                for i in range(len(prostate_size_est)):
-                    writer.writerow([prostate_size_est[i], ctcbct_distance[i]])
-                print("File written at ", file_path)
+                writer.writerow(["Prostate Size"])
+                for size in prostate_size_est:
+                    writer.writerow([size])
+            print("Prostate size file written at ", prostate_size_file)
 
 
+            # Write CT-CBCT distance data
+            with open(ctcbct_distance_file, mode='w', newline='') as file:
+                writer = csv.writer(file)
+                writer.writerow(["CT-CBCT Distance"])
+                for distance in ctcbct_distance:
+                    writer.writerow([distance])
+            print("CT-CBCT distance file written at ", ctcbct_distance_file)
+
+        end_time = time.time()
+        # Calculate and print elapsed time
+        elapsed_time = end_time - start_time
+        # Convert to minutes and seconds
+        minutes = int(elapsed_time // 60)
+        seconds = elapsed_time % 60
+
+        print(f"Execution time: {minutes} minutes and {seconds:.6f} seconds")