開春版本
This commit is contained in:
parent
7cfd15c6c3
commit
8802d67cda
1 changed files with 263 additions and 168 deletions
431
onlylian/oar4.py
431
onlylian/oar4.py
|
@ -125,6 +125,9 @@ LABEL_MAPPING: Dict[int, str] = {
|
||||||
5: "Right_Optic_Nerve",
|
5: "Right_Optic_Nerve",
|
||||||
6: "Left_Optic_Nerve"
|
6: "Left_Optic_Nerve"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# MAX_OAR = max(LABEL_MAPPING.keys())
|
||||||
|
max_existing_label = max(LABEL_MAPPING.keys())
|
||||||
|
|
||||||
# 設定日誌
|
# 設定日誌
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
|
@ -346,7 +349,7 @@ def process_multiple_tumors(image_array):
|
||||||
return result_img
|
return result_img
|
||||||
|
|
||||||
# 為每個腫瘤生成唯一的標籤值,從最大現有標籤值開始
|
# 為每個腫瘤生成唯一的標籤值,從最大現有標籤值開始
|
||||||
max_existing_label = max(LABEL_MAPPING.keys())
|
# max_existing_label = max(LABEL_MAPPING.keys())
|
||||||
tumor_colors = generate_distinct_colors(num_tumors)
|
tumor_colors = generate_distinct_colors(num_tumors)
|
||||||
|
|
||||||
# 更新標籤映射和顏色映射
|
# 更新標籤映射和顏色映射
|
||||||
|
@ -374,37 +377,49 @@ def create_structure_masks(image: sitk.Image) -> Dict[str, np.ndarray]:
|
||||||
|
|
||||||
return masks
|
return masks
|
||||||
|
|
||||||
def register(ct_fixed, moving, out_moving):
|
MODELS = (
|
||||||
|
'rigid',
|
||||||
|
'affine',
|
||||||
|
'joint',
|
||||||
|
)
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
def register(ct_fixed, moving):
|
||||||
fixed = os.path.join(tmp, 'clipped.nii.gz')
|
|
||||||
|
out_dir = tempfile.mkdtemp(dir=Path(__file__).resolve().parent/'0')
|
||||||
ct = sf.load_volume(ct_fixed)
|
|
||||||
clipped = ct.clip(0, 80)
|
|
||||||
clipped.save(fixed)
|
|
||||||
|
|
||||||
FREESURFER_HOME = str(Path(__file__).resolve().parent/'mri_synthmorph')
|
fixed = os.path.join(out_dir, 'clipped.nii.gz')
|
||||||
|
|
||||||
|
ct = sf.load_volume(ct_fixed)
|
||||||
|
clipped = ct.clip(0, 80)
|
||||||
|
clipped.save(fixed)
|
||||||
|
|
||||||
|
FREESURFER_HOME = Path(__file__).resolve().parent/'mri_synthmorph'
|
||||||
|
my_env = os.environ.copy()
|
||||||
|
my_env["FREESURFER_HOME"] = str(FREESURFER_HOME)
|
||||||
|
|
||||||
|
mri_synthmorph = str(FREESURFER_HOME/'mri_synthmorph')
|
||||||
|
|
||||||
|
for m in MODELS:
|
||||||
|
|
||||||
|
transform_extension = 'lta' if m in ('affine','rigid') else 'nii.gz'
|
||||||
|
|
||||||
args = [
|
args = [
|
||||||
str(Path(__file__).resolve().parent/'mri_synthmorph/mri_synthmorph'),
|
mri_synthmorph,
|
||||||
# 'register',
|
'-m', m,
|
||||||
'-m', 'affine',
|
'-o', os.path.join(out_dir, '%s.nii.gz'%m),
|
||||||
# '-m', 'rigid',
|
|
||||||
'-o', out_moving,
|
'-O', os.path.join(out_dir, 'out_fixed-%s.nii.gz'%m),
|
||||||
|
'-t', os.path.join(out_dir, 'moving_to_fixed-%s.%s'% (m, transform_extension)),
|
||||||
|
'-T', os.path.join(out_dir, 'fixed_to_moving-%s.%s'% (m, transform_extension)),
|
||||||
|
|
||||||
'-g',
|
'-g',
|
||||||
moving,
|
moving,
|
||||||
fixed,
|
fixed,
|
||||||
]
|
]
|
||||||
|
logger.info(f" 執行registration 指令:{' '.join(args)}")
|
||||||
|
result = subprocess.run(args, env=my_env)
|
||||||
|
|
||||||
my_env = os.environ.copy()
|
return out_dir
|
||||||
my_env["FREESURFER_HOME"] = str(Path(__file__).resolve().parent/'mri_synthmorph')
|
|
||||||
|
|
||||||
|
|
||||||
# logger.info(f"執行推論指令:{' '.join(predict_cmd)}")
|
|
||||||
result = subprocess.run(
|
|
||||||
args,
|
|
||||||
env=my_env,
|
|
||||||
)
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Namespace(command='register', extent=256, fixed='0/clipped.nii.gz', gpu=True, header_only=False, hyper=0.5, init=None, inverse=None, mid_space=False, model='affine', moving='0/t1c.nii.gz', out_dir=None, out_fixed=None, out_moving='0/out-aff.nii.gz', steps=7, threads=None, trans='0/trans.lta', verbose=False, weights=None)
|
Namespace(command='register', extent=256, fixed='0/clipped.nii.gz', gpu=True, header_only=False, hyper=0.5, init=None, inverse=None, mid_space=False, model='affine', moving='0/t1c.nii.gz', out_dir=None, out_fixed=None, out_moving='0/out-aff.nii.gz', steps=7, threads=None, trans='0/trans.lta', verbose=False, weights=None)
|
||||||
|
@ -455,8 +470,8 @@ def register_synthmorph(ct_fixed, moving, out_moving):
|
||||||
|
|
||||||
arg=argparse.Namespace(**default)
|
arg=argparse.Namespace(**default)
|
||||||
FREESURFER_HOME = str(Path(__file__).resolve().parent/'mri_synthmorph')
|
FREESURFER_HOME = str(Path(__file__).resolve().parent/'mri_synthmorph')
|
||||||
print(arg)
|
# print(arg)
|
||||||
print(FREESURFER_HOME)
|
# print(FREESURFER_HOME)
|
||||||
|
|
||||||
os.environ["FREESURFER_HOME"] = str(Path(__file__).resolve().parent/'mri_synthmorph')
|
os.environ["FREESURFER_HOME"] = str(Path(__file__).resolve().parent/'mri_synthmorph')
|
||||||
registration.register(arg)
|
registration.register(arg)
|
||||||
|
@ -476,11 +491,7 @@ def inference(DCM_CT, DCM_MR):
|
||||||
ROOT_DIR = os.path.dirname(os.path.dirname(DCM_CT))
|
ROOT_DIR = os.path.dirname(os.path.dirname(DCM_CT))
|
||||||
NII_DIR = os.path.join(ROOT_DIR, 'nii')
|
NII_DIR = os.path.join(ROOT_DIR, 'nii')
|
||||||
INPUT_DIR = os.path.join(ROOT_DIR, 'input')
|
INPUT_DIR = os.path.join(ROOT_DIR, 'input')
|
||||||
OUTPUT_DIR = os.path.join(ROOT_DIR, 'output')
|
OUTPUT_DIR = os.path.join(ROOT_DIR, 'output')
|
||||||
|
|
||||||
# 設定 RTSTRUCT 輸出路徑
|
|
||||||
head, tail = os.path.split(DCM_CT)
|
|
||||||
rtss_file = os.path.join(head, tail+'-rtss.dcm')
|
|
||||||
|
|
||||||
# 清理並建立工作目錄
|
# 清理並建立工作目錄
|
||||||
for dir_path in [NII_DIR, INPUT_DIR, OUTPUT_DIR]:
|
for dir_path in [NII_DIR, INPUT_DIR, OUTPUT_DIR]:
|
||||||
|
@ -519,26 +530,26 @@ def inference(DCM_CT, DCM_MR):
|
||||||
raise FileNotFoundError(f"找不到 CT 的 NIfTI 檔案,目錄內容:{os.listdir(NII_DIR)}")
|
raise FileNotFoundError(f"找不到 CT 的 NIfTI 檔案,目錄內容:{os.listdir(NII_DIR)}")
|
||||||
if not NII_MR:
|
if not NII_MR:
|
||||||
raise FileNotFoundError(f"找不到 MR 的 NIfTI 檔案,目錄內容:{os.listdir(NII_DIR)}")
|
raise FileNotFoundError(f"找不到 MR 的 NIfTI 檔案,目錄內容:{os.listdir(NII_DIR)}")
|
||||||
|
|
||||||
|
|
||||||
|
logger.info("Registration of %s and %s..."%(NII_CT, NII_MR))
|
||||||
|
out_dir = register(NII_CT, NII_MR)
|
||||||
|
|
||||||
# 準備輸入檔案
|
# 準備輸入檔案
|
||||||
|
|
||||||
basename = os.path.basename(NII_MR)
|
basename = os.path.basename(NII_MR)
|
||||||
|
|
||||||
old = '_'+basename.split('_')[-1]
|
old = '_'+basename.split('_')[-1]
|
||||||
|
|
||||||
input_ct = os.path.join(INPUT_DIR, basename.replace(old, '_0000.nii.gz'))
|
for m in MODELS:
|
||||||
shutil.copy(NII_CT, input_ct)
|
input_ct = os.path.join(INPUT_DIR, basename.replace(old, '_%s_0000.nii.gz'%m))
|
||||||
|
shutil.copy(NII_CT, input_ct)
|
||||||
|
|
||||||
input_mr = os.path.join(INPUT_DIR, basename.replace(old, '_0001.nii.gz'))
|
input_mr = os.path.join(INPUT_DIR, basename.replace(old, '_%s_0001.nii.gz'%m))
|
||||||
|
shutil.copy(os.path.join(out_dir, '%s.nii.gz'%m), input_mr)
|
||||||
logger.info("Registration of %s and %s..."%(NII_CT, NII_MR))
|
|
||||||
register(NII_CT, NII_MR, input_mr)
|
# shutil.rmtree(out_dir)
|
||||||
|
|
||||||
output_file = os.path.join(OUTPUT_DIR, basename.replace(old, '.nii.gz'))
|
|
||||||
|
|
||||||
basename_ct = os.path.basename(NII_CT)
|
|
||||||
old_ct = '_'+basename_ct.split('_')[-1]
|
|
||||||
label_file = os.path.join(NII_DIR, basename_ct.replace(old_ct, '.label.nii.gz'))
|
|
||||||
|
|
||||||
# basename = os.path.basename(NII_MR)
|
# basename = os.path.basename(NII_MR)
|
||||||
# old = '_'+basename.split('_')[-1]
|
# old = '_'+basename.split('_')[-1]
|
||||||
|
@ -597,129 +608,133 @@ def inference(DCM_CT, DCM_MR):
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
logger.error(f"模型推論失敗:\n輸出:{e.output}\n錯誤:{e.stderr}")
|
logger.error(f"模型推論失敗:\n輸出:{e.output}\n錯誤:{e.stderr}")
|
||||||
raise RuntimeError(f"模型推論失敗:{e.stderr}")
|
raise RuntimeError(f"模型推論失敗:{e.stderr}")
|
||||||
|
|
||||||
if not os.path.exists(output_file):
|
|
||||||
raise FileNotFoundError(f"找不到模型輸出檔案:{output_file}")
|
|
||||||
|
|
||||||
logger.info("開始執行影像後處理...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 讀取預測結果
|
|
||||||
predicted_image = sitk.ReadImage(output_file)
|
|
||||||
predicted_image = sitk.DICOMOrient(predicted_image)
|
|
||||||
predicted_array = sitk.GetArrayFromImage(predicted_image)
|
|
||||||
|
|
||||||
# 執行後處理
|
rtss_combination = []
|
||||||
processed_array = process_symmetric_structures(predicted_array)
|
|
||||||
processed_array = process_multiple_tumors(processed_array)
|
|
||||||
|
|
||||||
processed_image = sitk.GetImageFromArray(processed_array)
|
|
||||||
processed_image.CopyInformation(predicted_image)
|
|
||||||
|
|
||||||
# 暫時保存後處理結果
|
|
||||||
processed_output = os.path.join(OUTPUT_DIR, 'processed_' + os.path.basename(output_file))
|
|
||||||
sitk.WriteImage(processed_image, processed_output)
|
|
||||||
logger.info(f"後處理結果已保存至:{processed_output}")
|
|
||||||
|
|
||||||
logger.info("開始執行影像配準...")
|
|
||||||
# reg_transform(NII_CT, NII_MR, processed_output, label_file)
|
|
||||||
shutil.copy(processed_output, label_file)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"影像處理失敗:{str(e)}")
|
|
||||||
raise RuntimeError("後處理或配準失敗")
|
|
||||||
|
|
||||||
if not os.path.exists(label_file):
|
|
||||||
raise FileNotFoundError(f"找不到配準後的標籤檔案:{label_file}")
|
|
||||||
|
|
||||||
logger.info("開始建立 RTSTRUCT...")
|
|
||||||
|
|
||||||
# 讀取 CT 影像序列
|
|
||||||
try:
|
|
||||||
reader = sitk.ImageSeriesReader()
|
|
||||||
dicom_names = reader.GetGDCMSeriesFileNames(DCM_CT)
|
|
||||||
reader.SetFileNames(dicom_names)
|
|
||||||
reader.MetaDataDictionaryArrayUpdateOn()
|
|
||||||
reader.LoadPrivateTagsOn()
|
|
||||||
image = reader.Execute()
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"讀取 CT 系列失敗:{str(e)}")
|
|
||||||
raise RuntimeError("無法讀取 CT 影像系列")
|
|
||||||
|
|
||||||
# 讀取並重新取樣配準後的預測結果
|
|
||||||
try:
|
|
||||||
final_image = sitk.ReadImage(label_file)
|
|
||||||
final_image = sitk.Resample(
|
|
||||||
final_image,
|
|
||||||
image,
|
|
||||||
sitk.Transform(),
|
|
||||||
sitk.sitkNearestNeighbor
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"重新取樣失敗:{str(e)}")
|
|
||||||
raise RuntimeError("預測結果重新取樣失敗")
|
|
||||||
|
|
||||||
# 建立 RTSTRUCT
|
|
||||||
try:
|
|
||||||
|
|
||||||
images = []
|
for m in MODELS:
|
||||||
images.append(IntensityImage(image, Modality('CT')))
|
|
||||||
# images.append(IntensityImage(sitk.ReadImage(input_mr), Modality('MR')))
|
basename_ct = os.path.basename(NII_CT)
|
||||||
|
old_ct = '_'+basename_ct.split('_')[-1]
|
||||||
|
label_file = os.path.join(NII_DIR, basename_ct.replace(old_ct, '_%s.label.nii.gz'%m))
|
||||||
|
|
||||||
|
output_file = os.path.join(OUTPUT_DIR, basename.replace(old, '_%s.nii.gz'%m))
|
||||||
|
|
||||||
|
if not os.path.exists(output_file):
|
||||||
|
raise FileNotFoundError(f"找不到模型輸出檔案:{output_file}")
|
||||||
|
|
||||||
|
logger.info("開始執行影像後處理...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 讀取預測結果
|
||||||
|
predicted_image = sitk.ReadImage(output_file)
|
||||||
|
predicted_image = sitk.DICOMOrient(predicted_image)
|
||||||
|
predicted_array = sitk.GetArrayFromImage(predicted_image)
|
||||||
|
|
||||||
|
# 執行後處理
|
||||||
|
processed_array = process_symmetric_structures(predicted_array)
|
||||||
|
processed_array = process_multiple_tumors(processed_array)
|
||||||
|
|
||||||
|
processed_image = sitk.GetImageFromArray(processed_array)
|
||||||
|
processed_image.CopyInformation(predicted_image)
|
||||||
|
|
||||||
|
# 暫時保存後處理結果
|
||||||
|
processed_output = os.path.join(OUTPUT_DIR, 'processed_' + os.path.basename(output_file))
|
||||||
|
sitk.WriteImage(processed_image, processed_output)
|
||||||
|
logger.info(f"後處理結果已保存至:{processed_output}")
|
||||||
|
|
||||||
|
logger.info("開始執行影像配準...")
|
||||||
|
# reg_transform(NII_CT, NII_MR, processed_output, label_file)
|
||||||
|
shutil.copy(processed_output, label_file)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"影像處理失敗:{str(e)}")
|
||||||
|
raise RuntimeError("後處理或配準失敗")
|
||||||
|
|
||||||
|
if not os.path.exists(label_file):
|
||||||
|
raise FileNotFoundError(f"找不到配準後的標籤檔案:{label_file}")
|
||||||
|
|
||||||
|
logger.info("開始建立 RTSTRUCT...")
|
||||||
|
|
||||||
|
# 讀取 CT 影像序列
|
||||||
|
try:
|
||||||
|
reader = sitk.ImageSeriesReader()
|
||||||
|
dicom_names = reader.GetGDCMSeriesFileNames(DCM_CT)
|
||||||
|
reader.SetFileNames(dicom_names)
|
||||||
|
reader.MetaDataDictionaryArrayUpdateOn()
|
||||||
|
reader.LoadPrivateTagsOn()
|
||||||
|
image = reader.Execute()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"讀取 CT 系列失敗:{str(e)}")
|
||||||
|
raise RuntimeError("無法讀取 CT 影像系列")
|
||||||
|
|
||||||
|
# 讀取並重新取樣配準後的預測結果
|
||||||
|
try:
|
||||||
|
final_image = sitk.ReadImage(label_file)
|
||||||
|
final_image = sitk.Resample(
|
||||||
|
final_image,
|
||||||
|
image,
|
||||||
|
sitk.Transform(),
|
||||||
|
sitk.sitkNearestNeighbor
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"重新取樣失敗:{str(e)}")
|
||||||
|
raise RuntimeError("預測結果重新取樣失敗")
|
||||||
|
|
||||||
|
# 建立 RTSTRUCT
|
||||||
|
try:
|
||||||
|
|
||||||
|
images = []
|
||||||
|
images.append(IntensityImage(image, Modality('CT')))
|
||||||
|
# images.append(IntensityImage(sitk.ReadImage(input_mr), Modality('MR')))
|
||||||
|
|
||||||
|
for k, v in LABEL_MAPPING.items():
|
||||||
|
images.append(SegmentationImage(final_image==k, v))
|
||||||
|
|
||||||
|
dcm_crawler = ps_io.SubjectDicomCrawler(DCM_CT,
|
||||||
|
modality_extractor=ExampleModalityExtractor(),
|
||||||
|
)
|
||||||
|
dicom_series_info = dcm_crawler.execute()
|
||||||
|
|
||||||
|
subject = Subject(dicom_series_info[0].get_patient_id(), images)
|
||||||
|
|
||||||
|
reference_modality = 'CT'
|
||||||
|
|
||||||
|
use_3d_conversion = True
|
||||||
|
if use_3d_conversion:
|
||||||
|
conv_conf = ps_io.RTSSConverter3DConfiguration()
|
||||||
|
else:
|
||||||
|
conv_conf = ps_io.RTSSConverter2DConfiguration()
|
||||||
|
meta_data = ps_io.RTSSMetaData(
|
||||||
|
series_description = '%s by %s'%(m, type(conv_conf).__name__)
|
||||||
|
)
|
||||||
|
|
||||||
|
converter = ps_io.SubjectToRTSSConverter(
|
||||||
|
subject,
|
||||||
|
dicom_series_info,
|
||||||
|
reference_modality,
|
||||||
|
conv_conf,
|
||||||
|
meta_data,
|
||||||
|
# colors = tuple(LABEL_COLORS.values()),
|
||||||
|
)
|
||||||
|
|
||||||
|
rtss = converter.convert()
|
||||||
|
|
||||||
|
# 設定 RTSTRUCT 輸出路徑
|
||||||
|
head, tail = os.path.split(DCM_CT)
|
||||||
|
rtss_file = os.path.join(head, tail+'_%s-rtss.dcm'%m)
|
||||||
|
|
||||||
|
# Write the DICOM-RTSS to a separate subject directory
|
||||||
|
# and include the DICOM files crawled before
|
||||||
|
# Note: If you want to output just a subset of the
|
||||||
|
# original DICOM files you may use additional selectors
|
||||||
|
|
||||||
|
output_dir_path = os.path.join(ROOT_DIR, 'pyradise')
|
||||||
|
rtss_filename = os.path.basename(rtss_file)
|
||||||
|
|
||||||
for k, v in LABEL_MAPPING.items():
|
rtss_combination.append((rtss_filename, rtss))
|
||||||
images.append(SegmentationImage(final_image==k, v))
|
|
||||||
|
|
||||||
dcm_crawler = ps_io.SubjectDicomCrawler(DCM_CT,
|
|
||||||
modality_extractor=ExampleModalityExtractor(),
|
# rtss_combination = ((rtss_filename, rtss),)
|
||||||
)
|
|
||||||
dicom_series_info = dcm_crawler.execute()
|
|
||||||
|
|
||||||
subject = Subject(dicom_series_info[0].get_patient_id(), images)
|
|
||||||
|
|
||||||
reference_modality = 'CT'
|
|
||||||
|
|
||||||
use_3d_conversion = True
|
|
||||||
if use_3d_conversion:
|
|
||||||
conv_conf = ps_io.RTSSConverter3DConfiguration()
|
|
||||||
else:
|
|
||||||
conv_conf = ps_io.RTSSConverter2DConfiguration()
|
|
||||||
meta_data = ps_io.RTSSMetaData(
|
|
||||||
series_description = '%s by onlylian'%type(conv_conf).__name__)
|
|
||||||
|
|
||||||
converter = ps_io.SubjectToRTSSConverter(
|
|
||||||
subject,
|
|
||||||
dicom_series_info,
|
|
||||||
reference_modality,
|
|
||||||
conv_conf,
|
|
||||||
meta_data,
|
|
||||||
# colors = tuple(LABEL_COLORS.values()),
|
|
||||||
)
|
|
||||||
|
|
||||||
rtss = converter.convert()
|
|
||||||
|
|
||||||
|
|
||||||
# Write the DICOM-RTSS to a separate subject directory
|
|
||||||
# and include the DICOM files crawled before
|
|
||||||
# Note: If you want to output just a subset of the
|
|
||||||
# original DICOM files you may use additional selectors
|
|
||||||
|
|
||||||
output_dir_path = os.path.join(ROOT_DIR, 'pyradise')
|
|
||||||
rtss_filename = os.path.basename(rtss_file)
|
|
||||||
rtss_combination = ((rtss_filename, rtss),)
|
|
||||||
print(output_dir_path)
|
|
||||||
print(rtss_combination)
|
|
||||||
|
|
||||||
# shutil.rmtree(output_dir_path, ignore_errors=True)
|
|
||||||
os.makedirs(output_dir_path, exist_ok=True)
|
|
||||||
|
|
||||||
writer = ps_io.DicomSeriesSubjectWriter()
|
|
||||||
writer.write(rtss_combination,
|
|
||||||
output_dir_path,
|
|
||||||
# subject.get_name(),
|
|
||||||
None,
|
|
||||||
dicom_series_info,)
|
|
||||||
|
|
||||||
shutil.copy(os.path.join(output_dir_path, rtss_filename), rtss_file)
|
|
||||||
|
|
||||||
# rtstruct = RTStructBuilder.create_new(dicom_series_path=DCM_CT)
|
# rtstruct = RTStructBuilder.create_new(dicom_series_path=DCM_CT)
|
||||||
|
|
||||||
|
@ -748,16 +763,30 @@ def inference(DCM_CT, DCM_MR):
|
||||||
# logger.info(f"儲存 RTSTRUCT:{rtss_file}")
|
# logger.info(f"儲存 RTSTRUCT:{rtss_file}")
|
||||||
# rtstruct.save(rtss_file)
|
# rtstruct.save(rtss_file)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"RTSTRUCT 生成失敗:{str(e)}")
|
logger.error(f"RTSTRUCT 生成失敗:{str(e)}")
|
||||||
raise RuntimeError("無法生成或儲存 RTSTRUCT")
|
raise RuntimeError("無法生成或儲存 RTSTRUCT")
|
||||||
|
|
||||||
return rtss_file
|
# print(output_dir_path)
|
||||||
|
# print(rtss_combination)
|
||||||
|
|
||||||
|
# shutil.rmtree(output_dir_path, ignore_errors=True)
|
||||||
|
os.makedirs(output_dir_path, exist_ok=True)
|
||||||
|
|
||||||
|
writer = ps_io.DicomSeriesSubjectWriter()
|
||||||
|
writer.write(rtss_combination,
|
||||||
|
output_dir_path,
|
||||||
|
# subject.get_name(),
|
||||||
|
None,
|
||||||
|
dicom_series_info,)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"推論過程發生錯誤:{str(e)}", exc_info=True)
|
logger.error(f"推論過程發生錯誤:{str(e)}", exc_info=True)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
return rtss_combination, output_dir_path
|
||||||
|
|
||||||
|
|
||||||
def SendDCM(fp):
|
def SendDCM(fp):
|
||||||
"""傳送 DICOM 檔案到 PACS"""
|
"""傳送 DICOM 檔案到 PACS"""
|
||||||
logger.info(f"準備傳送 DICOM 檔案:{fp}")
|
logger.info(f"準備傳送 DICOM 檔案:{fp}")
|
||||||
|
@ -803,6 +832,62 @@ def SendDCM(fp):
|
||||||
logger.error(f"DICOM 傳送過程發生錯誤:{str(e)}", exc_info=True)
|
logger.error(f"DICOM 傳送過程發生錯誤:{str(e)}", exc_info=True)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def SendDCMs(filelist):
|
||||||
|
"""傳送 DICOM 檔案到 PACS"""
|
||||||
|
logger.info(f"準備傳送 DICOM 檔案:{' '.join(filelist)}")
|
||||||
|
debug_logger()
|
||||||
|
|
||||||
|
dslist = []
|
||||||
|
|
||||||
|
for fp in filelist:
|
||||||
|
|
||||||
|
if not os.path.exists(fp):
|
||||||
|
raise FileNotFoundError(f"找不到 DICOM 檔案:{fp}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
ds = dcmread(fp)
|
||||||
|
dslist.append(ds)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"讀取 DICOM 檔案失敗:{str(e)}")
|
||||||
|
raise RuntimeError("無法讀取 DICOM 檔案")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
ae = AE()
|
||||||
|
ae.ae_title = 'OUR_STORE_SCP'
|
||||||
|
ae.add_requested_context(RTStructureSetStorage)
|
||||||
|
|
||||||
|
|
||||||
|
logger.info("正在連接 PACS 伺服器...")
|
||||||
|
try:
|
||||||
|
assoc = ae.associate("172.16.40.36", 104, ae_title='N1000_STORAGE')
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"PACS 連接失敗:{str(e)}")
|
||||||
|
raise RuntimeError("無法連接 PACS 伺服器")
|
||||||
|
|
||||||
|
if assoc.is_established:
|
||||||
|
try:
|
||||||
|
for ds in dslist:
|
||||||
|
status = assoc.send_c_store(ds)
|
||||||
|
if status:
|
||||||
|
logger.info(f'DICOM 傳送成功,狀態碼: 0x{status.Status:04x}')
|
||||||
|
else:
|
||||||
|
raise RuntimeError('傳送失敗:連接逾時或收到無效回應')
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"DICOM 傳送失敗:{str(e)}")
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
assoc.release()
|
||||||
|
else:
|
||||||
|
raise RuntimeError('傳送失敗:無法建立連接')
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"DICOM 傳送過程發生錯誤:{str(e)}", exc_info=True)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""主程式進入點"""
|
"""主程式進入點"""
|
||||||
if len(sys.argv) < 3:
|
if len(sys.argv) < 3:
|
||||||
|
@ -817,8 +902,18 @@ def main():
|
||||||
|
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
try:
|
try:
|
||||||
rtss_file = inference(sys.argv[1], sys.argv[2])
|
|
||||||
SendDCM(rtss_file)
|
rtss_combination, output_dir_path = inference(sys.argv[1], sys.argv[2])
|
||||||
|
|
||||||
|
RTSS = []
|
||||||
|
for rtss_filename, rtss in rtss_combination:
|
||||||
|
rtss_file = os.path.join(output_dir_path, rtss_filename)
|
||||||
|
shutil.copy(rtss_file, Path(sys.argv[1]).resolve().parent)
|
||||||
|
RTSS.append(rtss_file)
|
||||||
|
# SendDCM(rtss_file)
|
||||||
|
|
||||||
|
SendDCMs(RTSS)
|
||||||
|
|
||||||
logger.info(f"處理完成,總耗時:{time.time() - start_time:.2f} 秒")
|
logger.info(f"處理完成,總耗時:{time.time() - start_time:.2f} 秒")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"處理過程發生錯誤:{str(e)}", exc_info=True)
|
logger.error(f"處理過程發生錯誤:{str(e)}", exc_info=True)
|
||||||
|
|
Loading…
Reference in a new issue