Rotate a 3D image, in medical volumes, is two different jobs: reorient so the array matches a named patient frame (LPS / RAS), and resample a rotation so the voxels themselves turn (Euler / affine). It is not a Blender orbit of a product shot. It is not a mesh cut. It is not a PYCAD rotate product.
If you meant which way +x points → image coordinate system. If you meant freehand-cut a mesh in Vedo → 3D mesh cutter. If you meant a PNG of that mesh → Vedo screenshots. If you meant Blender / Unreal / KeyShot as DCC tools → 3D visualization software. If you meant what a .vtk file is → VTK data format.
The live body this URL used to carry was an Outrank 360°-product / Maya / Cinema 4D stamp. That is not the job. This page is the volume. PYCAD builds custom web DICOM viewers and imaging models. A viewer has to honour direction cosines; that is not a rotate SKU.
Two jobs people mix
| Job | What changes | What stays | When you want it |
|---|---|---|---|
| Reorient | Axis order and flips so the array is LPS (or RAS) | The patient. Spacing, if you only permute/flip | A NIfTI that opens on its side; a mask that does not overlay the CT |
| Rotate (resample) | Every voxel is interpolated through a transform | Nothing for free. You invent values | A 15° gantry tilt you want upright; an aug; a registration |
Permute-and-flip is a metadata + index-order change. A 15° Euler is a new grid. Do not call a np.rot90 on a numpy view “I reoriented the patient” unless you also rewrite origin and direction. That is how a left kidney becomes a right kidney in a mesh.
Reorient (canonical frame)
SimpleITK’s DICOMOrient permutes and flips so the stored axes match a three-letter code. LPS is the DICOM default. RAS is what 3D Slicer uses inside. The coordinate-system page is the why; this is the call:
import SimpleITK as sitk
img = sitk.ReadImage("ct.nii.gz")
print("before", sitk.DICOMOrientImageFilter().GetOrientationFromDirectionCosines(img.GetDirection()))
lps = sitk.DICOMOrient(img, "LPS")
print("after", sitk.DICOMOrientImageFilter().GetOrientationFromDirectionCosines(lps.GetDirection()))
sitk.WriteImage(lps, "ct_lps.nii.gz")
A labelmap must go through the same permute/flip. Interpolating a mask with linear is how you get 0.4 at a boundary that was 0 or 1. For a reorient, nearest-neighbour is the mask rule — but DICOMOrient is a permute/flip, not an interpolate, so the label values survive if you run it on the label image too.
Rotate (Euler, then resample)
A rotation in patient millimetres, around the volume centre, then a resample onto the original grid (or a new one). Linear for the CT/MR. Nearest for the mask. The fill value is usually 0 — know what 0 means on that file (air on CT is not 0 HU; it is closer to −1000).
import math
import SimpleITK as sitk
img = sitk.ReadImage("ct.nii.gz")
center = img.TransformContinuousIndexToPhysicalPoint([s / 2.0 for s in img.GetSize()])
tx = sitk.Euler3DTransform()
tx.SetCenter(center)
tx.SetRotation(0.0, 0.0, math.radians(90)) # about z, patient frame
rotated = sitk.Resample(
img,
img,
tx,
sitk.sitkLinear,
0.0,
img.GetPixelID(),
)
sitk.WriteImage(rotated, "ct_rot90z.nii.gz")
Registration (image registration in Python) is this transform estimated from two volumes, not typed as 90°. Augmentation in MONAI (RandRotated / RandAffined) is the same idea during training — that notebook is 3063, not this page.
What this page is not
- Not a 360° e-commerce spinner, Maya, or Cinema 4D. If you meant those DCC tools, 7786 is the list.
- Not Vedo. Cut and screenshot stay on 259 and 410.
- Not “what LPS vs RAS means.” That is 5542.
- Not a PYCAD rotate app, a quaternion product, or a market CAGR. Dropped.
Install is pip install SimpleITK. If the rotate has to happen in a clinic viewer (a hang that honours direction cosines), that is the imaging piece. Case studies.