feat(tasks): Enhance series_reconstruct_task with additional metadata and improved slice handling

This commit is contained in:
Ross
2026-05-17 22:29:23 +01:00
parent a9946961f1
commit ff00efecf7
+30 -7
View File
@@ -86,6 +86,7 @@ from generic.models import CimarCase
from rad.settings import REMOTE_URL, CIMAR_USERNAME, CIMAR_PASSWORD
from helpers.cimar import CimarAPI, NotFoundError
from pydicom.uid import generate_uid
from datetime import datetime
from django.contrib.auth.models import User
from django.core.files.base import ContentFile
from django.core.cache import cache
@@ -338,6 +339,7 @@ def series_reconstruct_task(
"spacing_between_slices_out": float(plane_spacing),
"slice_thickness_out": float(plane_thickness),
"image_orientation_out": [1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
"plane_normal_out": [0.0, 0.0, 1.0],
"position_fn": lambda idx, centers=slab_centers_idx: position_from_patient_zyx(centers[idx], 0.0, 0.0),
}
elif plane_norm == "coronal":
@@ -348,12 +350,13 @@ def series_reconstruct_task(
)
plane_builders[plane_norm] = {
"count": int(len(slab_indices)),
"slice_fn": lambda idx, slabs=slab_indices: _reduce_slab(vol_zyx[:, slabs[idx], :], reduce_axis=1),
"slice_fn": lambda idx, slabs=slab_indices: np.flipud(_reduce_slab(vol_zyx[:, slabs[idx], :], reduce_axis=1)),
"pixel_spacing_out": [float(spacing_z), float(spacing_x)],
"spacing_between_slices_out": float(plane_spacing),
"slice_thickness_out": float(plane_thickness),
"image_orientation_out": [1.0, 0.0, 0.0, 0.0, 0.0, 1.0],
"position_fn": lambda idx, centers=slab_centers_idx: position_from_patient_zyx(0.0, centers[idx], 0.0),
"image_orientation_out": [1.0, 0.0, 0.0, 0.0, 0.0, -1.0],
"plane_normal_out": [0.0, 1.0, 0.0],
"position_fn": lambda idx, centers=slab_centers_idx: position_from_patient_zyx(float(vol_zyx.shape[0] - 1), centers[idx], 0.0),
}
elif plane_norm == "sagittal":
plane_spacing = float(requested_slice_spacing) if requested_slice_spacing is not None else float(spacing_x)
@@ -363,12 +366,13 @@ def series_reconstruct_task(
)
plane_builders[plane_norm] = {
"count": int(len(slab_indices)),
"slice_fn": lambda idx, slabs=slab_indices: _reduce_slab(vol_zyx[:, :, slabs[idx]], reduce_axis=2),
"slice_fn": lambda idx, slabs=slab_indices: np.flipud(_reduce_slab(vol_zyx[:, :, slabs[idx]], reduce_axis=2)),
"pixel_spacing_out": [float(spacing_z), float(spacing_y)],
"spacing_between_slices_out": float(plane_spacing),
"slice_thickness_out": float(plane_thickness),
"image_orientation_out": [0.0, 1.0, 0.0, 0.0, 0.0, 1.0],
"position_fn": lambda idx, centers=slab_centers_idx: position_from_patient_zyx(0.0, 0.0, centers[idx]),
"image_orientation_out": [0.0, 1.0, 0.0, 0.0, 0.0, -1.0],
"plane_normal_out": [1.0, 0.0, 0.0],
"position_fn": lambda idx, centers=slab_centers_idx: position_from_patient_zyx(float(vol_zyx.shape[0] - 1), 0.0, centers[idx]),
}
if not plane_builders:
@@ -388,6 +392,9 @@ def series_reconstruct_task(
recon_series = atlas_views._create_series_derivative(series, f"Recon {plane_norm.title()} ({source_plane.title()} src)")
recon_series.series_instance_uid = generate_uid()
recon_series.save(update_fields=["series_instance_uid"])
now = datetime.utcnow()
date_str = now.strftime("%Y%m%d")
time_str = now.strftime("%H%M%S.%f")[:13]
for idx in range(int(cfg["count"])):
arr2d = cfg["slice_fn"](idx)
@@ -399,12 +406,28 @@ def series_reconstruct_task(
ds_new.InstanceNumber = idx + 1
ds_new.SOPInstanceUID = generate_uid()
ds_new.SeriesInstanceUID = recon_series.series_instance_uid
ds_new.SeriesDescription = recon_series.description or f"Recon {plane_norm.title()}"
ds_new.ImageType = ["DERIVED", "SECONDARY", "MPR"]
ds_new.DerivationDescription = (
f"{plane_norm.title()} reconstruction; mode={recon_thickness_mode}; "
f"thickness={float(cfg['slice_thickness_out']):.3f}mm; spacing={float(cfg['spacing_between_slices_out']):.3f}mm"
)
ds_new.ContentDate = date_str
ds_new.ContentTime = time_str
ds_new.InstanceCreationDate = date_str
ds_new.InstanceCreationTime = time_str
ds_new.PixelData = arr2d.tobytes()
ds_new.PixelSpacing = [float(cfg["pixel_spacing_out"][0]), float(cfg["pixel_spacing_out"][1])]
ds_new.ImageOrientationPatient = cfg["image_orientation_out"]
ds_new.ImagePositionPatient = cfg["position_fn"](idx)
ipp = cfg["position_fn"](idx)
ds_new.ImagePositionPatient = ipp
ds_new.SliceThickness = float(cfg["slice_thickness_out"])
ds_new.SpacingBetweenSlices = float(cfg["spacing_between_slices_out"])
try:
normal = np.asarray(cfg["plane_normal_out"], dtype=float)
ds_new.SliceLocation = float(np.dot(np.asarray(ipp, dtype=float), normal))
except Exception:
pass
out_io = io.BytesIO()
ds_new.save_as(out_io, write_like_original=False)