@@ -416,19 +416,87 @@ Image& Image::resample(double isoSpacing, Image::InterpolationType interp) {
416416 return resample (makeVector ({isoSpacing, isoSpacing, isoSpacing}), interp);
417417}
418418
419- Image& Image::resize (Dims dims, Image::InterpolationType interp) {
420- // use existing dims for any that are unspecified
421- Dims inputDims (this ->dims ());
422- if (dims[0 ] == 0 ) dims[0 ] = inputDims[0 ];
423- if (dims[1 ] == 0 ) dims[1 ] = inputDims[1 ];
424- if (dims[2 ] == 0 ) dims[2 ] = inputDims[2 ];
419+ Image& Image::toAxisAligned (InterpolationType interp) {
420+ // Check if image is already axis-aligned
421+ auto direction = itk_image_->GetDirection ();
422+ bool is_oblique = false ;
423+ for (unsigned int i = 0 ; i < 3 ; i++) {
424+ for (unsigned int j = 0 ; j < 3 ; j++) {
425+ double expected = (i == j) ? 1.0 : 0.0 ;
426+ if (std::abs (direction (i, j) - expected) > 1e-6 ) {
427+ is_oblique = true ;
428+ break ;
429+ }
430+ }
431+ }
432+
433+ // If already axis-aligned, return immediately
434+ if (!is_oblique) {
435+ return *this ;
436+ }
437+
438+ // The key insight: we need to create a transform that represents
439+ // the DIFFERENCE between the oblique and axis-aligned coordinate systems
440+
441+ using TransformType = itk::AffineTransform<double , 3 >;
442+ auto transform = TransformType::New ();
443+
444+ // The direction matrix rotates from index space to physical space
445+ // To go from new axis-aligned indices to old oblique physical space,
446+ // we need: newPhysical = identity * newIndex
447+ // oldIndex = direction^-1 * oldPhysical
448+ // So: oldIndex = direction^-1 * identity * newIndex = direction^-1 * newIndex
449+
450+ TransformType::MatrixType matrix;
451+ for (int i = 0 ; i < 3 ; i++) {
452+ for (int j = 0 ; j < 3 ; j++) {
453+ matrix (i, j) = direction (i, j);
454+ }
455+ }
456+ transform->SetMatrix (matrix);
425457
426- // compute new spacing so physical image size remains the same
427- Vector3 inputSpacing (spacing ());
428- Vector3 spacing (makeVector ({inputSpacing[0 ] * inputDims[0 ] / dims[0 ], inputSpacing[1 ] * inputDims[1 ] / dims[1 ],
429- inputSpacing[2 ] * inputDims[2 ] / dims[2 ]}));
458+ // Get bounding box
459+ auto size = itk_image_->GetLargestPossibleRegion ().GetSize ();
460+ auto spacing = itk_image_->GetSpacing ();
461+ auto origin = itk_image_->GetOrigin ();
462+
463+ Point3 minPt, maxPt;
464+ for (int i = 0 ; i < 3 ; i++) {
465+ minPt[i] = std::numeric_limits<double >::max ();
466+ maxPt[i] = std::numeric_limits<double >::lowest ();
467+ }
468+
469+ for (int corner = 0 ; corner < 8 ; corner++) {
470+ Coord index;
471+ index[0 ] = (corner & 1 ) ? size[0 ] - 1 : 0 ;
472+ index[1 ] = (corner & 2 ) ? size[1 ] - 1 : 0 ;
473+ index[2 ] = (corner & 4 ) ? size[2 ] - 1 : 0 ;
474+
475+ Point3 point;
476+ itk_image_->TransformIndexToPhysicalPoint (index, point);
477+
478+ for (int dim = 0 ; dim < 3 ; dim++) {
479+ minPt[dim] = std::min (minPt[dim], point[dim]);
480+ maxPt[dim] = std::max (maxPt[dim], point[dim]);
481+ }
482+ }
483+
484+ ImageType::DirectionType identityDir;
485+ identityDir.SetIdentity ();
486+
487+ ImageType::SizeType outputSize;
488+ for (int i = 0 ; i < 3 ; i++) {
489+ outputSize[i] = static_cast <size_t >(std::ceil ((maxPt[i] - minPt[i]) / spacing[i])) + 1 ;
490+ }
491+
492+ Vector3 spacingVec;
493+ Dims dims;
494+ for (int i = 0 ; i < 3 ; i++) {
495+ spacingVec[i] = spacing[i];
496+ dims[i] = outputSize[i];
497+ }
430498
431- return resample (IdentityTransform::New (), origin () , dims, spacing, coordsys () , interp);
499+ return resample (transform, minPt , dims, spacingVec, identityDir , interp);
432500}
433501
434502bool Image::compare (const Image& other, bool verifyall, double tolerance, double precision) const {
0 commit comments