|
@@ -446,7 +446,102 @@ class MyTransformModuleLogic(ScriptedLoadableModuleLogic):
|
|
|
|
|
|
|
|
|
|
return unique_centroids
|
|
return unique_centroids
|
|
-
|
|
+
|
|
|
|
+ def find_table_top_z(ct_volume_name, writefilecheck, yesCbct):
|
|
|
|
+ """
|
|
|
|
+ Najde višino zgornjega roba mize v CT/CBCT volumnu in doda markerje.
|
|
|
|
+
|
|
|
|
+ :param ct_volume_name: Ime volumna v slicerju
|
|
|
|
+ :param writefilecheck: Če je True, zapiše rezultat v CSV
|
|
|
|
+ :param yesCbct: Če je True, uporabi CBCT thresholde
|
|
|
|
+ :return: Višina zgornjega roba mize v mm
|
|
|
|
+ """
|
|
|
|
+
|
|
|
|
+ ct_volume_node = slicer.util.getNode(ct_volume_name)
|
|
|
|
+ image_data = ct_volume_node.GetImageData()
|
|
|
|
+ spacing = ct_volume_node.GetSpacing()
|
|
|
|
+ dims = image_data.GetDimensions()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ np_array = slicer.util.arrayFromVolume(ct_volume_node)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ mid_ijk = [dims[0] // 2, dims[1] // 2, dims[2] // 2]
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ mid_ijk = [max(0, min(dims[i] - 1, mid_ijk[i])) for i in range(3)]
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ ijkToRasMatrix = vtk.vtkMatrix4x4()
|
|
|
|
+ ct_volume_node.GetIJKToRASMatrix(ijkToRasMatrix)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ mid_z_voxel = mid_ijk[2]
|
|
|
|
+ slice_data = np_array[mid_z_voxel, :, :]
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ mid_x_voxel = mid_ijk[0] - 15
|
|
|
|
+ column_values = slice_data[:, mid_x_voxel]
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ threshold = -300
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ previous_value = -1000
|
|
|
|
+ edge_count = 0
|
|
|
|
+ table_top_y = None
|
|
|
|
+ min_jump = 100
|
|
|
|
+
|
|
|
|
+ for y in range(len(column_values) - 1, -1, -1):
|
|
|
|
+ intensity = column_values[y]
|
|
|
|
+
|
|
|
|
+ if (intensity - previous_value) > min_jump and intensity > threshold:
|
|
|
|
+ if yesCbct:
|
|
|
|
+ table_top_y = y
|
|
|
|
+ print(f"Zgornji rob mize najden pri Y = {y}")
|
|
|
|
+ break
|
|
|
|
+ edge_count += 1
|
|
|
|
+ print(f"Zaznan rob mize pri Y = {y}")
|
|
|
|
+ if edge_count == 2:
|
|
|
|
+ table_top_y = y
|
|
|
|
+ print(f"Zgornji rob mize najden pri Y = {y}")
|
|
|
|
+ break
|
|
|
|
+ previous_value = column_values[y]
|
|
|
|
+
|
|
|
|
+ if table_top_y is None:
|
|
|
|
+ print("❌ Zgornji rob mize ni bil najden!")
|
|
|
|
+ return None
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ table_ijk = [mid_x_voxel, table_top_y, mid_z_voxel]
|
|
|
|
+ table_ras = np.array(ijkToRasMatrix.MultiplyPoint([*table_ijk, 1]))[:3]
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ table_node = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsFiducialNode", f"VišinaMize_{ct_volume_name}")
|
|
|
|
+ table_node.AddControlPoint(table_ras)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ image_center_y = dims[1] // 2
|
|
|
|
+ pixel_offset = table_top_y - image_center_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(pixel_offset)} pixel {'nižja' if pixel_offset > 0 else 'višja'} od središča.")
|
|
|
|
+
|
|
|
|
+ if writefilecheck:
|
|
|
|
+ file_path = os.path.join(os.path.dirname(__file__), "heightdata.csv")
|
|
|
|
+ with open(file_path, mode='w', newline='') as file:
|
|
|
|
+ writer = csv.writer(file)
|
|
|
|
+ writer.writerow([f"Upper part of table detected at Z = {mm_offset:.2f} mm, {pixel_offset} pixels"])
|
|
|
|
+
|
|
|
|
+ return mm_offset
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -474,10 +569,6 @@ class MyTransformModuleLogic(ScriptedLoadableModuleLogic):
|
|
|
|
|
|
for j in range(volumeItems.GetNumberOfIds()):
|
|
for j in range(volumeItems.GetNumberOfIds()):
|
|
intermediateItem = volumeItems.GetId(j)
|
|
intermediateItem = volumeItems.GetId(j)
|
|
-
|
|
|
|
-
|
|
|
|
- intermediateName = shNode.GetItemName(intermediateItem)
|
|
|
|
-
|
|
|
|
|
|
|
|
finalVolumeItems = vtk.vtkIdList()
|
|
finalVolumeItems = vtk.vtkIdList()
|
|
shNode.GetItemChildren(intermediateItem, finalVolumeItems)
|
|
shNode.GetItemChildren(intermediateItem, finalVolumeItems)
|
|
@@ -496,30 +587,15 @@ 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
|
|
+
|
|
-
|
|
+
|
|
-
|
|
+
|
|
- storageNode = volumeNode.GetStorageNode()
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- if not storageNode:
|
|
|
|
- print("Can't find storageNode")
|
|
|
|
- continue
|
|
|
|
volumeName = volumeNode.GetName()
|
|
volumeName = volumeNode.GetName()
|
|
-
|
|
|
|
imageItem = shNode.GetItemByDataNode(volumeNode)
|
|
imageItem = shNode.GetItemByDataNode(volumeNode)
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
modality = shNode.GetItemAttribute(imageItem, "DICOM.Modality")
|
|
modality = shNode.GetItemAttribute(imageItem, "DICOM.Modality")
|
|
-
|
|
|
|
|
|
|
|
- dimensions = volumeNode.GetImageData().GetDimensions()
|
|
+
|
|
- spacing = volumeNode.GetSpacing()
|
|
+
|
|
|
|
|
|
|
|
|
|
if modality != "CT":
|
|
if modality != "CT":
|
|
@@ -542,12 +618,12 @@ class MyTransformModuleLogic(ScriptedLoadableModuleLogic):
|
|
cbct_list.append(volumeName)
|
|
cbct_list.append(volumeName)
|
|
scan_type = "CBCT"
|
|
scan_type = "CBCT"
|
|
yesCbct = True
|
|
yesCbct = True
|
|
- print("CBCT")
|
|
+
|
|
else:
|
|
else:
|
|
ct_list.append(volumeName)
|
|
ct_list.append(volumeName)
|
|
scan_type = "CT"
|
|
scan_type = "CT"
|
|
yesCbct = False
|
|
yesCbct = False
|
|
- print("CT")
|
|
+
|
|
|
|
|
|
|
|
|
|
grouped_points = detect_points_region_growing(volumeName, yesCbct, intensity_threshold=3000)
|
|
grouped_points = detect_points_region_growing(volumeName, yesCbct, intensity_threshold=3000)
|
|
@@ -558,15 +634,22 @@ class MyTransformModuleLogic(ScriptedLoadableModuleLogic):
|
|
|
|
|
|
if cbct_list and ct_list:
|
|
if cbct_list and ct_list:
|
|
ct_volume_name = ct_list[0]
|
|
ct_volume_name = ct_list[0]
|
|
|
|
+ print("\nProcessing CT")
|
|
|
|
+ yesCbct = False
|
|
|
|
+ find_table_top_z(ct_volume_name, writefilecheck, yesCbct)
|
|
|
|
+
|
|
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)]]
|
|
|
|
|
|
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.")
|
|
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)]]
|
|
cbct_points = [centroid for centroid, _ in volume_points_dict[("CBCT", cbct_volume_name)]]
|
|
|
|
+
|
|
|
|
+ find_table_top_z(cbct_volume_name, writefilecheck, yesCbct)
|
|
|
|
|
|
- print(f"\nProcessing CBCT Volume: {cbct_volume_name}")
|
|
|
|
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.")
|
|
continue
|
|
continue
|