diff --git a/atlas/tasks.py b/atlas/tasks.py index 085c4815..e752705d 100644 --- a/atlas/tasks.py +++ b/atlas/tasks.py @@ -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)