CSF_Seg / app.py
Haojiacheng's picture
Update app.py
dd9427d
import os
import matplotlib.pyplot as plt
from monai.losses import DiceCELoss
from monai.inferers import sliding_window_inference
from monai.transforms import (
EnsureChannelFirstd,
Compose,
LoadImaged,
Orientationd,
ScaleIntensityRanged,
Rotate90d,
)
from monai.networks.nets import UNETR
from monai.data import (
SmartCacheDataset,
)
import numpy as np
import torch
import gradio as gr
import matplotlib.pyplot as plt
import torch
import nibabel as nib
import numpy as np
import SimpleITK as sitk
def dcm2nii(dcms_path, nii_path):
# 1.构建dicom序列文件阅读器,并执行(即将dicom序列文件“打包整合”)
reader = sitk.ImageSeriesReader()
dicom_names = reader.GetGDCMSeriesFileNames(dcms_path)
reader.SetFileNames(dicom_names)
image2 = reader.Execute()
# 2.将整合后的数据转为array,并获取dicom文件基本信息
image_array = sitk.GetArrayFromImage(image2) # z, y, x
origin = image2.GetOrigin() # x, y, z
print(origin)
spacing = image2.GetSpacing() # x, y, z
print(spacing)
direction = image2.GetDirection() # x, y, z
print(direction)
# 3.将array转为img,并保存为.nii.gz
image3 = sitk.GetImageFromArray(image_array)
image3.SetSpacing(spacing)
image3.SetDirection(direction)
image3.SetOrigin(origin)
sitk.WriteImage(image3, nii_path)
def calculate_volume(mask_image_path):
# 读取分割结果的图像文件
mask_image = sitk.ReadImage(mask_image_path)
# 获取图像的大小、原点和间距
size = mask_image.GetSize()
origin = mask_image.GetOrigin()
spacing = mask_image.GetSpacing()
# 将 SimpleITK 图像转换为 NumPy 数组
mask_array = sitk.GetArrayFromImage(mask_image)
# if len(np.unique(mask_array)) != 5:
# print(mask_image_path[-15:-12])
# print(np.unique(mask_array))
# 计算非零像素的数量
one_voxels = (mask_array == 1).sum()
two_voxels = (mask_array == 2).sum()
three_voxels = (mask_array == 3).sum()
four_voxels = (mask_array == 4).sum()
# print(one_voxels,two_voxels,three_voxels,four_voxels)
# 计算像素的体积(以立方毫米为单位)
voxel_volume_mm3 = spacing[0] * spacing[1] * spacing[2]
# 计算体积(以 mm³ 为单位)
V_Right_ventricular_cistern = one_voxels * voxel_volume_mm3 / 1000.0
V_Right_cerebral_sulcus = two_voxels * voxel_volume_mm3 / 1000.0
V_Left_ventricular_cistern = three_voxels * voxel_volume_mm3 / 1000.0
V_Left_cerebral_sulcus = four_voxels * voxel_volume_mm3 / 1000.0
# 如果需要以其他单位(例如 cm³)显示,请进行适当的单位转换
# volume_cm3 = volume_mm3 / 1000.0
return size,spacing,V_Right_ventricular_cistern, V_Right_cerebral_sulcus, V_Left_ventricular_cistern, V_Left_cerebral_sulcus
def process_nii_file(input_nii_file, slice, mode):
if mode == "Step1:Segment":
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
root_dir = "./"
model = UNETR(
in_channels=1,
out_channels=5,
img_size=(96, 96, 16),
feature_size=16,
hidden_size=768,
mlp_dim=3072,
num_heads=12,
pos_embed="perceptron",
norm_name="instance",
res_block=True,
dropout_rate=0.0,
).to(device)
model.load_state_dict(torch.load(os.path.join(root_dir, "best_metric_model67v2.pth"), map_location=torch.device('cpu'))) # CPU map
test_transforms = Compose(
[
LoadImaged(keys=["image"]),
EnsureChannelFirstd(keys=["image"]),
Orientationd(keys=["image"], axcodes="RAS"),
ScaleIntensityRanged(
keys=["image"],
a_min=-50,
a_max=100,
b_min=0.0,
b_max=1.0,
clip=True,
),
Rotate90d(keys=["image"], k=1)
# ResizeWithPadOrCropd(keys=["image"], spatial_size=(512, 512, 16)),
]
)
test_file = [{'image':input_nii_file.name}]
test_image = SmartCacheDataset(data=test_file, transform=test_transforms)[0]['image']
with torch.no_grad():
inputs = torch.unsqueeze(test_image, 1).cpu() ################################### .cuda() -> .cpu()
val_outputs = sliding_window_inference(inputs, (96, 96, 16), 8, model, overlap=0.8)
# Display the images
fig1 = plt.figure()
plt.title("image")
plt.axis('off') # Remove axis
plt.imshow(inputs.cpu().numpy()[0, 0, :, :, slice], cmap="gray")
fig2 = plt.figure()
plt.title("output")
plt.axis('off') # Remove axis
plt.imshow(torch.argmax(val_outputs, dim=1).detach().cpu()[0, :, :, slice])
val_outputs = torch.argmax(val_outputs, dim=1).detach().cpu()[0, :, :, :]
val_outputs = val_outputs.numpy().astype('int16')
# val_outputs = np.transpose(val_outputs, (2, 1, 0))
val_outputs = np.rot90(val_outputs, k=3)
val_outputs = nib.Nifti1Image(val_outputs, np.eye(4))
nib.save(val_outputs, f'D:/{input_nii_file.name[-15:-7]}_mask.nii.gz')
return ["指定切片分割结果如下, mask文件已保存至D:/", fig1, fig2]
if mode == "Step2:Volumn":
maskFilePath = input_nii_file.name
size,spacing,V_Right_ventricular_cistern, V_Right_cerebral_sulcus, V_Left_ventricular_cistern, V_Left_cerebral_sulcus = calculate_volume(maskFilePath)
vol = f"""右侧脑室脑池的体积为{V_Right_ventricular_cistern}cm³\n 右侧脑沟的体积为{V_Right_cerebral_sulcus}cm³\n 左侧脑室脑池的体积为{V_Left_ventricular_cistern}cm³\n 左侧脑沟的体积为{V_Left_cerebral_sulcus}cm³"""
fig1 = plt.figure()
fig2 = plt.figure()
return [vol, fig1, fig2]
# Define the Gradio interface
iface = gr.Interface(
fn=process_nii_file,
inputs=
[gr.File(file_count='single', file_types=['.nii.gz']),
gr.Slider(0, 24, value=8, label="Select Slice", step=1),
gr.Radio(
["Step1:Segment", "Step2:Volumn"], label="mode"
),
],
outputs=[gr.Text(label="Output"), gr.Plot(label="image"), gr.Plot(label="mask")], # Display both "image" and "output"
)
iface.launch()