add DICOM slice previews to image library
This commit is contained in:
@@ -16,10 +16,14 @@ from urllib.parse import parse_qs, unquote, urlparse
|
||||
|
||||
os.environ.setdefault("MPLCONFIGDIR", "/tmp/head_ct_morph_matplotlib")
|
||||
|
||||
import pydicom
|
||||
from PIL import Image
|
||||
|
||||
from generate_head_extension_video import generate_video
|
||||
from head_extension_app import (
|
||||
APP_DIR,
|
||||
crop_head_neck,
|
||||
ct_window,
|
||||
fit_image,
|
||||
load_dicom_volume,
|
||||
preview_deform_2d,
|
||||
@@ -109,6 +113,54 @@ def list_library():
|
||||
return live_items
|
||||
|
||||
|
||||
def sort_key_for_dicom(path):
|
||||
path = Path(path)
|
||||
try:
|
||||
ds = pydicom.dcmread(str(path), stop_before_pixels=True, force=True)
|
||||
return (0, int(getattr(ds, "InstanceNumber", 0)), path.name)
|
||||
except Exception:
|
||||
stem = path.stem
|
||||
return (1, int(stem) if stem.isdigit() else 0, path.name)
|
||||
|
||||
|
||||
def sorted_dicom_files(dicom_dir):
|
||||
return sorted(Path(dicom_dir).glob("*.dcm"), key=sort_key_for_dicom)
|
||||
|
||||
|
||||
def find_library_item(item_id):
|
||||
return next((item for item in list_library() if item["id"] == item_id), None)
|
||||
|
||||
|
||||
def make_library_slice_preview(item_id, index):
|
||||
item = find_library_item(item_id)
|
||||
if not item:
|
||||
raise RuntimeError("影像库中没有找到该数据。")
|
||||
|
||||
dicom_files = sorted_dicom_files(item["dicomPath"])
|
||||
if not dicom_files:
|
||||
raise RuntimeError("该影像数据没有可预览的 .dcm 文件。")
|
||||
|
||||
count = len(dicom_files)
|
||||
index = max(0, min(int(index), count - 1))
|
||||
ds = pydicom.dcmread(str(dicom_files[index]), force=True)
|
||||
image = ds.pixel_array.astype("float32")
|
||||
image = image * float(getattr(ds, "RescaleSlope", 1))
|
||||
image = image + float(getattr(ds, "RescaleIntercept", 0))
|
||||
|
||||
preview = Image.fromarray(ct_window(image)).convert("RGB")
|
||||
preview = fit_image(preview, 720, 520)
|
||||
canvas = BytesIO()
|
||||
preview.save(canvas, format="PNG")
|
||||
encoded = base64.b64encode(canvas.getvalue()).decode("ascii")
|
||||
return {
|
||||
"image": f"data:image/png;base64,{encoded}",
|
||||
"index": index,
|
||||
"count": count,
|
||||
"file": dicom_files[index].name,
|
||||
"patientId": item["patientId"],
|
||||
}
|
||||
|
||||
|
||||
def parse_multipart(headers, body):
|
||||
content_type = headers.get("content-type", "")
|
||||
message = BytesParser(policy=default).parsebytes(
|
||||
@@ -333,6 +385,13 @@ class Handler(BaseHTTPRequestHandler):
|
||||
self.send_json({"items": list_library()})
|
||||
return
|
||||
|
||||
if parsed.path == "/api/library/preview":
|
||||
params = parse_qs(parsed.query)
|
||||
item_id = params.get("id", [""])[0]
|
||||
index = params.get("index", ["0"])[0]
|
||||
self.send_json(make_library_slice_preview(item_id, index))
|
||||
return
|
||||
|
||||
if parsed.path == "/api/job":
|
||||
params = parse_qs(parsed.query)
|
||||
job_id = params.get("id", [""])[0]
|
||||
|
||||
Reference in New Issue
Block a user