Skip to content

Commit 6a94827

Browse files
committed
Fir #2423 by resampling oblique images first.
1 parent dfc8147 commit 6a94827

File tree

3 files changed

+86
-11
lines changed

3 files changed

+86
-11
lines changed

Libs/Groom/Groom.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,10 @@ bool Groom::image_pipeline(std::shared_ptr<Subject> subject, size_t domain) {
216216

217217
//---------------------------------------------------------------------------
218218
bool Groom::run_image_pipeline(Image& image, GroomParameters params) {
219+
220+
// ensure axis aligned
221+
image.toAxisAligned();
222+
219223
// isolate
220224
if (params.get_isolate_tool()) {
221225
image.isolate();

Libs/Image/Image.cpp

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -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

434502
bool Image::compare(const Image& other, bool verifyall, double tolerance, double precision) const {

Libs/Image/Image.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ class Image {
105105
/// resamples image using isotropic physical spacing
106106
Image& resample(double isoSpacing = 1.0, InterpolationType interp = Linear);
107107

108+
/// resamples image to be axis-aligned (i.e., identity direction matrix) if needed
109+
Image& toAxisAligned(InterpolationType interp = NearestNeighbor);
110+
108111
/// changes logical image size, computing new physical spacing based on this size (i.e., physical image size remains
109112
/// the same)
110113
Image& resize(Dims logicalDims, InterpolationType interp = Linear);

0 commit comments

Comments
 (0)