/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkImageReslice.cxx,v $ Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkImageReslice.h" #include "vtkImageData.h" #include "vtkImageStencilData.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkMath.h" #include "vtkObjectFactory.h" #include "vtkStreamingDemandDrivenPipeline.h" #include "vtkTransform.h" #include "vtkDataSetAttributes.h" #include "vtkTemplateAliasMacro.h" // turn off 64-bit ints when templating over all types # undef VTK_USE_INT64 # define VTK_USE_INT64 0 # undef VTK_USE_UINT64 # define VTK_USE_UINT64 0 #include #include #include vtkCxxRevisionMacro(vtkImageReslice, "$Revision: 1.63.6.2 $"); vtkStandardNewMacro(vtkImageReslice); vtkCxxSetObjectMacro(vtkImageReslice, InformationInput, vtkImageData); vtkCxxSetObjectMacro(vtkImageReslice,ResliceAxes,vtkMatrix4x4); vtkCxxSetObjectMacro(vtkImageReslice,ResliceTransform,vtkAbstractTransform); //-------------------------------------------------------------------------- // The 'floor' function on x86 and mips is many times slower than these // and is used a lot in this code, optimize for different CPU architectures template inline int vtkResliceFloor(double x, F &f) { #if defined mips || defined sparc || defined __ppc__ x += 2147483648.0; unsigned int i = (unsigned int)(x); f = x - i; return (int)(i - 2147483648U); #elif defined i386 || defined _M_IX86 union { double d; unsigned short s[4]; unsigned int i[2]; } dual; dual.d = x + 103079215104.0; // (2**(52-16))*1.5 f = dual.s[0]*0.0000152587890625; // 2**(-16) return (int)((dual.i[1]<<16)|((dual.i[0])>>16)); #elif defined ia64 || defined __ia64__ || defined IA64 x += 103079215104.0; long long i = (long long)(x); f = x - i; return (int)(i - 103079215104LL); #else double y = floor(x); f = x - y; return (int)(y); #endif } inline int vtkResliceRound(double x) { #if defined mips || defined sparc || defined __ppc__ return (int)((unsigned int)(x + 2147483648.5) - 2147483648U); #elif defined i386 || defined _M_IX86 union { double d; unsigned int i[2]; } dual; dual.d = x + 103079215104.5; // (2**(52-16))*1.5 return (int)((dual.i[1]<<16)|((dual.i[0])>>16)); #elif defined ia64 || defined __ia64__ || defined IA64 x += 103079215104.5; long long i = (long long)(x); return (int)(i - 103079215104LL); #else return (int)(floor(x+0.5)); #endif } //---------------------------------------------------------------------------- vtkImageReslice::vtkImageReslice() { // if NULL, the main Input is used this->InformationInput = NULL; this->TransformInputSampling = 1; this->AutoCropOutput = 0; this->OutputDimensionality = 3; // flag to use default Spacing this->OutputSpacing[0] = VTK_DOUBLE_MAX; this->OutputSpacing[1] = VTK_DOUBLE_MAX; this->OutputSpacing[2] = VTK_DOUBLE_MAX; // ditto this->OutputOrigin[0] = VTK_DOUBLE_MAX; this->OutputOrigin[1] = VTK_DOUBLE_MAX; this->OutputOrigin[2] = VTK_DOUBLE_MAX; // ditto this->OutputExtent[0] = VTK_INT_MIN; this->OutputExtent[2] = VTK_INT_MIN; this->OutputExtent[4] = VTK_INT_MIN; this->OutputExtent[1] = VTK_INT_MAX; this->OutputExtent[3] = VTK_INT_MAX; this->OutputExtent[5] = VTK_INT_MAX; this->Wrap = 0; // don't wrap this->Mirror = 0; // don't mirror this->Border = 1; // apply a border this->InterpolationMode = VTK_RESLICE_NEAREST; // no interpolation this->Optimization = 1; // turn off when you're paranoid // default black background this->BackgroundColor[0] = 0; this->BackgroundColor[1] = 0; this->BackgroundColor[2] = 0; this->BackgroundColor[3] = 0; // default reslice axes are x, y, z this->ResliceAxesDirectionCosines[0] = 1.0; this->ResliceAxesDirectionCosines[1] = 0.0; this->ResliceAxesDirectionCosines[2] = 0.0; this->ResliceAxesDirectionCosines[3] = 0.0; this->ResliceAxesDirectionCosines[4] = 1.0; this->ResliceAxesDirectionCosines[5] = 0.0; this->ResliceAxesDirectionCosines[6] = 0.0; this->ResliceAxesDirectionCosines[7] = 0.0; this->ResliceAxesDirectionCosines[8] = 1.0; // default (0,0,0) axes origin this->ResliceAxesOrigin[0] = 0.0; this->ResliceAxesOrigin[1] = 0.0; this->ResliceAxesOrigin[2] = 0.0; // axes and transform are identity if set to NULL this->ResliceAxes = NULL; this->ResliceTransform = NULL; // cache a matrix that converts output voxel indices -> input voxel indices this->IndexMatrix = NULL; this->OptimizedTransform = NULL; // set to zero when we completely missed the input extent this->HitInputExtent = 1; // There is an optional second input. this->SetNumberOfInputPorts(2); } //---------------------------------------------------------------------------- vtkImageReslice::~vtkImageReslice() { this->SetResliceTransform(NULL); this->SetResliceAxes(NULL); if (this->IndexMatrix) { this->IndexMatrix->Delete(); } if (this->OptimizedTransform) { this->OptimizedTransform->Delete(); } this->SetInformationInput(NULL); } //---------------------------------------------------------------------------- void vtkImageReslice::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); os << indent << "ResliceAxes: " << this->ResliceAxes << "\n"; if (this->ResliceAxes) { this->ResliceAxes->PrintSelf(os,indent.GetNextIndent()); } this->GetResliceAxesDirectionCosines(this->ResliceAxesDirectionCosines); os << indent << "ResliceAxesDirectionCosines: " << this->ResliceAxesDirectionCosines[0] << " " << this->ResliceAxesDirectionCosines[1] << " " << this->ResliceAxesDirectionCosines[2] << "\n"; os << indent << " " << this->ResliceAxesDirectionCosines[3] << " " << this->ResliceAxesDirectionCosines[4] << " " << this->ResliceAxesDirectionCosines[5] << "\n"; os << indent << " " << this->ResliceAxesDirectionCosines[6] << " " << this->ResliceAxesDirectionCosines[7] << " " << this->ResliceAxesDirectionCosines[8] << "\n"; this->GetResliceAxesOrigin(this->ResliceAxesOrigin); os << indent << "ResliceAxesOrigin: " << this->ResliceAxesOrigin[0] << " " << this->ResliceAxesOrigin[1] << " " << this->ResliceAxesOrigin[2] << "\n"; os << indent << "ResliceTransform: " << this->ResliceTransform << "\n"; if (this->ResliceTransform) { this->ResliceTransform->PrintSelf(os,indent.GetNextIndent()); } os << indent << "InformationInput: " << this->InformationInput << "\n"; os << indent << "TransformInputSampling: " << (this->TransformInputSampling ? "On\n":"Off\n"); os << indent << "AutoCropOutput: " << (this->AutoCropOutput ? "On\n":"Off\n"); os << indent << "OutputSpacing: " << this->OutputSpacing[0] << " " << this->OutputSpacing[1] << " " << this->OutputSpacing[2] << "\n"; os << indent << "OutputOrigin: " << this->OutputOrigin[0] << " " << this->OutputOrigin[1] << " " << this->OutputOrigin[2] << "\n"; os << indent << "OutputExtent: " << this->OutputExtent[0] << " " << this->OutputExtent[1] << " " << this->OutputExtent[2] << " " << this->OutputExtent[3] << " " << this->OutputExtent[4] << " " << this->OutputExtent[5] << "\n"; os << indent << "OutputDimensionality: " << this->OutputDimensionality << "\n"; os << indent << "Wrap: " << (this->Wrap ? "On\n":"Off\n"); os << indent << "Mirror: " << (this->Mirror ? "On\n":"Off\n"); os << indent << "Border: " << (this->Border ? "On\n":"Off\n"); os << indent << "InterpolationMode: " << this->GetInterpolationModeAsString() << "\n"; os << indent << "Optimization: " << (this->Optimization ? "On\n":"Off\n"); os << indent << "BackgroundColor: " << this->BackgroundColor[0] << " " << this->BackgroundColor[1] << " " << this->BackgroundColor[2] << " " << this->BackgroundColor[3] << "\n"; os << indent << "BackgroundLevel: " << this->BackgroundColor[0] << "\n"; os << indent << "Stencil: " << this->GetStencil() << "\n"; } //---------------------------------------------------------------------------- void vtkImageReslice::SetStencil(vtkImageStencilData *stencil) { this->SetInput(1, stencil); } //---------------------------------------------------------------------------- vtkImageStencilData *vtkImageReslice::GetStencil() { if (this->GetNumberOfInputConnections(1) < 1) { return NULL; } return vtkImageStencilData::SafeDownCast( this->GetExecutive()->GetInputData(1, 0)); } //---------------------------------------------------------------------------- void vtkImageReslice::SetResliceAxesDirectionCosines(double x0, double x1, double x2, double y0, double y1, double y2, double z0, double z1, double z2) { if (!this->ResliceAxes) { // consistent registers/unregisters this->SetResliceAxes(vtkMatrix4x4::New()); this->ResliceAxes->Delete(); this->Modified(); } this->ResliceAxes->SetElement(0,0,x0); this->ResliceAxes->SetElement(1,0,x1); this->ResliceAxes->SetElement(2,0,x2); this->ResliceAxes->SetElement(3,0,0); this->ResliceAxes->SetElement(0,1,y0); this->ResliceAxes->SetElement(1,1,y1); this->ResliceAxes->SetElement(2,1,y2); this->ResliceAxes->SetElement(3,1,0); this->ResliceAxes->SetElement(0,2,z0); this->ResliceAxes->SetElement(1,2,z1); this->ResliceAxes->SetElement(2,2,z2); this->ResliceAxes->SetElement(3,2,0); } //---------------------------------------------------------------------------- void vtkImageReslice::GetResliceAxesDirectionCosines(double xdircos[3], double ydircos[3], double zdircos[3]) { if (!this->ResliceAxes) { xdircos[0] = ydircos[1] = zdircos[2] = 1; xdircos[1] = ydircos[2] = zdircos[0] = 0; xdircos[2] = ydircos[0] = zdircos[1] = 0; return; } for (int i = 0; i < 3; i++) { xdircos[i] = this->ResliceAxes->GetElement(i,0); ydircos[i] = this->ResliceAxes->GetElement(i,1); zdircos[i] = this->ResliceAxes->GetElement(i,2); } } //---------------------------------------------------------------------------- void vtkImageReslice::SetResliceAxesOrigin(double x, double y, double z) { if (!this->ResliceAxes) { // consistent registers/unregisters this->SetResliceAxes(vtkMatrix4x4::New()); this->ResliceAxes->Delete(); this->Modified(); } this->ResliceAxes->SetElement(0,3,x); this->ResliceAxes->SetElement(1,3,y); this->ResliceAxes->SetElement(2,3,z); this->ResliceAxes->SetElement(3,3,1); } //---------------------------------------------------------------------------- void vtkImageReslice::GetResliceAxesOrigin(double origin[3]) { if (!this->ResliceAxes) { origin[0] = origin[1] = origin[2] = 0; return; } for (int i = 0; i < 3; i++) { origin[i] = this->ResliceAxes->GetElement(i,3); } } //---------------------------------------------------------------------------- // Account for the MTime of the transform and its matrix when determining // the MTime of the filter unsigned long int vtkImageReslice::GetMTime() { unsigned long mTime=this->vtkObject::GetMTime(); unsigned long time; if ( this->ResliceTransform != NULL ) { time = this->ResliceTransform->GetMTime(); mTime = ( time > mTime ? time : mTime ); if (this->ResliceTransform->IsA("vtkHomogeneousTransform")) { // this is for people who directly modify the transform matrix time = ((vtkHomogeneousTransform *)this->ResliceTransform) ->GetMatrix()->GetMTime(); mTime = ( time > mTime ? time : mTime ); } } if ( this->ResliceAxes != NULL) { time = this->ResliceAxes->GetMTime(); mTime = ( time > mTime ? time : mTime ); } return mTime; } //---------------------------------------------------------------------------- int vtkImageReslice::RequestUpdateExtent( vtkInformation *vtkNotUsed(request), vtkInformationVector **inputVector, vtkInformationVector *outputVector) { int inExt[6], outExt[6]; vtkInformation *outInfo = outputVector->GetInformationObject(0); vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), outExt); if (this->ResliceTransform) { this->ResliceTransform->Update(); if (!this->ResliceTransform->IsA("vtkHomogeneousTransform")) { // update the whole input extent if the transform is nonlinear inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), inExt); inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), inExt, 6); return 1; } } int i,j,k; int idX,idY,idZ; double xAxis[4], yAxis[4], zAxis[4], origin[4]; double inPoint0[4]; double inPoint1[4]; double point[4],f; double *inSpacing,*inOrigin,*outSpacing,*outOrigin,inInvSpacing[3]; int wrap = this->Wrap || this->Mirror; inOrigin = inInfo->Get(vtkDataObject::ORIGIN()); inSpacing = inInfo->Get(vtkDataObject::SPACING()); outOrigin = outInfo->Get(vtkDataObject::ORIGIN()); outSpacing = outInfo->Get(vtkDataObject::SPACING()); if (this->Optimization) { vtkMatrix4x4 *matrix = this->GetIndexMatrix(inInfo, outInfo); // convert matrix from world coordinates to pixel indices for (i = 0; i < 4; i++) { xAxis[i] = matrix->GetElement(i,0); yAxis[i] = matrix->GetElement(i,1); zAxis[i] = matrix->GetElement(i,2); origin[i] = matrix->GetElement(i,3); } } else { // save effor later: invert inSpacing inInvSpacing[0] = 1.0/inSpacing[0]; inInvSpacing[1] = 1.0/inSpacing[1]; inInvSpacing[2] = 1.0/inSpacing[2]; } for (i = 0; i < 3; i++) { inExt[2*i] = VTK_INT_MAX; inExt[2*i+1] = VTK_INT_MIN; } // check the coordinates of the 8 corners of the output extent // (this must be done exactly the same as the calculation in // vtkImageResliceExecute) for (i = 0; i < 8; i++) { // get output coords idX = outExt[i%2]; idY = outExt[2+(i/2)%2]; idZ = outExt[4+(i/4)%2]; if (this->Optimization) { inPoint0[0] = origin[0] + idZ*zAxis[0]; // incremental transform inPoint0[1] = origin[1] + idZ*zAxis[1]; inPoint0[2] = origin[2] + idZ*zAxis[2]; inPoint0[3] = origin[3] + idZ*zAxis[3]; inPoint1[0] = inPoint0[0] + idY*yAxis[0]; // incremental transform inPoint1[1] = inPoint0[1] + idY*yAxis[1]; inPoint1[2] = inPoint0[2] + idY*yAxis[2]; inPoint1[3] = inPoint0[3] + idY*yAxis[3]; point[0] = inPoint1[0] + idX*xAxis[0]; point[1] = inPoint1[1] + idX*xAxis[1]; point[2] = inPoint1[2] + idX*xAxis[2]; point[3] = inPoint1[3] + idX*xAxis[3]; if (point[3] != 1.0) { f = 1/point[3]; point[0] *= f; point[1] *= f; point[2] *= f; } } else { point[0] = idX*outSpacing[0] + outOrigin[0]; point[1] = idY*outSpacing[1] + outOrigin[1]; point[2] = idZ*outSpacing[2] + outOrigin[2]; if (this->ResliceAxes) { point[3] = 1.0; this->ResliceAxes->MultiplyPoint(point, point); f = 1.0/point[3]; point[0] *= f; point[1] *= f; point[2] *= f; } if (this->ResliceTransform) { this->ResliceTransform->TransformPoint(point, point); } point[0] = (point[0] - inOrigin[0])*inInvSpacing[0]; point[1] = (point[1] - inOrigin[1])*inInvSpacing[1]; point[2] = (point[2] - inOrigin[2])*inInvSpacing[2]; } // set the extent appropriately according to the interpolation mode if (this->GetInterpolationMode() != VTK_RESLICE_NEAREST) { int extra = (this->GetInterpolationMode() == VTK_RESLICE_CUBIC); for (j = 0; j < 3; j++) { k = vtkResliceFloor(point[j], f); if (f == 0) { if (k < inExt[2*j]) { inExt[2*j] = k; } if (k > inExt[2*j+1]) { inExt[2*j+1] = k; } } else { if (k - extra < inExt[2*j]) { inExt[2*j] = k - extra; } if (k + 1 + extra > inExt[2*j+1]) { inExt[2*j+1] = k + 1 + extra; } } } } else { for (j = 0; j < 3; j++) { k = vtkResliceRound(point[j]); if (k < inExt[2*j]) { inExt[2*j] = k; } if (k > inExt[2*j+1]) { inExt[2*j+1] = k; } } } } // Clip to whole extent, make sure we hit the extent int wholeExtent[6]; inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), wholeExtent); this->HitInputExtent = 1; for (i = 0; i < 3; i++) { if (inExt[2*i] < wholeExtent[2*i]) { inExt[2*i] = wholeExtent[2*i]; if (wrap) { inExt[2*i+1] = wholeExtent[2*i+1]; } else if (inExt[2*i+1] < wholeExtent[2*i]) { // didn't hit any of the input extent inExt[2*i+1] = wholeExtent[2*i]; this->HitInputExtent = 0; } } if (inExt[2*i+1] > wholeExtent[2*i+1]) { inExt[2*i+1] = wholeExtent[2*i+1]; if (wrap) { inExt[2*i] = wholeExtent[2*i]; } else if (inExt[2*i] > wholeExtent[2*i+1]) { // didn't hit any of the input extent inExt[2*i] = wholeExtent[2*i+1]; // finally, check for null input extent if (inExt[2*i] < wholeExtent[2*i]) { inExt[2*i] = wholeExtent[2*i]; } this->HitInputExtent = 0; } } } inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), inExt, 6); // need to set the stencil update extent to the output extent if (this->GetNumberOfInputConnections(1) > 0) { vtkInformation *stencilInfo = inputVector[1]->GetInformationObject(0); stencilInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), outExt, 6); } return 1; } //---------------------------------------------------------------------------- int vtkImageReslice::FillInputPortInformation(int port, vtkInformation *info) { if (port == 1) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageStencilData"); // the stencil input is optional info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1); } else { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageData"); } return 1; } //---------------------------------------------------------------------------- void vtkImageReslice::GetAutoCroppedOutputBounds(vtkInformation *inInfo, double bounds[6]) { int i, j; double inSpacing[3], inOrigin[3]; int inWholeExt[6]; double f; double point[4]; inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), inWholeExt); inInfo->Get(vtkDataObject::SPACING(), inSpacing); inInfo->Get(vtkDataObject::ORIGIN(), inOrigin); vtkMatrix4x4 *matrix = vtkMatrix4x4::New(); if (this->ResliceAxes) { vtkMatrix4x4::Invert(this->ResliceAxes, matrix); } vtkAbstractTransform* transform = NULL; if (this->ResliceTransform) { transform = this->ResliceTransform->GetInverse(); } for (i = 0; i < 3; i++) { bounds[2*i] = VTK_DOUBLE_MAX; bounds[2*i+1] = -VTK_DOUBLE_MAX; } for (i = 0; i < 8; i++) { point[0] = inOrigin[0] + inWholeExt[i%2]*inSpacing[0]; point[1] = inOrigin[1] + inWholeExt[2+(i/2)%2]*inSpacing[1]; point[2] = inOrigin[2] + inWholeExt[4+(i/4)%2]*inSpacing[2]; point[3] = 1.0; if (this->ResliceTransform) { transform->TransformPoint(point,point); } matrix->MultiplyPoint(point,point); f = 1.0/point[3]; point[0] *= f; point[1] *= f; point[2] *= f; for (j = 0; j < 3; j++) { if (point[j] > bounds[2*j+1]) { bounds[2*j+1] = point[j]; } if (point[j] < bounds[2*j]) { bounds[2*j] = point[j]; } } } matrix->Delete(); } //---------------------------------------------------------------------------- int vtkImageReslice::RequestInformation( vtkInformation *vtkNotUsed(request), vtkInformationVector **inputVector, vtkInformationVector *outputVector) { int i,j; double inSpacing[3], inOrigin[3]; int inWholeExt[6]; double outSpacing[3], outOrigin[3]; int outWholeExt[6]; double maxBounds[6]; vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); vtkInformation *outInfo = outputVector->GetInformationObject(0); if (this->InformationInput) { this->InformationInput->UpdateInformation(); this->InformationInput->GetWholeExtent(inWholeExt); this->InformationInput->GetSpacing(inSpacing); this->InformationInput->GetOrigin(inOrigin); } else { inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), inWholeExt); inInfo->Get(vtkDataObject::SPACING(), inSpacing); inInfo->Get(vtkDataObject::ORIGIN(), inOrigin); } // reslice axes matrix is identity by default double matrix[4][4]; double imatrix[4][4]; for (i = 0; i < 4; i++) { matrix[i][0] = matrix[i][1] = matrix[i][2] = matrix[i][3] = 0; matrix[i][i] = 1; imatrix[i][0] = imatrix[i][1] = imatrix[i][2] = imatrix[i][3] = 0; imatrix[i][i] = 1; } if (this->ResliceAxes) { vtkMatrix4x4::DeepCopy(*matrix, this->ResliceAxes); vtkMatrix4x4::Invert(*matrix,*imatrix); } if (this->AutoCropOutput) { this->GetAutoCroppedOutputBounds(inInfo, maxBounds); } // pass the center of the volume through the inverse of the // 3x3 direction cosines matrix double inCenter[3]; for (i = 0; i < 3; i++) { inCenter[i] = inOrigin[i] + \ 0.5*(inWholeExt[2*i] + inWholeExt[2*i+1])*inSpacing[i]; } // the default spacing, extent and origin are the input spacing, extent // and origin, transformed by the direction cosines of the ResliceAxes // if requested (note that the transformed output spacing will always // be positive) for (i = 0; i < 3; i++) { double s = 0; // default output spacing double d = 0; // default linear dimension double e = 0; // default extent start double c = 0; // transformed center-of-volume if (this->TransformInputSampling) { double r = 0.0; for (j = 0; j < 3; j++) { c += imatrix[i][j]*(inCenter[j] - matrix[j][3]); double tmp = matrix[j][i]*matrix[j][i]; s += tmp*fabs(inSpacing[j]); d += tmp*(inWholeExt[2*j+1] - inWholeExt[2*j])*fabs(inSpacing[j]); e += tmp*inWholeExt[2*j]; r += tmp; } s /= r; d /= r*sqrt(r); e /= r; } else { s = inSpacing[i]; d = (inWholeExt[2*i+1] - inWholeExt[2*i])*s; e = inWholeExt[2*i]; } if (this->OutputSpacing[i] == VTK_DOUBLE_MAX) { outSpacing[i] = s; } else { outSpacing[i] = this->OutputSpacing[i]; } if (i >= this->OutputDimensionality) { outWholeExt[2*i] = 0; outWholeExt[2*i+1] = 0; } else if (this->OutputExtent[2*i] == VTK_INT_MIN || this->OutputExtent[2*i+1] == VTK_INT_MAX) { if (this->AutoCropOutput) { d = maxBounds[2*i+1] - maxBounds[2*i]; } outWholeExt[2*i] = vtkResliceRound(e); outWholeExt[2*i+1] = vtkResliceRound(outWholeExt[2*i] + fabs(d/outSpacing[i])); } else { outWholeExt[2*i] = this->OutputExtent[2*i]; outWholeExt[2*i+1] = this->OutputExtent[2*i+1]; } if (i >= this->OutputDimensionality) { outOrigin[i] = 0; } else if (this->OutputOrigin[i] == VTK_DOUBLE_MAX) { if (this->AutoCropOutput) { // set origin so edge of extent is edge of bounds outOrigin[i] = maxBounds[2*i] - outWholeExt[2*i]*outSpacing[i]; } else { // center new bounds over center of input bounds outOrigin[i] = c - \ 0.5*(outWholeExt[2*i] + outWholeExt[2*i+1])*outSpacing[i]; } } else { outOrigin[i] = this->OutputOrigin[i]; } } outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(),outWholeExt,6); outInfo->Set(vtkDataObject::SPACING(), outSpacing, 3); outInfo->Set(vtkDataObject::ORIGIN(), outOrigin, 3); this->GetIndexMatrix(inInfo, outInfo); if (this->GetNumberOfInputConnections(1) > 0) { vtkInformation *stencilInfo = inputVector[1]->GetInformationObject(0); stencilInfo->Set(vtkDataObject::SPACING(), inInfo->Get(vtkDataObject::SPACING()), 3); stencilInfo->Set(vtkDataObject::ORIGIN(), inInfo->Get(vtkDataObject::ORIGIN()), 3); } return 1; } //---------------------------------------------------------------------------- // Interpolation subroutines and associated code //---------------------------------------------------------------------------- // Three interpolation functions are supported: NearestNeighbor, Trilinear, // and Tricubic. These routines have the following signature: // //int interpolate(T *&outPtr, // const T *inPtr, // const int inExt[6], // const vtkIdType inInc[3], // int numscalars, // const F point[3], // int mode, // const T *background) // // where 'T' is any arithmetic type and 'F' is a float type // // The result of the interpolation is put in *outPtr, and outPtr is // incremented. //---------------------------------------------------------------------------- // constants for different boundary-handling modes #define VTK_RESLICE_BACKGROUND 0 // use background if out-of-bounds #define VTK_RESLICE_WRAP 1 // wrap to opposite side of image #define VTK_RESLICE_MIRROR 2 // mirror off of the boundary #define VTK_RESLICE_BORDER 3 // use a half-voxel border #define VTK_RESLICE_NULL 4 // do nothing to *outPtr if out-of-bounds //---------------------------------------------------------------------------- // rounding functions for each type, where 'F' is a floating-point type #if (VTK_USE_INT8 != 0) template inline void vtkResliceRound(F val, vtkTypeInt8& rnd) { rnd = vtkResliceRound(val); } #endif #if (VTK_USE_UINT8 != 0) template inline void vtkResliceRound(F val, vtkTypeUInt8& rnd) { rnd = vtkResliceRound(val); } #endif #if (VTK_USE_INT16 != 0) template inline void vtkResliceRound(F val, vtkTypeInt16& rnd) { rnd = vtkResliceRound(val); } #endif #if (VTK_USE_UINT16 != 0) template inline void vtkResliceRound(F val, vtkTypeUInt16& rnd) { rnd = vtkResliceRound(val); } #endif #if (VTK_USE_INT32 != 0) template inline void vtkResliceRound(F val, vtkTypeInt32& rnd) { rnd = vtkResliceRound(val); } #endif #if (VTK_USE_UINT32 != 0) template inline void vtkResliceRound(F val, vtkTypeUInt32& rnd) { rnd = vtkResliceRound(val); } #endif #if (VTK_USE_FLOAT32 != 0) template inline void vtkResliceRound(F val, vtkTypeFloat32& rnd) { rnd = val; } #endif #if (VTK_USE_FLOAT64 != 0) template inline void vtkResliceRound(F val, vtkTypeFloat64& rnd) { rnd = val; } #endif //---------------------------------------------------------------------------- // clamping functions for each type #if (VTK_USE_INT8 != 0) template inline void vtkResliceClamp(F val, vtkTypeInt8& clamp) { if (val < -128.0) { val = -128.0; } if (val > 127.0) { val = 127.0; } vtkResliceRound(val,clamp); } #endif #if (VTK_USE_UINT8 != 0) template inline void vtkResliceClamp(F val, vtkTypeUInt8& clamp) { if (val < 0) { val = 0; } if (val > 255.0) { val = 255.0; } vtkResliceRound(val,clamp); } #endif #if (VTK_USE_INT16 != 0) template inline void vtkResliceClamp(F val, vtkTypeInt16& clamp) { if (val < -32768.0) { val = -32768.0; } if (val > 32767.0) { val = 32767.0; } vtkResliceRound(val,clamp); } #endif #if (VTK_USE_UINT16 != 0) template inline void vtkResliceClamp(F val, vtkTypeUInt16& clamp) { if (val < 0) { val = 0; } if (val > 65535.0) { val = 65535.0; } vtkResliceRound(val,clamp); } #endif #if (VTK_USE_INT32 != 0) template inline void vtkResliceClamp(F val, vtkTypeInt32& clamp) { if (val < -2147483648.0) { val = -2147483648.0; } if (val > 2147483647.0) { val = 2147483647.0; } vtkResliceRound(val,clamp); } #endif #if (VTK_USE_UINT32 != 0) template inline void vtkResliceClamp(F val, vtkTypeUInt32& clamp) { if (val < 0) { val = 0; } if (val > 4294967295.0) { val = 4294967295.0; } vtkResliceRound(val,clamp); } #endif #if (VTK_USE_FLOAT32 != 0) template inline void vtkResliceClamp(F val, vtkTypeFloat32& clamp) { clamp = val; } #endif #if (VTK_USE_FLOAT64 != 0) template inline void vtkResliceClamp(F val, vtkTypeFloat64& clamp) { clamp = val; } #endif //---------------------------------------------------------------------------- // Perform a wrap to limit an index to [0,range). // Ensures correct behaviour when the index is negative. inline int vtkInterpolateWrap(int num, int range) { if ((num %= range) < 0) { num += range; // required for some % implementations } return num; } //---------------------------------------------------------------------------- // Perform a mirror to limit an index to [0,range). inline int vtkInterpolateMirror(int num, int range) { if (num < 0) { num = -num - 1; } int count = num/range; num %= range; if (count & 0x1) { num = range - num - 1; } return num; } //---------------------------------------------------------------------------- // If the value is within one half voxel of the range [0,inExtX), then // set it to "0" or "inExtX-1" as appropriate. inline int vtkInterpolateBorder(int &inIdX0, int &inIdX1, int inExtX, double fx) { if (inIdX0 >= 0 && inIdX1 < inExtX) { return 0; } if (inIdX0 == -1 && fx >= 0.5) { inIdX1 = inIdX0 = 0; return 0; } if (inIdX0 == inExtX - 1 && fx < 0.5) { inIdX1 = inIdX0; return 0; } return 1; } inline int vtkInterpolateBorderCheck(int inIdX0, int inIdX1, int inExtX, double fx) { if ((inIdX0 >= 0 && inIdX1 < inExtX) || (inIdX0 == -1 && fx >= 0.5) || (inIdX0 == inExtX - 1 && fx < 0.5)) { return 0; } return 1; } //---------------------------------------------------------------------------- // Do nearest-neighbor interpolation of the input data 'inPtr' of extent // 'inExt' at the 'point'. The result is placed at 'outPtr'. // If the lookup data is beyond the extent 'inExt', set 'outPtr' to // the background color 'background'. // The number of scalar components in the data is 'numscalars' template int vtkNearestNeighborInterpolation(T *&outPtr, const T *inPtr, const int inExt[6], const vtkIdType inInc[3], int numscalars, const F point[3], int mode, const T *background) { int inIdX0 = vtkResliceRound(point[0]) - inExt[0]; int inIdY0 = vtkResliceRound(point[1]) - inExt[2]; int inIdZ0 = vtkResliceRound(point[2]) - inExt[4]; int inExtX = inExt[1] - inExt[0] + 1; int inExtY = inExt[3] - inExt[2] + 1; int inExtZ = inExt[5] - inExt[4] + 1; if (inIdX0 < 0 || inIdX0 >= inExtX || inIdY0 < 0 || inIdY0 >= inExtY || inIdZ0 < 0 || inIdZ0 >= inExtZ) { if (mode == VTK_RESLICE_WRAP) { inIdX0 = vtkInterpolateWrap(inIdX0, inExtX); inIdY0 = vtkInterpolateWrap(inIdY0, inExtY); inIdZ0 = vtkInterpolateWrap(inIdZ0, inExtZ); } else if (mode == VTK_RESLICE_MIRROR) { inIdX0 = vtkInterpolateMirror(inIdX0, inExtX); inIdY0 = vtkInterpolateMirror(inIdY0, inExtY); inIdZ0 = vtkInterpolateMirror(inIdZ0, inExtZ); } else if (mode == VTK_RESLICE_BACKGROUND || mode == VTK_RESLICE_BORDER) { do { *outPtr++ = *background++; } while (--numscalars); return 0; } else { return 0; } } inPtr += inIdX0*inInc[0]+inIdY0*inInc[1]+inIdZ0*inInc[2]; do { *outPtr++ = *inPtr++; } while (--numscalars); return 1; } //---------------------------------------------------------------------------- // Do trilinear interpolation of the input data 'inPtr' of extent 'inExt' // at the 'point'. The result is placed at 'outPtr'. // If the lookup data is beyond the extent 'inExt', set 'outPtr' to // the background color 'background'. // The number of scalar components in the data is 'numscalars' template int vtkTrilinearInterpolation(T *&outPtr, const T *inPtr, const int inExt[6], const vtkIdType inInc[3], int numscalars, const F point[3], int mode, const T *background) { F fx, fy, fz; int floorX = vtkResliceFloor(point[0], fx); int floorY = vtkResliceFloor(point[1], fy); int floorZ = vtkResliceFloor(point[2], fz); int inIdX0 = floorX - inExt[0]; int inIdY0 = floorY - inExt[2]; int inIdZ0 = floorZ - inExt[4]; int inIdX1 = inIdX0 + (fx != 0); int inIdY1 = inIdY0 + (fy != 0); int inIdZ1 = inIdZ0 + (fz != 0); int inExtX = inExt[1] - inExt[0] + 1; int inExtY = inExt[3] - inExt[2] + 1; int inExtZ = inExt[5] - inExt[4] + 1; if (inIdX0 < 0 || inIdX1 >= inExtX || inIdY0 < 0 || inIdY1 >= inExtY || inIdZ0 < 0 || inIdZ1 >= inExtZ) { if (mode == VTK_RESLICE_BORDER) { if (vtkInterpolateBorder(inIdX0, inIdX1, inExtX, fx) || vtkInterpolateBorder(inIdY0, inIdY1, inExtY, fy) || vtkInterpolateBorder(inIdZ0, inIdZ1, inExtZ, fz)) { do { *outPtr++ = *background++; } while (--numscalars); return 0; } } else if (mode == VTK_RESLICE_WRAP) { inIdX0 = vtkInterpolateWrap(inIdX0, inExtX); inIdY0 = vtkInterpolateWrap(inIdY0, inExtY); inIdZ0 = vtkInterpolateWrap(inIdZ0, inExtZ); inIdX1 = vtkInterpolateWrap(inIdX1, inExtX); inIdY1 = vtkInterpolateWrap(inIdY1, inExtY); inIdZ1 = vtkInterpolateWrap(inIdZ1, inExtZ); } else if (mode == VTK_RESLICE_MIRROR) { inIdX0 = vtkInterpolateMirror(inIdX0, inExtX); inIdY0 = vtkInterpolateMirror(inIdY0, inExtY); inIdZ0 = vtkInterpolateMirror(inIdZ0, inExtZ); inIdX1 = vtkInterpolateMirror(inIdX1, inExtX); inIdY1 = vtkInterpolateMirror(inIdY1, inExtY); inIdZ1 = vtkInterpolateMirror(inIdZ1, inExtZ); } else if (mode == VTK_RESLICE_BACKGROUND) { do { *outPtr++ = *background++; } while (--numscalars); return 0; } else { return 0; } } vtkIdType factX0 = inIdX0*inInc[0]; vtkIdType factX1 = inIdX1*inInc[0]; vtkIdType factY0 = inIdY0*inInc[1]; vtkIdType factY1 = inIdY1*inInc[1]; vtkIdType factZ0 = inIdZ0*inInc[2]; vtkIdType factZ1 = inIdZ1*inInc[2]; vtkIdType i00 = factY0 + factZ0; vtkIdType i01 = factY0 + factZ1; vtkIdType i10 = factY1 + factZ0; vtkIdType i11 = factY1 + factZ1; F rx = 1 - fx; F ry = 1 - fy; F rz = 1 - fz; F ryrz = ry*rz; F fyrz = fy*rz; F ryfz = ry*fz; F fyfz = fy*fz; const T *inPtr0 = inPtr + factX0; const T *inPtr1 = inPtr + factX1; do { F result = (rx*(ryrz*inPtr0[i00] + ryfz*inPtr0[i01] + fyrz*inPtr0[i10] + fyfz*inPtr0[i11]) + fx*(ryrz*inPtr1[i00] + ryfz*inPtr1[i01] + fyrz*inPtr1[i10] + fyfz*inPtr1[i11])); vtkResliceRound(result, *outPtr++); inPtr0++; inPtr1++; } while (--numscalars); return 1; } //---------------------------------------------------------------------------- // Do tricubic interpolation of the input data 'inPtr' of extent 'inExt' // at the 'point'. The result is placed at 'outPtr'. // The number of scalar components in the data is 'numscalars' // The tricubic interpolation ensures that both the intensity and // the first derivative of the intensity are smooth across the // image. The first derivative is estimated using a // centered-difference calculation. // helper function: set up the lookup indices and the interpolation // coefficients template void vtkTricubicInterpCoeffs(T F[4], int l, int h, T f) { static const T half = T(0.5); int order = h - l; if (order == 0) { // no interpolation F[0] = 0; F[1] = 1; F[2] = 0; F[3] = 0; return; } if (order == 3) { // cubic interpolation T fm1 = f - 1; T fd2 = f*half; T ft3 = f*3; F[0] = -fd2*fm1*fm1; F[1] = ((ft3 - 2)*fd2 - 1)*fm1; F[2] = -((ft3 - 4)*f - 1)*fd2; F[3] = f*fd2*fm1; return; } if (order == 1) { // linear interpolation F[0] = 0; F[1] = 1 - f; F[2] = f; F[3] = 0; return; } if (l == 0) { // quadratic interpolation T fp1 = f + 1; T fm1 = f - 1; T fd2 = f*half; F[0] = fd2*fm1; F[1] = -fp1*fm1; F[2] = fp1*fd2; F[3] = 0; return; } // else { // quadratic interpolation T fm1 = f - 1; T fm2 = fm1 - 1; T fm1d2 = fm1*half; F[0] = 0; F[1] = fm1d2*fm2; F[2] = -f*fm2; F[3] = f*fm1d2; return; } } // tricubic interpolation template int vtkTricubicInterpolation(T *&outPtr, const T *inPtr, const int inExt[6], const vtkIdType inInc[3], int numscalars, const F point[3], int mode, const T *background) { F fx, fy, fz; int floorX = vtkResliceFloor(point[0], fx); int floorY = vtkResliceFloor(point[1], fy); int floorZ = vtkResliceFloor(point[2], fz); int fxIsNotZero = (fx != 0); int fyIsNotZero = (fy != 0); int fzIsNotZero = (fz != 0); int inIdX0 = floorX - inExt[0]; int inIdY0 = floorY - inExt[2]; int inIdZ0 = floorZ - inExt[4]; int inIdX1 = inIdX0 + fxIsNotZero; int inIdY1 = inIdY0 + fyIsNotZero; int inIdZ1 = inIdZ0 + fzIsNotZero; int inExtX = inExt[1] - inExt[0] + 1; int inExtY = inExt[3] - inExt[2] + 1; int inExtZ = inExt[5] - inExt[4] + 1; vtkIdType inIncX = inInc[0]; vtkIdType inIncY = inInc[1]; vtkIdType inIncZ = inInc[2]; vtkIdType factX[4], factY[4], factZ[4]; if (inIdX0 < 0 || inIdX1 >= inExtX || inIdY0 < 0 || inIdY1 >= inExtY || inIdZ0 < 0 || inIdZ1 >= inExtZ) { if (mode == VTK_RESLICE_BORDER) { if (vtkInterpolateBorderCheck(inIdX0, inIdX1, inExtX, fx) || vtkInterpolateBorderCheck(inIdY0, inIdY1, inExtY, fy) || vtkInterpolateBorderCheck(inIdZ0, inIdZ1, inExtZ, fz)) { do { *outPtr++ = *background++; } while (--numscalars); return 0; } } else if (mode != VTK_RESLICE_WRAP && mode != VTK_RESLICE_MIRROR) { if (mode == VTK_RESLICE_BACKGROUND) { do { *outPtr++ = *background++; } while (--numscalars); return 0; } else { return 0; } } } F fX[4], fY[4], fZ[4]; int k1, k2, j1, j2, i1, i2; if (mode == VTK_RESLICE_WRAP || mode == VTK_RESLICE_MIRROR) { i1 = 0; i2 = 3; vtkTricubicInterpCoeffs(fX, i1, i2, fx); j1 = 1 - fyIsNotZero; j2 = 1 + (fyIsNotZero<<1); vtkTricubicInterpCoeffs(fY, j1, j2, fy); k1 = 1 - fzIsNotZero; k2 = 1 + (fzIsNotZero<<1); vtkTricubicInterpCoeffs(fZ, k1, k2, fz); if (mode == VTK_RESLICE_WRAP) { for (int i = 0; i < 4; i++) { factX[i] = vtkInterpolateWrap(inIdX0 + i - 1, inExtX)*inIncX; factY[i] = vtkInterpolateWrap(inIdY0 + i - 1, inExtY)*inIncY; factZ[i] = vtkInterpolateWrap(inIdZ0 + i - 1, inExtZ)*inIncZ; } } else { for (int i = 0; i < 4; i++) { factX[i] = vtkInterpolateMirror(inIdX0 + i - 1, inExtX)*inIncX; factY[i] = vtkInterpolateMirror(inIdY0 + i - 1, inExtY)*inIncY; factZ[i] = vtkInterpolateMirror(inIdZ0 + i - 1, inExtZ)*inIncZ; } } } else if (mode == VTK_RESLICE_BORDER) { // clamp to the border of the input extent i1 = 1 - fxIsNotZero; j1 = 1 - fyIsNotZero; k1 = 1 - fzIsNotZero; i2 = 1 + 2*fxIsNotZero; j2 = 1 + 2*fyIsNotZero; k2 = 1 + 2*fzIsNotZero; vtkTricubicInterpCoeffs(fX, i1, i2, fx); vtkTricubicInterpCoeffs(fY, j1, j2, fy); vtkTricubicInterpCoeffs(fZ, k1, k2, fz); int tmpExt = inExtX - 1; int tmpId = tmpExt - inIdX0 - 1; factX[0] = (inIdX0 - 1)*(inIdX0 - 1 >= 0)*inIncX; factX[1] = (inIdX0)*(inIdX0 >= 0)*inIncX; factX[2] = (tmpExt - (tmpId)*(tmpId >= 0))*inIncX; factX[3] = (tmpExt - (tmpId - 1)*(tmpId - 1 >= 0))*inIncX; tmpExt = inExtY - 1; tmpId = tmpExt - inIdY0 - 1; factY[0] = (inIdY0 - 1)*(inIdY0 - 1 >= 0)*inIncY; factY[1] = (inIdY0)*(inIdY0 >= 0)*inIncY; factY[2] = (tmpExt - (tmpId)*(tmpId >= 0))*inIncY; factY[3] = (tmpExt - (tmpId - 1)*(tmpId - 1 >= 0))*inIncY; tmpExt = inExtZ - 1; tmpId = tmpExt - inIdZ0 - 1; factZ[0] = (inIdZ0 - 1)*(inIdZ0 - 1 >= 0)*inIncZ; factZ[1] = (inIdZ0)*(inIdZ0 >= 0)*inIncZ; factZ[2] = (tmpExt - (tmpId)*(tmpId >= 0))*inIncZ; factZ[3] = (tmpExt - (tmpId - 1)*(tmpId - 1 >= 0))*inIncZ; } else { // depending on whether we are at the edge of the // input extent, choose the appropriate interpolation // method to use i1 = 1 - (inIdX0 > 0)*fxIsNotZero; j1 = 1 - (inIdY0 > 0)*fyIsNotZero; k1 = 1 - (inIdZ0 > 0)*fzIsNotZero; i2 = 1 + (1 + (inIdX0 + 2 < inExtX))*fxIsNotZero; j2 = 1 + (1 + (inIdY0 + 2 < inExtY))*fyIsNotZero; k2 = 1 + (1 + (inIdZ0 + 2 < inExtZ))*fzIsNotZero; vtkTricubicInterpCoeffs(fX, i1, i2, fx); vtkTricubicInterpCoeffs(fY, j1, j2, fy); vtkTricubicInterpCoeffs(fZ, k1, k2, fz); factX[1] = inIdX0*inIncX; factX[0] = factX[1] - inIncX; factX[2] = factX[1] + inIncX; factX[3] = factX[2] + inIncX; factY[1] = inIdY0*inIncY; factY[0] = factY[1] - inIncY; factY[2] = factY[1] + inIncY; factY[3] = factY[2] + inIncY; factZ[1] = inIdZ0*inIncZ; factZ[0] = factZ[1] - inIncZ; factZ[2] = factZ[1] + inIncZ; factZ[3] = factZ[2] + inIncZ; // this little bit of wierdness allows us to unroll the x loop if (i1 > 0) { factX[0] = factX[1]; } if (i2 < 3) { factX[3] = factX[1]; if (i2 < 2) { factX[2] = factX[1]; } } } do // loop over components { F val = 0; int k = k1; do // loop over z { F ifz = fZ[k]; vtkIdType factz = factZ[k]; int j = j1; do // loop over y { F ify = fY[j]; F fzy = ifz*ify; vtkIdType factzy = factz + factY[j]; const T *tmpPtr = inPtr + factzy; // loop over x is unrolled (significant performance boost) val += fzy*(fX[0]*tmpPtr[factX[0]] + fX[1]*tmpPtr[factX[1]] + fX[2]*tmpPtr[factX[2]] + fX[3]*tmpPtr[factX[3]]); } while (++j <= j2); } while (++k <= k2); vtkResliceClamp(val, *outPtr++); inPtr++; } while (--numscalars); return 1; } //-------------------------------------------------------------------------- // get appropriate interpolation function according to interpolation mode // and scalar type template void vtkGetResliceInterpFunc(vtkImageReslice *self, int (**interpolate)(void *&outPtr, const void *inPtr, const int inExt[6], const vtkIdType inInc[3], int numscalars, const F point[3], int mode, const void *background)) { int dataType = self->GetOutput()->GetScalarType(); int interpolationMode = self->GetInterpolationMode(); switch (interpolationMode) { case VTK_RESLICE_NEAREST: switch (dataType) { vtkTemplateAliasMacro(*((int (**)(VTK_TT *&outPtr, const VTK_TT *inPtr, const int inExt[6], const vtkIdType inInc[3], int numscalars, const F point[3], int mode, const VTK_TT *background))interpolate) = \ &vtkNearestNeighborInterpolation); default: interpolate = 0; } break; case VTK_RESLICE_LINEAR: switch (dataType) { vtkTemplateAliasMacro(*((int (**)(VTK_TT *&outPtr, const VTK_TT *inPtr, const int inExt[6], const vtkIdType inInc[3], int numscalars, const F point[3], int mode, const VTK_TT *background))interpolate) = \ &vtkTrilinearInterpolation); default: interpolate = 0; } break; case VTK_RESLICE_CUBIC: switch (dataType) { vtkTemplateAliasMacro(*((int (**)(VTK_TT *&outPtr, const VTK_TT *inPtr, const int inExt[6], const vtkIdType inInc[3], int numscalars, const F point[3], int mode, const VTK_TT *background))interpolate) = \ &vtkTricubicInterpolation); default: interpolate = 0; } break; default: interpolate = 0; } } //---------------------------------------------------------------------------- // Some helper functions for 'RequestData' //---------------------------------------------------------------------------- //-------------------------------------------------------------------------- // pixel copy function, templated for different scalar types template void vtkSetPixels(T *&outPtr, const T *inPtr, int numscalars, int n) { for (int i = 0; i < n; i++) { const T *tmpPtr = inPtr; int m = numscalars; do { *outPtr++ = *tmpPtr++; } while (--m); } } // optimized for 1 scalar components template void vtkSetPixels1(T *&outPtr, const T *inPtr, int vtkNotUsed(numscalars), int n) { T val = *inPtr; for (int i = 0; i < n; i++) { *outPtr++ = val; } } // get a pixel copy function that is appropriate for the data type void vtkGetSetPixelsFunc(vtkImageReslice *self, void (**setpixels)(void *&out, const void *in, int numscalars, int n)) { int dataType = self->GetOutput()->GetScalarType(); int numscalars = self->GetOutput()->GetNumberOfScalarComponents(); switch (numscalars) { case 1: switch (dataType) { vtkTemplateAliasMacro(*((void (**)(VTK_TT *&out, const VTK_TT *in, int numscalars, int n))setpixels) = \ vtkSetPixels1); default: setpixels = 0; } default: switch (dataType) { vtkTemplateAliasMacro(*((void (**)(VTK_TT *&out, const VTK_TT *in, int numscalars, int n))setpixels) = \ vtkSetPixels); default: setpixels = 0; } } } //---------------------------------------------------------------------------- // Convert background color from float to appropriate type template void vtkAllocBackgroundPixelT(vtkImageReslice *self, T **background_ptr, int numComponents) { *background_ptr = new T[numComponents]; T *background = *background_ptr; for (int i = 0; i < numComponents; i++) { if (i < 4) { vtkResliceClamp(self->GetBackgroundColor()[i], background[i]); } else { background[i] = 0; } } } void vtkAllocBackgroundPixel(vtkImageReslice *self, void **rval, int numComponents) { switch (self->GetOutput()->GetScalarType()) { vtkTemplateAliasMacro(vtkAllocBackgroundPixelT(self, (VTK_TT **)rval, numComponents)); } } void vtkFreeBackgroundPixel(vtkImageReslice *self, void **rval) { switch (self->GetOutput()->GetScalarType()) { vtkTemplateAliasMacro(delete [] *((VTK_TT **)rval)); } *rval = 0; } //---------------------------------------------------------------------------- // helper function for clipping of the output with a stencil int vtkResliceGetNextExtent(vtkImageStencilData *stencil, int &r1, int &r2, int rmin, int rmax, int yIdx, int zIdx, void *&outPtr, void *background, int numscalars, void (*setpixels)(void *&out, const void *in, int numscalars, int n), int &iter) { // trivial case if stencil is not set if (!stencil) { if (iter++ == 0) { r1 = rmin; r2 = rmax; return 1; } return 0; } // for clearing, start at last r2 plus 1 int clear1 = r2 + 1; if (iter == 0) { // if no 'last time', start at rmin clear1 = rmin; } int rval = stencil->GetNextExtent(r1, r2, rmin, rmax, yIdx, zIdx, iter); int clear2 = r1 - 1; if (rval == 0) { clear2 = rmax; } setpixels(outPtr, background, numscalars, clear2 - clear1 + 1); return rval; } //---------------------------------------------------------------------------- // This function simply clears the entire output to the background color, // for cases where the transformation places the output extent completely // outside of the input extent. void vtkImageResliceClearExecute(vtkImageReslice *self, vtkImageData *, void *, vtkImageData *outData, void *outPtr, int outExt[6], int id) { int numscalars; int idY, idZ; vtkIdType outIncX, outIncY, outIncZ; int scalarSize; unsigned long count = 0; unsigned long target; void *background; void (*setpixels)(void *&out, const void *in, int numscalars, int n); // for the progress meter target = (unsigned long) ((outExt[5]-outExt[4]+1)*(outExt[3]-outExt[2]+1)/50.0); target++; // Get Increments to march through data outData->GetContinuousIncrements(outExt, outIncX, outIncY, outIncZ); scalarSize = outData->GetScalarSize(); numscalars = outData->GetNumberOfScalarComponents(); // allocate a voxel to copy into the background (out-of-bounds) regions vtkAllocBackgroundPixel(self, &background, numscalars); // get the appropriate function for pixel copying vtkGetSetPixelsFunc(self, &setpixels); // Loop through output voxels for (idZ = outExt[4]; idZ <= outExt[5]; idZ++) { for (idY = outExt[2]; idY <= outExt[3]; idY++) { if (id == 0) { // update the progress if this is the main thread if (!(count%target)) { self->UpdateProgress(count/(50.0*target)); } count++; } // clear the pixels to background color and go to next row setpixels(outPtr, background, numscalars, outExt[1]-outExt[0]+1); outPtr = (void *)((char *)outPtr + outIncY*scalarSize); } outPtr = (void *)((char *)outPtr + outIncZ*scalarSize); } vtkFreeBackgroundPixel(self, &background); } //---------------------------------------------------------------------------- // This function executes the filter for any type of data. It is much simpler // in structure than vtkImageResliceOptimizedExecute. void vtkImageResliceExecute(vtkImageReslice *self, vtkImageData *inData, void *inPtr, vtkImageData *outData, void *outPtr, int outExt[6], int id) { int numscalars; int idX, idY, idZ; int idXmin, idXmax, iter; vtkIdType outIncX, outIncY, outIncZ; int scalarSize; int inExt[6]; vtkIdType inInc[3]; unsigned long count = 0; unsigned long target; double point[4]; double f; double *inSpacing, *inOrigin, *outSpacing, *outOrigin, inInvSpacing[3]; void *background; int (*interpolate)(void *&outPtr, const void *inPtr, const int inExt[6], const vtkIdType inInc[3], int numscalars, const double point[3], int mode, const void *background); void (*setpixels)(void *&out, const void *in, int numscalars, int n); // the 'mode' species what to do with the 'pad' (out-of-bounds) area int mode = VTK_RESLICE_BACKGROUND; if (self->GetMirror()) { mode = VTK_RESLICE_MIRROR; } else if (self->GetWrap()) { mode = VTK_RESLICE_WRAP; } else if (self->GetBorder()) { mode = VTK_RESLICE_BORDER; } // the transformation to apply to the data vtkAbstractTransform *transform = self->GetResliceTransform(); vtkMatrix4x4 *matrix = self->GetResliceAxes(); // for conversion to data coordinates inOrigin = inData->GetOrigin(); inSpacing = inData->GetSpacing(); outOrigin = outData->GetOrigin(); outSpacing = outData->GetSpacing(); // save effor later: invert inSpacing inInvSpacing[0] = 1.0/inSpacing[0]; inInvSpacing[1] = 1.0/inSpacing[1]; inInvSpacing[2] = 1.0/inSpacing[2]; // find maximum input range inData->GetExtent(inExt); // for the progress meter target = (unsigned long) ((outExt[5]-outExt[4]+1)*(outExt[3]-outExt[2]+1)/50.0); target++; // Get Increments to march through data inData->GetIncrements(inInc); outData->GetContinuousIncrements(outExt, outIncX, outIncY, outIncZ); scalarSize = outData->GetScalarSize(); numscalars = inData->GetNumberOfScalarComponents(); // allocate a voxel to copy into the background (out-of-bounds) regions vtkAllocBackgroundPixel(self, &background, numscalars); // get the appropriate functions for interpolation and pixel copying vtkGetResliceInterpFunc(self, &interpolate); vtkGetSetPixelsFunc(self, &setpixels); // get the stencil vtkImageStencilData *stencil = self->GetStencil(); // Loop through output voxels for (idZ = outExt[4]; idZ <= outExt[5]; idZ++) { for (idY = outExt[2]; idY <= outExt[3]; idY++) { if (id == 0) { // update the progress if this is the main thread if (!(count%target)) { self->UpdateProgress(count/(50.0*target)); } count++; } iter = 0; // if there is a stencil, it is applied here while (vtkResliceGetNextExtent(stencil, idXmin, idXmax, outExt[0], outExt[1], idY, idZ, outPtr, background, numscalars, setpixels, iter)) { for (idX = idXmin; idX <= idXmax; idX++) { // convert to data coordinates point[0] = idX*outSpacing[0] + outOrigin[0]; point[1] = idY*outSpacing[1] + outOrigin[1]; point[2] = idZ*outSpacing[2] + outOrigin[2]; // apply ResliceAxes matrix if (matrix) { point[3] = 1.0; matrix->MultiplyPoint(point, point); f = 1.0/point[3]; point[0] *= f; point[1] *= f; point[2] *= f; } // apply ResliceTransform if (transform) { transform->InternalTransformPoint(point, point); } // convert back to voxel indices point[0] = (point[0] - inOrigin[0])*inInvSpacing[0]; point[1] = (point[1] - inOrigin[1])*inInvSpacing[1]; point[2] = (point[2] - inOrigin[2])*inInvSpacing[2]; // interpolate output voxel from input data set interpolate(outPtr, inPtr, inExt, inInc, numscalars, point, mode, background); } } outPtr = (void *)((char *)outPtr + outIncY*scalarSize); } outPtr = (void *)((char *)outPtr + outIncZ*scalarSize); } vtkFreeBackgroundPixel(self, &background); } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // The remainder of this file is the 'optimized' version of the code. //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // application of the transform has different forms for fixed-point // vs. floating-point template inline void vtkResliceApplyTransform(vtkAbstractTransform *newtrans, F inPoint[3], F inOrigin[3], F inInvSpacing[3]) { newtrans->InternalTransformPoint(inPoint, inPoint); inPoint[0] -= inOrigin[0]; inPoint[1] -= inOrigin[1]; inPoint[2] -= inOrigin[2]; inPoint[0] *= inInvSpacing[0]; inPoint[1] *= inInvSpacing[1]; inPoint[2] *= inInvSpacing[2]; } // The vtkOptimizedExecute() is like vtkImageResliceExecute, except that // it provides a few optimizations: // 1) the ResliceAxes and ResliceTransform are joined to create a // single 4x4 matrix if possible // 2) the transformation is calculated incrementally to increase efficiency // 3) nearest-neighbor interpolation is treated specially in order to // increase efficiency template void vtkOptimizedExecute(vtkImageReslice *self, vtkImageData *inData, void *inPtr, vtkImageData *outData, void *outPtr, int outExt[6], int id, F newmat[4][4], vtkAbstractTransform *newtrans) { int i, numscalars; int idX, idY, idZ; vtkIdType outIncX, outIncY, outIncZ; int scalarSize; int inExt[6]; vtkIdType inInc[3]; unsigned long count = 0; unsigned long target; int iter, idXmin, idXmax; double temp[3]; F inOrigin[3], inInvSpacing[3]; F xAxis[4], yAxis[4], zAxis[4], origin[4]; F inPoint0[4]; F inPoint1[4]; F inPoint[4], f; void *background; int (*interpolate)(void *&outPtr, const void *inPtr, const int inExt[6], const vtkIdType inInc[3], int numscalars, const F point[3], int mode, const void *background); void (*setpixels)(void *&out, const void *in, int numscalars, int n); int mode = VTK_RESLICE_BACKGROUND; int wrap = 0; if (self->GetMirror()) { mode = VTK_RESLICE_MIRROR; wrap = 1; } else if (self->GetWrap()) { mode = VTK_RESLICE_WRAP; wrap = 1; } else if (self->GetBorder()) { mode = VTK_RESLICE_BORDER; } int perspective = 0; if (newmat[3][0] != 0 || newmat[3][1] != 0 || newmat[3][2] != 0 || newmat[3][3] != 1) { perspective = 1; } int optimizeNearest = 0; if (self->GetInterpolationMode() == VTK_RESLICE_NEAREST && !(wrap || newtrans || perspective)) { optimizeNearest = 1; } // find maximum input range inData->GetExtent(inExt); target = (unsigned long) ((outExt[5]-outExt[4]+1)*(outExt[3]-outExt[2]+1)/50.0); target++; // Get Increments to march through data inData->GetIncrements(inInc); outData->GetContinuousIncrements(outExt, outIncX, outIncY, outIncZ); scalarSize = outData->GetScalarSize(); numscalars = inData->GetNumberOfScalarComponents(); // break matrix into a set of axes plus an origin // (this allows us to calculate the transform Incrementally) for (i = 0; i < 4; i++) { xAxis[i] = newmat[i][0]; yAxis[i] = newmat[i][1]; zAxis[i] = newmat[i][2]; origin[i] = newmat[i][3]; } // get the input origin and spacing for conversion purposes inData->GetOrigin(temp); inOrigin[0] = F(temp[0]); inOrigin[1] = F(temp[1]); inOrigin[2] = F(temp[2]); inData->GetSpacing(temp); inInvSpacing[0] = F(1.0/temp[0]); inInvSpacing[1] = F(1.0/temp[1]); inInvSpacing[2] = F(1.0/temp[2]); // set color for area outside of input volume extent vtkAllocBackgroundPixel(self, &background, numscalars); // Set interpolation method vtkGetResliceInterpFunc(self, &interpolate); vtkGetSetPixelsFunc(self, &setpixels); // get the stencil vtkImageStencilData *stencil = self->GetStencil(); // Loop through output pixels for (idZ = outExt[4]; idZ <= outExt[5]; idZ++) { inPoint0[0] = origin[0] + idZ*zAxis[0]; // incremental transform inPoint0[1] = origin[1] + idZ*zAxis[1]; inPoint0[2] = origin[2] + idZ*zAxis[2]; inPoint0[3] = origin[3] + idZ*zAxis[3]; for (idY = outExt[2]; idY <= outExt[3]; idY++) { inPoint1[0] = inPoint0[0] + idY*yAxis[0]; // incremental transform inPoint1[1] = inPoint0[1] + idY*yAxis[1]; inPoint1[2] = inPoint0[2] + idY*yAxis[2]; inPoint1[3] = inPoint0[3] + idY*yAxis[3]; if (!id) { if (!(count%target)) { self->UpdateProgress(count/(50.0*target)); } count++; } iter = 0; while (vtkResliceGetNextExtent(stencil, idXmin, idXmax, outExt[0], outExt[1], idY, idZ, outPtr, background, numscalars, setpixels, iter)) { if (!optimizeNearest) { for (idX = idXmin; idX <= idXmax; idX++) { inPoint[0] = inPoint1[0] + idX*xAxis[0]; inPoint[1] = inPoint1[1] + idX*xAxis[1]; inPoint[2] = inPoint1[2] + idX*xAxis[2]; if (perspective) { // only do perspective if necessary inPoint[3] = inPoint1[3] + idX*xAxis[3]; f = 1/inPoint[3]; inPoint[0] *= f; inPoint[1] *= f; inPoint[2] *= f; } if (newtrans) { // apply the AbstractTransform if there is one vtkResliceApplyTransform(newtrans, inPoint, inOrigin, inInvSpacing); } // call the interpolation function interpolate(outPtr, inPtr, inExt, inInc, numscalars, inPoint, mode, background); } } else // optimize for nearest-neighbor interpolation { int inExtX = inExt[1] - inExt[0] + 1; int inExtY = inExt[3] - inExt[2] + 1; int inExtZ = inExt[5] - inExt[4] + 1; for (int iidX = idXmin; iidX <= idXmax; iidX++) { void *inPtrTmp = background; inPoint[0] = inPoint1[0] + iidX*xAxis[0]; inPoint[1] = inPoint1[1] + iidX*xAxis[1]; inPoint[2] = inPoint1[2] + iidX*xAxis[2]; int inIdX = vtkResliceRound(inPoint[0]) - inExt[0]; int inIdY = vtkResliceRound(inPoint[1]) - inExt[2]; int inIdZ = vtkResliceRound(inPoint[2]) - inExt[4]; if (inIdX >= 0 && inIdX < inExtX && inIdY >= 0 && inIdY < inExtY && inIdZ >= 0 && inIdZ < inExtZ) { inPtrTmp = (void *)((char *)inPtr + \ (inIdX*inInc[0] + inIdY*inInc[1] + inIdZ*inInc[2])*scalarSize); } setpixels(outPtr, inPtrTmp, numscalars, 1); } } } outPtr = (void *)((char *)outPtr + outIncY*scalarSize); } outPtr = (void *)((char *)outPtr + outIncZ*scalarSize); } vtkFreeBackgroundPixel(self, &background); } //---------------------------------------------------------------------------- // vtkReslicePermuteExecute is specifically optimized for // cases where the IndexMatrix has only one non-zero component // per row, i.e. when the matrix is permutation+scale+translation. // All of the interpolation coefficients are calculated ahead // of time instead of on a pixel-by-pixel basis. //---------------------------------------------------------------------------- // helper function for nearest neighbor interpolation template void vtkPermuteNearestSummation(T *&outPtr, const T *inPtr, int numscalars, int n, const vtkIdType *iX, const F *, const vtkIdType *iY, const F *, const vtkIdType *iZ, const F *, const int [3]) { const T *inPtr0 = inPtr + iY[0] + iZ[0]; for (int i = 0; i < n; i++) { const T *tmpPtr = &inPtr0[iX[0]]; iX++; int m = numscalars; do { *outPtr++ = *tmpPtr++; } while (--m); } } // ditto, but optimized for numscalars = 1 template void vtkPermuteNearestSummation1(T *&outPtr, const T *inPtr, int, int n, const vtkIdType *iX, const F *, const vtkIdType *iY, const F *, const vtkIdType *iZ, const F *, const int [3]) { const T *inPtr0 = inPtr + iY[0] + iZ[0]; for (int i = 0; i < n; i++) { *outPtr++ = inPtr0[iX[0]]; iX++; } } //---------------------------------------------------------------------------- // helper function for linear interpolation template void vtkPermuteTrilinearSummation(T *&outPtr, const T *inPtr, int numscalars, int n, const vtkIdType *iX, const F *fX, const vtkIdType *iY, const F *fY, const vtkIdType *iZ, const F *fZ, const int useNearestNeighbor[3]) { vtkIdType i00 = iY[0] + iZ[0]; vtkIdType i01 = iY[0] + iZ[1]; vtkIdType i10 = iY[1] + iZ[0]; vtkIdType i11 = iY[1] + iZ[1]; F ry = fY[0]; F fy = fY[1]; F rz = fZ[0]; F fz = fZ[1]; F ryrz = ry*rz; F ryfz = ry*fz; F fyrz = fy*rz; F fyfz = fy*fz; if (useNearestNeighbor[0] && fy == 0 && fz == 0) { // no interpolation needed at all for (int i = 0; i < n; i++) { vtkIdType t0 = iX[0]; iX += 2; const T *inPtr0 = inPtr + i00 + t0; int m = numscalars; do { *outPtr++ = *inPtr0++; } while (--m); } } else if (useNearestNeighbor[0] && fy == 0) { // only need linear z interpolation for (int i = 0; i < n; i++) { vtkIdType t0 = iX[0]; iX += 2; const T *inPtr0 = inPtr + t0; int m = numscalars; do { F result = (rz*inPtr0[i00] + fz*inPtr0[i01]); vtkResliceRound(result, *outPtr++); inPtr0++; } while (--m); } } else if (fz == 0) { // bilinear interpolation in x,y for (int i = 0; i < n; i++) { F rx = fX[0]; F fx = fX[1]; fX += 2; vtkIdType t0 = iX[0]; vtkIdType t1 = iX[1]; iX += 2; const T *inPtr0 = inPtr + t0; const T *inPtr1 = inPtr + t1; int m = numscalars; do { F result = (rx*(ry*inPtr0[i00] + fy*inPtr0[i10]) + fx*(ry*inPtr1[i00] + fy*inPtr1[i10])); vtkResliceRound(result, *outPtr++); inPtr0++; inPtr1++; } while (--m); } } else { // do full trilinear interpolation for (int i = 0; i < n; i++) { F rx = fX[0]; F fx = fX[1]; fX += 2; vtkIdType t0 = iX[0]; vtkIdType t1 = iX[1]; iX += 2; const T *inPtr0 = inPtr + t0; const T *inPtr1 = inPtr + t1; int m = numscalars; do { F result = (rx*(ryrz*inPtr0[i00] + ryfz*inPtr0[i01] + fyrz*inPtr0[i10] + fyfz*inPtr0[i11]) + fx*(ryrz*inPtr1[i00] + ryfz*inPtr1[i01] + fyrz*inPtr1[i10] + fyfz*inPtr1[i11])); vtkResliceRound(result, *outPtr++); inPtr0++; inPtr1++; } while (--m); } } } //-------------------------------------------------------------------------- // helper function for tricubic interpolation template void vtkPermuteTricubicSummation(T *&outPtr, const T *inPtr, int numscalars, int n, const vtkIdType *iX, const F *fX, const vtkIdType *iY, const F *fY, const vtkIdType *iZ, const F *fZ, const int useNearestNeighbor[3]) { // speed things up a bit for bicubic interpolation int k1 = 0; int k2 = 3; if (useNearestNeighbor[2]) { k1 = k2 = 1; } for (int i = 0; i < n; i++) { vtkIdType iX0 = iX[0]; vtkIdType iX1 = iX[1]; vtkIdType iX2 = iX[2]; vtkIdType iX3 = iX[3]; iX += 4; F fX0 = fX[0]; F fX1 = fX[1]; F fX2 = fX[2]; F fX3 = fX[3]; fX += 4; const T *inPtr0 = inPtr; int c = numscalars; do { // loop over components F result = 0; int k = k1; do { // loop over z F fz = fZ[k]; if (fz != 0) { vtkIdType iz = iZ[k]; int j = 0; do { // loop over y F fy = fY[j]; F fzy = fz*fy; vtkIdType izy = iz + iY[j]; const T *tmpPtr = inPtr0 + izy; // loop over x is unrolled (significant performance boost) result += fzy*(fX0*tmpPtr[iX0] + fX1*tmpPtr[iX1] + fX2*tmpPtr[iX2] + fX3*tmpPtr[iX3]); } while (++j <= 3); } } while (++k <= k2); vtkResliceClamp(result, *outPtr++); inPtr0++; } while (--c); } } //---------------------------------------------------------------------------- // get approprate summation function for different interpolation modes // and different scalar types template void vtkGetResliceSummationFunc(vtkImageReslice *self, void (**summation)(void *&out, const void *in, int numscalars, int n, const vtkIdType *iX, const F *fX, const vtkIdType *iY, const F *fY, const vtkIdType *iZ, const F *fZ, const int useNearest[3]), int interpolationMode) { int scalarType = self->GetOutput()->GetScalarType(); switch (interpolationMode) { case VTK_RESLICE_NEAREST: switch (scalarType) { vtkTemplateAliasMacro(*((void (**)(VTK_TT *&out, const VTK_TT *in, int numscalars, int n, const vtkIdType *iX, const F *fX, const vtkIdType *iY, const F *fY, const vtkIdType *iZ, const F *fZ, const int useNearest[3]))summation) = \ vtkPermuteNearestSummation); default: summation = 0; } break; case VTK_RESLICE_LINEAR: switch (scalarType) { vtkTemplateAliasMacro(*((void (**)(VTK_TT *&out, const VTK_TT *in, int numscalars, int n, const vtkIdType *iX, const F *fX, const vtkIdType *iY, const F *fY, const vtkIdType *iZ, const F *fZ, const int useNearest[3]))summation) = \ vtkPermuteTrilinearSummation); default: summation = 0; } break; case VTK_RESLICE_CUBIC: switch (scalarType) { vtkTemplateAliasMacro(*((void (**)(VTK_TT *&out, const VTK_TT *in, int numscalars, int n, const vtkIdType *iX, const F *fX, const vtkIdType *iY, const F *fY, const vtkIdType *iZ, const F *fZ, const int useNearest[3]))summation) = \ vtkPermuteTricubicSummation); default: summation = 0; } break; default: summation = 0; } } //---------------------------------------------------------------------------- template void vtkPermuteNearestTable(vtkImageReslice *self, const int outExt[6], const int inExt[6], const vtkIdType inInc[3], int clipExt[6], vtkIdType **traversal, F **vtkNotUsed(constants), int useNearestNeighbor[3], F newmat[4][4]) { // set up input traversal table for nearest-neighbor interpolation for (int j = 0; j < 3; j++) { int k; for (k = 0; k < 3; k++) { // set k to the element which is nonzero if (newmat[k][j] != 0) { break; } } // this is just for symmetry with Linear and Cubic useNearestNeighbor[j] = 1; int inExtK = inExt[2*k+1] - inExt[2*k] + 1; int region = 0; for (int i = outExt[2*j]; i <= outExt[2*j+1]; i++) { int inId = vtkResliceRound((newmat[k][3] + i*newmat[k][j])); inId -= inExt[2*k]; if (self->GetMirror()) { inId = vtkInterpolateMirror(inId, inExtK); region = 1; } else if (self->GetWrap()) { inId = vtkInterpolateWrap(inId, inExtK); region = 1; } else { if (inId < 0 || inId >= inExtK) { if (region == 1) { // leaving the input extent region = 2; clipExt[2*j+1] = i - 1; } } else { if (region == 0) { // entering the input extent region = 1; clipExt[2*j] = i; } } } traversal[j][i] = inId*inInc[k]; } if (region == 0) { // never entered input extent! clipExt[2*j] = clipExt[2*j+1] + 1; } } } //---------------------------------------------------------------------------- template void vtkPermuteLinearTable(vtkImageReslice *self, const int outExt[6], const int inExt[6], const vtkIdType inInc[3], int clipExt[6], vtkIdType **traversal, F **constants, int useNearestNeighbor[3], F newmat[4][4]) { // set up input traversal table for linear interpolation for (int j = 0; j < 3; j++) { int k; for (k = 0; k < 3; k++) { // set k to the element which is nonzero if (newmat[k][j] != 0) { break; } } // do the output pixels lie exactly on top of the input pixels? F f1, f2; vtkResliceFloor(newmat[k][j], f1); vtkResliceFloor(newmat[k][3], f2); useNearestNeighbor[j] = (f1 == 0 && f2 == 0); int inExtK = inExt[2*k+1] - inExt[2*k] + 1; int region = 0; for (int i = outExt[2*j]; i <= outExt[2*j+1]; i++) { F point = newmat[k][3] + i*newmat[k][j]; F f; int trunc = vtkResliceFloor(point, f); constants[j][2*i] = 1 - f; constants[j][2*i+1] = f; int fIsNotZero = (f != 0); int inId0 = trunc - inExt[2*k]; int inId1 = inId0 + fIsNotZero; if (self->GetMirror()) { inId0 = vtkInterpolateMirror(inId0, inExtK); inId1 = vtkInterpolateMirror(inId1, inExtK); region = 1; } else if (self->GetWrap()) { inId0 = vtkInterpolateWrap(inId0, inExtK); inId1 = vtkInterpolateWrap(inId1, inExtK); region = 1; } else if (self->GetBorder()) { if (vtkInterpolateBorder(inId0, inId1, inExtK, f)) { if (region == 1) { // leaving the input extent region = 2; clipExt[2*j+1] = i - 1; } } else { if (region == 0) { // entering the input extent region = 1; clipExt[2*j] = i; } } } else // not self->GetBorder() { if (inId0 < 0 || inId1 >= inExtK) { if (region == 1) { // leaving the input extent region = 2; clipExt[2*j+1] = i - 1; } } else { if (region == 0) { // entering the input extent region = 1; clipExt[2*j] = i; } } } traversal[j][2*i] = inId0*inInc[k]; traversal[j][2*i+1] = inId1*inInc[k]; } if (region == 0) { // never entered input extent! clipExt[2*j] = clipExt[2*j+1] + 1; } } } //---------------------------------------------------------------------------- template void vtkPermuteCubicTable(vtkImageReslice *self, const int outExt[6], const int inExt[6], const vtkIdType inInc[3], int clipExt[6], vtkIdType **traversal, F **constants, int useNearestNeighbor[3], F newmat[4][4]) { // set up input traversal table for cubic interpolation for (int j = 0; j < 3; j++) { int k; for (k = 0; k < 3; k++) { // set k to the element which is nonzero if (newmat[k][j] != 0) { break; } } // do the output pixels lie exactly on top of the input pixels? F f1, f2; vtkResliceFloor(newmat[k][j], f1); vtkResliceFloor(newmat[k][3], f2); useNearestNeighbor[j] = (f1 == 0 && f2 == 0); int inExtK = inExt[2*k+1] - inExt[2*k] + 1; int region = 0; for (int i = outExt[2*j]; i <= outExt[2*j+1]; i++) { F point = newmat[k][3] + i*newmat[k][j]; F f; int trunc = vtkResliceFloor(point, f); int fIsNotZero = (f != 0); int inId[4]; inId[1] = trunc - inExt[2*k]; inId[0] = inId[1] - 1; inId[2] = inId[1] + 1; inId[3] = inId[1] + 2; int low = 1 - fIsNotZero; int high = 1 + 2*fIsNotZero; if (self->GetMirror()) { inId[0] = vtkInterpolateMirror(inId[0], inExtK); inId[1] = vtkInterpolateMirror(inId[1], inExtK); inId[2] = vtkInterpolateMirror(inId[2], inExtK); inId[3] = vtkInterpolateMirror(inId[3], inExtK); region = 1; } else if (self->GetWrap()) { inId[0] = vtkInterpolateWrap(inId[0], inExtK); inId[1] = vtkInterpolateWrap(inId[1], inExtK); inId[2] = vtkInterpolateWrap(inId[2], inExtK); inId[3] = vtkInterpolateWrap(inId[3], inExtK); region = 1; } else if (self->GetBorder()) { if (vtkInterpolateBorderCheck(inId[1], inId[2], inExtK, f)) { if (region == 1) { // leaving the input extent region = 2; clipExt[2*j+1] = i - 1; } } else { if (region == 0) { // entering the input extent region = 1; clipExt[2*j] = i; } } int tmpExt = inExtK - 1; inId[0] = (inId[0])*(inId[0] >= 0); inId[1] = (inId[1])*(inId[1] >= 0); inId[2] = tmpExt - (tmpExt - inId[2])*(tmpExt - inId[2] >= 0); inId[3] = tmpExt - (tmpExt - inId[3])*(tmpExt - inId[3] >= 0); low = 1 - fIsNotZero; high = 1 + 2*fIsNotZero; } else // not self->GetBorder() { if (inId[1] < 0 || inId[1] + fIsNotZero >= inExtK) { if (region == 1) { // leaving the input extent region = 2; clipExt[2*j+1] = i - 1; } } else { if (region == 0) { // entering the input extent region = 1; clipExt[2*j] = i; } } low = 1 - (inId[0] >= 0)*fIsNotZero; high = 1 + (1 + (inId[3] < inExtK))*fIsNotZero; } vtkTricubicInterpCoeffs(&constants[j][4*i], low, high, f); // set default values int l; for (l = 0; l < 4; l++) { traversal[j][4*i+l] = inId[1]*inInc[k]; } for (l = low; l <= high; l++) { traversal[j][4*i+l] = inId[l]*inInc[k]; } } if (region == 0) { // never entered input extent! clipExt[2*j] = clipExt[2*j+1] + 1; } } } //---------------------------------------------------------------------------- // Check to see if we can do nearest-neighbor instead of linear or cubic. // This check only works on permutation+scale+translation matrices. template inline int vtkCanUseNearestNeighbor(F matrix[4][4], int outExt[6]) { // loop through dimensions for (int i = 0; i < 3; i++) { int j; for (j = 0; j < 3; j++) { if (matrix[i][j] != 0) { break; } } F x = matrix[i][j]; F y = matrix[i][3]; if (outExt[2*j] == outExt[2*j+1]) { y += x*outExt[2*i]; x = 0; } F fx, fy; vtkResliceFloor(x, fx); vtkResliceFloor(y, fy); if (fx != 0 || fy != 0) { return 0; } } return 1; } //---------------------------------------------------------------------------- // the ReslicePermuteExecute path is taken when the output slices are // orthogonal to the input slices template void vtkReslicePermuteExecute(vtkImageReslice *self, vtkImageData *inData, void *inPtr, vtkImageData *outData, void *outPtr, int outExt[6], int id, F newmat[4][4]) { vtkIdType outInc[3]; int scalarSize, numscalars; vtkIdType inInc[3]; int inExt[6], clipExt[6]; vtkIdType *traversal[3]; F *constants[3]; int useNearestNeighbor[3]; int i; // find maximum input range inData->GetExtent(inExt); // Get Increments to march through data inData->GetIncrements(inInc); outData->GetContinuousIncrements(outExt, outInc[0], outInc[1], outInc[2]); scalarSize = outData->GetScalarSize(); numscalars = inData->GetNumberOfScalarComponents(); for (i = 0; i < 3; i++) { clipExt[2*i] = outExt[2*i]; clipExt[2*i+1] = outExt[2*i+1]; } int interpolationMode = self->GetInterpolationMode(); if (vtkCanUseNearestNeighbor(newmat, outExt)) { interpolationMode = VTK_RESLICE_NEAREST; } // the step size is the number of coefficients per dimension int step = 1; switch (interpolationMode) { case VTK_RESLICE_NEAREST: step = 1; break; case VTK_RESLICE_LINEAR: step = 2; break; case VTK_RESLICE_CUBIC: step = 4; break; } // allocate the interpolation tables for (i = 0; i < 3; i++) { int outExtI = outExt[2*i+1] - outExt[2*i] + 1; traversal[i] = new vtkIdType[outExtI*step]; traversal[i] -= step*outExt[2*i]; constants[i] = new F[outExtI*step]; constants[i] -= step*outExt[2*i]; } // fill in the interpolation tables switch (interpolationMode) { case VTK_RESLICE_NEAREST: vtkPermuteNearestTable(self, outExt, inExt, inInc, clipExt, traversal, constants, useNearestNeighbor, newmat); break; case VTK_RESLICE_LINEAR: vtkPermuteLinearTable(self, outExt, inExt, inInc, clipExt, traversal, constants, useNearestNeighbor, newmat); break; case VTK_RESLICE_CUBIC: vtkPermuteCubicTable(self, outExt, inExt, inInc, clipExt, traversal, constants, useNearestNeighbor, newmat); break; } // get type-specific functions void (*summation)(void *&out, const void *in, int numscalars, int n, const vtkIdType *iX, const F *fX, const vtkIdType *iY, const F *fY, const vtkIdType *iZ, const F *fZ, const int useNearestNeighbor[3]); void (*setpixels)(void *&out, const void *in, int numscalars, int n); vtkGetResliceSummationFunc(self, &summation, interpolationMode); vtkGetSetPixelsFunc(self, &setpixels); // set color for area outside of input volume extent void *background; vtkAllocBackgroundPixel(self, &background, numscalars); // get the stencil vtkImageStencilData *stencil = self->GetStencil(); // for tracking progress unsigned long count = 0; unsigned long target = (unsigned long) ((outExt[5]-outExt[4]+1)*(outExt[3]-outExt[2]+1)/50.0); target++; // Loop through output pixels for (int idZ = outExt[4]; idZ <= outExt[5]; idZ++) { int idZ0 = idZ*step; for (int idY = outExt[2]; idY <= outExt[3]; idY++) { int idY0 = idY*step; if (id == 0) { // track progress if we are main thread if (!(count%target)) { self->UpdateProgress(count/(50.0*target)); } count++; } // do extent check if (idZ < clipExt[4] || idZ > clipExt[5] || idY < clipExt[2] || idY > clipExt[3]) { // just clear, we're completely outside setpixels(outPtr, background, numscalars, outExt[1] - outExt[0] + 1); } else { // clear pixels to left of input extent setpixels(outPtr, background, numscalars, clipExt[0] - outExt[0]); int iter = 0; int idXmin, idXmax; while (vtkResliceGetNextExtent(stencil, idXmin, idXmax, clipExt[0], clipExt[1], idY, idZ, outPtr, background, numscalars, setpixels, iter)) { int idX0 = idXmin*step; summation(outPtr, inPtr, numscalars, idXmax - idXmin + 1, &traversal[0][idX0], &constants[0][idX0], &traversal[1][idY0], &constants[1][idY0], &traversal[2][idZ0], &constants[2][idZ0], useNearestNeighbor); } // clear pixels to right of input extent setpixels(outPtr, background, numscalars, outExt[1] - clipExt[1]); } outPtr = (void *)((char *)outPtr + outInc[1]*scalarSize); } outPtr = (void *)((char *)outPtr + outInc[2]*scalarSize); } vtkFreeBackgroundPixel(self, &background); for (i = 0; i < 3; i++) { traversal[i] += step*outExt[2*i]; constants[i] += step*outExt[2*i]; delete [] traversal[i]; delete [] constants[i]; } } //---------------------------------------------------------------------------- // check a matrix to ensure that it is a permutation+scale+translation // matrix template int vtkIsPermutationMatrix(F matrix[4][4]) { for (int i = 0; i < 3; i++) { if (matrix[3][i] != 0) { return 0; } } if (matrix[3][3] != 1) { return 0; } for (int j = 0; j < 3; j++) { int k = 0; for (int i = 0; i < 3; i++) { if (matrix[i][j] != 0) { k++; } } if (k != 1) { return 0; } } return 1; } //---------------------------------------------------------------------------- // check a matrix to see whether it is the identity matrix int vtkIsIdentityMatrix(vtkMatrix4x4 *matrix) { static double identity[16] = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1}; int i,j; for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { if (matrix->GetElement(i,j) != identity[4*i+j]) { return 0; } } } return 1; } //---------------------------------------------------------------------------- // The transform matrix supplied by the user converts output coordinates // to input coordinates. // To speed up the pixel lookup, the following function provides a // matrix which converts output pixel indices to input pixel indices. // // This will also concatenate the ResliceAxes and the ResliceTransform // if possible (if the ResliceTransform is a 4x4 matrix transform). // If it does, this->OptimizedTransform will be set to NULL, otherwise // this->OptimizedTransform will be equal to this->ResliceTransform. vtkMatrix4x4 *vtkImageReslice::GetIndexMatrix(vtkInformation *inInfo, vtkInformation *outInfo) { // first verify that we have to update the matrix if (this->IndexMatrix == NULL) { this->IndexMatrix = vtkMatrix4x4::New(); } int isIdentity = 0; double inOrigin[3]; double inSpacing[3]; double outOrigin[3]; double outSpacing[3]; inInfo->Get(vtkDataObject::SPACING(), inSpacing); inInfo->Get(vtkDataObject::ORIGIN(), inOrigin); outInfo->Get(vtkDataObject::SPACING(), outSpacing); outInfo->Get(vtkDataObject::ORIGIN(), outOrigin); vtkTransform *transform = vtkTransform::New(); vtkMatrix4x4 *inMatrix = vtkMatrix4x4::New(); vtkMatrix4x4 *outMatrix = vtkMatrix4x4::New(); if (this->OptimizedTransform) { this->OptimizedTransform->Delete(); } this->OptimizedTransform = NULL; if (this->ResliceAxes) { transform->SetMatrix(this->GetResliceAxes()); } if (this->ResliceTransform) { if (this->ResliceTransform->IsA("vtkHomogeneousTransform")) { transform->PostMultiply(); transform->Concatenate(((vtkHomogeneousTransform *) this->ResliceTransform)->GetMatrix()); } else { this->ResliceTransform->Register(this); this->OptimizedTransform = this->ResliceTransform; } } // check to see if we have an identity matrix isIdentity = vtkIsIdentityMatrix(transform->GetMatrix()); // the outMatrix takes OutputData indices to OutputData coordinates, // the inMatrix takes InputData coordinates to InputData indices for (int i = 0; i < 3; i++) { if ((this->OptimizedTransform == NULL && (inSpacing[i] != outSpacing[i] || inOrigin[i] != outOrigin[i])) || (this->OptimizedTransform != NULL && (inSpacing[i] != 1.0 || inOrigin[i] != 0.0))) { isIdentity = 0; } inMatrix->Element[i][i] = 1.0/inSpacing[i]; inMatrix->Element[i][3] = -inOrigin[i]/inSpacing[i]; outMatrix->Element[i][i] = outSpacing[i]; outMatrix->Element[i][3] = outOrigin[i]; } outInfo->Get(vtkDataObject::ORIGIN(), outOrigin); if (!isIdentity) { transform->PreMultiply(); transform->Concatenate(outMatrix); if (this->OptimizedTransform == NULL) { transform->PostMultiply(); transform->Concatenate(inMatrix); } } transform->GetMatrix(this->IndexMatrix); transform->Delete(); inMatrix->Delete(); outMatrix->Delete(); return this->IndexMatrix; } //---------------------------------------------------------------------------- // This method is passed a input and output region, and executes the filter // algorithm to fill the output from the input. // It just executes a switch statement to call the correct function for // the regions data types. void vtkImageReslice::ThreadedRequestData( vtkInformation *vtkNotUsed(request), vtkInformationVector **vtkNotUsed(inputVector), vtkInformationVector *vtkNotUsed(outputVector), vtkImageData ***inData, vtkImageData **outData, int outExt[6], int id) { vtkDebugMacro(<< "Execute: inData = " << inData[0][0] << ", outData = " << outData[0]); // this filter expects that input is the same type as output. if (inData[0][0]->GetScalarType() != outData[0]->GetScalarType()) { vtkErrorMacro(<< "Execute: input ScalarType, " << inData[0][0]->GetScalarType() << ", must match out ScalarType " << outData[0]->GetScalarType()); return; } int inExt[6]; inData[0][0]->GetExtent(inExt); // check for empty input extent if (inExt[1] < inExt[0] || inExt[3] < inExt[2] || inExt[5] < inExt[4]) { return; } // Get the output pointer void *outPtr = outData[0]->GetScalarPointerForExtent(outExt); if (this->HitInputExtent == 0) { vtkImageResliceClearExecute(this, inData[0][0], 0, outData[0], outPtr, outExt, id); return; } // Now that we know that we need the input, get the input pointer void *inPtr = inData[0][0]->GetScalarPointerForExtent(inExt); if (this->Optimization) { // change transform matrix so that instead of taking // input coords -> output coords it takes output indices -> input indices vtkMatrix4x4 *matrix = this->IndexMatrix; // get the portion of the transformation that remains apart from // the IndexMatrix vtkAbstractTransform *newtrans = this->OptimizedTransform; double newmat[4][4]; for (int i = 0; i < 4; i++) { newmat[i][0] = matrix->GetElement(i,0); newmat[i][1] = matrix->GetElement(i,1); newmat[i][2] = matrix->GetElement(i,2); newmat[i][3] = matrix->GetElement(i,3); } if (vtkIsPermutationMatrix(newmat) && newtrans == NULL) { vtkReslicePermuteExecute(this, inData[0][0], inPtr, outData[0], outPtr, outExt, id, newmat); } else { vtkOptimizedExecute(this, inData[0][0], inPtr, outData[0], outPtr, outExt, id, newmat, newtrans); } } else { vtkImageResliceExecute(this, inData[0][0], inPtr, outData[0], outPtr, outExt, id); } }