Organ-aware 3D lesion segmentation dataset and pipeline for abdominal CT analysis (ACM Multimedia 2025 candidate)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lesion_report.py 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import os
  2. import pandas as pd
  3. import cv2
  4. import numpy as np
  5. from tqdm import tqdm
  6. SEG_ROOT = "MONAI"
  7. DL_INFO_PATH = ""
  8. OUTPUT_PATH = ""
  9. DEBUG_DIR = ""
  10. os.makedirs(DEBUG_DIR, exist_ok=True)
  11. TARGET_ORGANS = ["liver", "spleen", "kidney_right", "kidney_left", "gallbladder", "stomach", "pancreas"]
  12. OVERLAP_THRESHOLD = 0.1
  13. df = pd.read_csv(DL_INFO_PATH)
  14. results = []
  15. for idx, row in tqdm(df.iterrows(), total=len(df)):
  16. try:
  17. filename = row["File_name"]
  18. patient_id, study_id, series_id = filename.split("_")[:3]
  19. series_name = f"{patient_id}_{study_id}_{series_id}"
  20. series_path = os.path.join(SEG_ROOT, series_name)
  21. if not os.path.exists(series_path):
  22. continue
  23. bbox = np.array(row["Bounding_boxes"].strip("[]").split(","), dtype=float).astype(int).tolist()
  24. bbox_x1, bbox_y1, bbox_x2, bbox_y2 = bbox
  25. lesion_area = (bbox_x2 - bbox_x1) * (bbox_y2 - bbox_y1)
  26. if lesion_area == 0:
  27. continue
  28. slice_start, slice_end = map(int, str(row["Slice_range"]).strip().split(","))
  29. key_slice = int(row["Key_slice_index"])
  30. matched_organs = set()
  31. found_overlap = False
  32. overlap_slices = range(max(key_slice - 2, slice_start), min(key_slice + 2, slice_end) + 1)
  33. for organ in TARGET_ORGANS:
  34. monai_organ = f"MONAI_{organ}"
  35. for slice_idx in overlap_slices:
  36. slice_filename = f"{slice_idx:03}_OUT.png"
  37. mask_path = os.path.join(series_path, monai_organ, slice_filename)
  38. if not os.path.exists(mask_path):
  39. continue
  40. mask_gray = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE)
  41. if mask_gray is None or mask_gray.shape[0] == 0:
  42. continue
  43. mask_gray = cv2.rotate(mask_gray, cv2.ROTATE_90_COUNTERCLOCKWISE)
  44. mask_gray = cv2.flip(mask_gray, 0)
  45. mask_crop = mask_gray[bbox_y1:bbox_y2, bbox_x1:bbox_x2]
  46. overlap_area = np.sum(mask_crop > 0)
  47. overlap_ratio = overlap_area / lesion_area
  48. if overlap_ratio > OVERLAP_THRESHOLD:
  49. matched_organs.add(organ)
  50. found_overlap = True
  51. break
  52. if not found_overlap:
  53. proximity_distances = []
  54. all_distances = []
  55. for organ in TARGET_ORGANS:
  56. monai_organ = f"MONAI_{organ}"
  57. slice_filename = f"{key_slice:03}_OUT.png"
  58. mask_path = os.path.join(series_path, monai_organ, slice_filename)
  59. if not os.path.exists(mask_path):
  60. continue
  61. mask_gray = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE)
  62. if mask_gray is None or np.sum(mask_gray > 0) == 0:
  63. continue
  64. mask_gray = cv2.rotate(mask_gray, cv2.ROTATE_90_COUNTERCLOCKWISE)
  65. mask_gray = cv2.flip(mask_gray, 0)
  66. mask_bin = (mask_gray > 0).astype(np.uint8)
  67. dist_transform = cv2.distanceTransform(255 - mask_bin * 255, cv2.DIST_L2, 5)
  68. lesion_mask = np.zeros_like(mask_bin)
  69. lesion_mask[bbox_y1:bbox_y2, bbox_x1:bbox_x2] = 1
  70. lesion_dist = dist_transform * lesion_mask
  71. if np.any(lesion_dist > 0):
  72. min_dist = lesion_dist[lesion_dist > 0].min()
  73. all_distances.append((organ, min_dist))
  74. if min_dist <= 10:
  75. matched_organs.add(organ)
  76. found_overlap = True
  77. proximity_distances.clear()
  78. if min_dist >10 and min_dist <= 20 and not found_overlap :
  79. proximity_distances.append((organ, min_dist))
  80. for organ, _ in proximity_distances:
  81. matched_organs.add(organ + "_prox")
  82. results.append({
  83. "series": series_name,
  84. "slice_range": f"{slice_start}~{slice_end}",
  85. "key_slice": key_slice,
  86. "lesion_id": idx,
  87. "matched_organs": ";".join(sorted(matched_organs)) if matched_organs else "none"
  88. })
  89. except Exception as e:
  90. print(f"⚠️ error {idx}: {e}")
  91. continue
  92. out_df = pd.DataFrame(results)
  93. out_df.to_csv(OUTPUT_PATH, index=False)