def extract_organ_3D_bounding_box(nii_file):
"""
주어진 .nii 파일에서 장기의 3D bounding box (min, max 좌표)를 추출
Args:
- nii_file (str): .nii 파일의 경로.
Returns:
- dict: 장기 이름을 키로 하고 해당하는 min, max 3D 좌표를 값
"""
# .nii 파일 불러오기
nii_image = sitk.ReadImage(nii_file)
data = sitk.GetArrayFromImage(nii_image)
# 각 장기에 대한 bounding box 좌표를 저장할 사전
organ_bounding_boxes = {}
organ_labels = {
'liver': [5],
'spleen': [1],
'kidney': [2, 3],
'bowel': [55, 57, 56] # small_bowel, colon, duodenum
}
# 각 장기의 bounding box 좌표 추출
for organ, labels in organ_labels.items():
coords = [] # 좌표 저장할 리스트
for label in labels:
coords.extend(np.argwhere(data == label).tolist())
if not coords:
organ_bounding_boxes[organ] = None
continue
coords = np.array(coords)
z_min, y_min, x_min = coords.min(axis=0)
z_max, y_max, x_max = coords.max(axis=0)
# 각 차원의 중심을 계산
z_center, y_center, x_center = (z_min + z_max) // 2, (y_min + y_max) // 2, (x_min + x_max) // 2
# scale_facor 지정
scale_factor = 1.2
# 각 차원의 절반 길이를 계산
z_half, y_half, x_half = (z_max - z_min) * scale_factor / 2, (y_max - y_min) * scale_factor / 2, (x_max - x_min) * scale_factor / 2
# 중심을 기준으로 bounding box 재조정
z_min, z_max = int(z_center - z_half), int(z_center + z_half)
y_min, y_max = int(y_center - y_half), int(y_center + y_half)
x_min, x_max = int(x_center - x_half), int(x_center + x_half)
organ_bounding_boxes[organ] = {
'x': (x_min, x_max),
'y': (y_min, y_max),
'z': (z_min, z_max)
}
return organ_bounding_boxes
def save_bounding_boxes_to_csv(nii_directory, csv_directory, csv_filename="bounding_boxes.csv"):
"""
주어진 디렉터리의 모든 .nii 파일에서 3D bounding box를 추출하고
결과를 지정된 CSV 파일에 저장합니다.
Args:
- nii_directory (str): .nii 파일들이 있는 디렉터리 경로.
- csv_directory (str): 결과를 저장할 CSV 파일의 디렉터리 경로.
- csv_filename (str): 생성될 CSV 파일의 이름. 기본값은 "bounding_boxes.csv".
"""
# CSV 파일 경로 생성
csv_file_path = os.path.join(csv_directory, csv_filename)
# .nii 파일 리스트 가져오기
nii_files = [f for f in os.listdir(nii_directory) if f.endswith('.nii')]
# series_id를 기준으로 정렬
nii_files = sorted(nii_files, key=lambda x: int(x.split('.')[0]))
with open(csv_file_path, 'w', newline='') as csvfile:
csv_writer = csv.writer(csvfile)
# CSV 헤더 작성
csv_writer.writerow(['patient_id', 'series_id', 'organ', 'x_min', 'x_max', 'y_min', 'y_max', 'z_min', 'z_max'])
# 각 .nii 파일에 대해 처리
for nii_file in tqdm(nii_files, desc="Processing NII files"):
nii_file_path = os.path.join(nii_directory, nii_file)
patient_id, series_id = nii_file.split('_')[0], nii_file.split('_')[1].split('.')[0]
bounding_boxes = extract_organ_3D_bounding_box(nii_file_path)
for organ, bbox in bounding_boxes.items():
if bbox:
csv_writer.writerow([patient_id, series_id, organ, bbox['x'][0], bbox['x'][1], bbox['y'][0], bbox['y'][1], bbox['z'][0], bbox['z'][1]])
( 티스토리 참고 ; https://bo-10000.tistory.com/61 )
( method 참고 ; https://simpleitk.org/SimpleITK-Notebooks/01_Image_Basics.html )
nda = sitk.GetArrayFromImage(image_RGB)
img = sitk.GetImageFromArray(nda)
*numpy array & sitk.Image 객체 상호 변환 가능
< 아래 속성들에 접근 가능 >
image.GetSize()
GetSize()는 이러한 영상의 각 차원을 통해 픽셀(2D) 또는 복셀(3D)의 전체 개수를 알려줍니다. 예를 들어, (512, 512)는 512x512 픽셀의 2D 영상을, (512, 512, 150)는 각각 512x512 픽셀 크기의 150개 슬라이스를 가진 3D 영상을 의미합니다.image.GetOrigin()
(0.0, 0.0, 0.0)은 가장 기본적인 원점 위치를, (-250.0, -250.0, -180.0)은 x, y, z 축에서 각각 -250mm, -250mm, -180mm에 해당하는 원점 위치를 의미할 수 있습니다.image.GetSpacing()
(0.5, 0.5)는 각 픽셀이 0.5mm x 0.5mm의 크기를 가짐을, (0.5, 0.5, 1.0)는 3D 영상에서 각 복셀이 0.5mm x 0.5mm x 1.0mm 크기임을 의미합니다.image.GetDirection()
(1, 0, 0, 0, 1, 0, 0, 0, 1)은 영상이 공간 상에서 표준 방향(축에 정렬)으로 배치되어 있음을, 비틀어진 방향을 가진 경우는 다른 값들을 포함할 수 있습니다.image.GetNumberOfComponentsPerPixel()
for key in image.GetMetaDataKeys():
print "\"{0}\":\"{1}\"".format(key, image.GetMetaData(key))
(x,y,z)(z,y,x)print img.GetSize() # (128, 128)
print nda.shape # (128, 128, 3)
print nda.shape[::-1] # (3, 128, 128)