/*========================================================================= Program: Visualization Toolkit Module: vtkImageBSplineCoefficients.cxx 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 "vtkImageBSplineCoefficients.h" #include "vtkImageBSplineInternals.h" #include "vtkDataArray.h" #include "vtkImageData.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkMath.h" #include "vtkObjectFactory.h" #include "vtkPointData.h" #include "vtkStreamingDemandDrivenPipeline.h" #include "vtkTemplateAliasMacro.h" #include vtkStandardNewMacro(vtkImageBSplineCoefficients); //------------------------------------------------------------------------------ vtkImageBSplineCoefficients::vtkImageBSplineCoefficients() { this->SplineDegree = 3; this->BorderMode = VTK_IMAGE_BORDER_CLAMP; this->OutputScalarType = VTK_FLOAT; this->Bypass = 0; this->DataWasPassed = 0; this->Iteration = 0; } //------------------------------------------------------------------------------ vtkImageBSplineCoefficients::~vtkImageBSplineCoefficients() = default; //------------------------------------------------------------------------------ void vtkImageBSplineCoefficients::AllocateOutputData( vtkImageData* vtkNotUsed(output), vtkInformation* vtkNotUsed(outInfo), int* vtkNotUsed(uExtent)) { // turn into a no-op, we allocate our output manually } //------------------------------------------------------------------------------ vtkImageData* vtkImageBSplineCoefficients::AllocateOutputData( vtkDataObject* output, vtkInformation* vtkNotUsed(outInfo)) { // turn into a no-op, we allocate our output manually vtkImageData* out = vtkImageData::SafeDownCast(output); return out; } //------------------------------------------------------------------------------ int vtkImageBSplineCoefficients::RequestData( vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector) { vtkInformation* inInfo = inputVector[0]->GetInformationObject(0); vtkInformation* outInfo = outputVector->GetInformationObject(0); vtkImageData* inData = vtkImageData::SafeDownCast(inInfo->Get(vtkDataObject::DATA_OBJECT())); vtkImageData* outData = vtkImageData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT())); if (this->Bypass) { // directly pass the scalars to the output outData->SetExtent(inData->GetExtent()); outData->GetPointData()->PassData(inData->GetPointData()); this->DataWasPassed = 1; return 1; } else if (this->DataWasPassed) { // force reallocation of the scalars outData->GetPointData()->SetScalars(nullptr); this->DataWasPassed = 0; } // Allocate the output data outData->SetExtent(outInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT())); outData->AllocateScalars(outInfo); if (outData->GetScalarType() != VTK_FLOAT && outData->GetScalarType() != VTK_DOUBLE) { vtkErrorMacro(<< "Execute: output data must be type float or double."); return 0; } // copy the data to the output int ie[6], oe[6]; inData->GetExtent(ie); outData->GetExtent(oe); if (ie[0] == oe[0] && ie[1] == oe[1] && ie[2] == oe[2] && ie[3] == oe[3] && ie[4] == oe[4] && ie[5] == oe[5]) { outData->GetPointData()->GetScalars()->DeepCopy(inData->GetPointData()->GetScalars()); } else { vtkErrorMacro(<< "Execute: input and output extents do not match: " << "(" << ie[0] << "," << ie[1] << "," << ie[2] << "," << ie[3] << "," << ie[4] << "," << ie[5] << ") vs. " << "(" << oe[0] << "," << oe[1] << "," << oe[2] << "," << oe[3] << "," << oe[4] << "," << oe[5] << ")"); return 0; } // if spline degree is < 2, no operation is required if (this->SplineDegree < 2) { return 1; } // We are about to call superclass' RequestData which allocates output // based on the update extent. However, we want the output to be the // whole extent. So we temporarily override the update extent to be // the whole extent. int extentcache[6]; memcpy( extentcache, outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT()), 6 * sizeof(int)); outInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), outInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()), 6); // execute over the three directions for (int i = 0; i < 3; i++) { this->Iteration = i; // ensure that iteration axis is not split during threaded execution this->SplitPathLength = 0; for (int axis = 2; axis >= 0; --axis) { if (axis != i) { this->SplitPath[this->SplitPathLength++] = axis; } } if (ie[2 * i + 1] > ie[2 * i]) { if (!this->vtkThreadedImageAlgorithm::RequestData(request, &outputVector, outputVector)) { return 0; } } } // Restore update extent outInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), extentcache, 6); return 1; } //------------------------------------------------------------------------------ int vtkImageBSplineCoefficients::RequestInformation(vtkInformation* vtkNotUsed(request), vtkInformationVector** inputVector, vtkInformationVector* outputVector) { vtkInformation* inInfo = inputVector[0]->GetInformationObject(0); vtkInformation* outInfo = outputVector->GetInformationObject(0); int numComponents = 1; int scalarType = VTK_FLOAT; vtkInformation* inScalarInfo = vtkDataObject::GetActiveFieldInformation( inInfo, vtkDataObject::FIELD_ASSOCIATION_POINTS, vtkDataSetAttributes::SCALARS); if (inScalarInfo) { if (inScalarInfo->Has(vtkDataObject::FIELD_NUMBER_OF_COMPONENTS())) { numComponents = inScalarInfo->Get(vtkDataObject::FIELD_NUMBER_OF_COMPONENTS()); } scalarType = inScalarInfo->Get(vtkDataObject::FIELD_ARRAY_TYPE()); } if (this->Bypass) { vtkDataObject::SetPointDataActiveScalarInfo(outInfo, scalarType, numComponents); } else if (this->OutputScalarType == VTK_DOUBLE) { vtkDataObject::SetPointDataActiveScalarInfo(outInfo, VTK_DOUBLE, numComponents); } else { vtkDataObject::SetPointDataActiveScalarInfo(outInfo, VTK_FLOAT, numComponents); } return 1; } //------------------------------------------------------------------------------ int vtkImageBSplineCoefficients::RequestUpdateExtent(vtkInformation* vtkNotUsed(request), vtkInformationVector** inputVector, vtkInformationVector* outputVector) { vtkInformation* inInfo = inputVector[0]->GetInformationObject(0); vtkInformation* outInfo = outputVector->GetInformationObject(0); int extent[6]; if (this->Bypass) { // in bypass mode, just pass the update extent outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), extent); } else { // the whole input extent is required every time inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), extent); } inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), extent, 6); return 1; } //------------------------------------------------------------------------------ template void vtkImageBSplineCoefficientsExecute(vtkImageBSplineCoefficients* self, vtkImageData* inData, vtkImageData* outData, T* inPtr, T* outPtr, int extent[6], int axis, int threadId) { // change the order so the inner loop is the chosen axis static const int permute[3][3] = { { 0, 1, 2 }, { 1, 0, 2 }, { 2, 0, 1 } }; vtkImageBorderMode borderMode = self->GetBorderMode(); int inExtent[6]; inData->GetExtent(inExtent); int inMin0 = inExtent[2 * permute[axis][0]]; int inMax0 = inExtent[2 * permute[axis][0] + 1]; int outMin0 = extent[2 * permute[axis][0]]; int outMax0 = extent[2 * permute[axis][0] + 1]; int outMin1 = extent[2 * permute[axis][1]]; int outMax1 = extent[2 * permute[axis][1] + 1]; int outMin2 = extent[2 * permute[axis][2]]; int outMax2 = extent[2 * permute[axis][2] + 1]; vtkIdType inInc[6]; inData->GetIncrements(inInc); vtkIdType inInc0 = inInc[permute[axis][0]]; vtkIdType inInc1 = inInc[permute[axis][1]]; vtkIdType inInc2 = inInc[permute[axis][2]]; vtkIdType outInc[6]; outData->GetIncrements(outInc); vtkIdType outInc0 = outInc[permute[axis][0]]; vtkIdType outInc1 = outInc[permute[axis][1]]; vtkIdType outInc2 = outInc[permute[axis][2]]; int numscalars = outData->GetNumberOfScalarComponents(); // for progress reporting unsigned long count = 0; unsigned long target = static_cast(0.02 * (outMax2 - outMin2 + 1) * (outMax1 - outMin1 + 1)); target++; // Get the poles for the spline double poles[4]; long numPoles; vtkImageBSplineInternals::GetPoleValues(poles, numPoles, self->GetSplineDegree()); // allocate workspace for one row double* image = new double[inMax0 - inMin0 + 1]; // loop over all the extra axes T* inPtr2 = inPtr - (outMin0 - inMin0) * inInc0; T* outPtr2 = outPtr; for (int idx2 = outMin2; idx2 <= outMax2; idx2++) { T* inPtr1 = inPtr2; T* outPtr1 = outPtr2; for (int idx1 = outMin1; !self->AbortExecute && idx1 <= outMax1; idx1++) { if (threadId == 0 && count % target == 0) { self->UpdateProgress((axis + count / (50.0 * target)) / 3.0); } count++; // loop over components for (int idxC = 0; idxC < numscalars; idxC++) { T* inPtr0 = inPtr1 + idxC; T* outPtr0 = outPtr1 + idxC; double* imagePtr = image; for (int jdx0 = inMin0; jdx0 <= inMax0; jdx0++) { *imagePtr++ = static_cast(*inPtr0); inPtr0 += inInc0; } // Call the code that generates the b-spline knots, vtkImageBSplineInternals::ConvertToInterpolationCoefficients( image, inMax0 - inMin0 + 1, borderMode, poles, numPoles, VTK_DBL_EPSILON); // Copy to output imagePtr = image + (outMin0 - inMin0); for (int idx0 = outMin0; idx0 <= outMax0; idx0++) { *outPtr0 = *imagePtr++; outPtr0 += outInc0; } } inPtr1 += inInc1; outPtr1 += outInc1; } inPtr2 += inInc2; outPtr2 += outInc2; } delete[] image; } //------------------------------------------------------------------------------ // This is called three times (once per dimension) void vtkImageBSplineCoefficients::ThreadedExecute( vtkImageData* inData, vtkImageData* outData, int outExt[6], int threadId) { void* inPtr = inData->GetScalarPointerForExtent(outExt); void* outPtr = outData->GetScalarPointerForExtent(outExt); if (outData->GetScalarType() == VTK_FLOAT) { vtkImageBSplineCoefficientsExecute(this, inData, outData, static_cast(inPtr), static_cast(outPtr), outExt, this->Iteration, threadId); } else if (outData->GetScalarType() == VTK_DOUBLE) { vtkImageBSplineCoefficientsExecute(this, inData, outData, static_cast(inPtr), static_cast(outPtr), outExt, this->Iteration, threadId); } } //------------------------------------------------------------------------------ void vtkImageBSplineCoefficients::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << "SplineDegree: " << this->SplineDegree << "\n"; os << "BorderMode: " << this->GetBorderModeAsString() << "\n"; os << "OutputScalarType: " << this->GetOutputScalarTypeAsString() << "\n"; os << "Bypass: " << (this->Bypass ? "On\n" : "Off\n"); } //------------------------------------------------------------------------------ const char* vtkImageBSplineCoefficients::GetBorderModeAsString() { switch (this->BorderMode) { case VTK_IMAGE_BORDER_CLAMP: return "Clamp"; case VTK_IMAGE_BORDER_MIRROR: return "Mirror"; case VTK_IMAGE_BORDER_REPEAT: return "Repeat"; default: break; } return "Unknown"; } //------------------------------------------------------------------------------ const char* vtkImageBSplineCoefficients::GetOutputScalarTypeAsString() { return vtkImageScalarTypeNameMacro(this->OutputScalarType); } //------------------------------------------------------------------------------ int vtkImageBSplineCoefficients::CheckBounds(const double point[3]) { const double* bounds = this->GetOutput()->GetBounds(); for (int i = 0; i < 3; i++) { double a = bounds[0]; double b = bounds[1]; if ((b - a) > 1e-16 && (point[i] < a || point[i] > b)) { return 0; } bounds++; } return 1; } //------------------------------------------------------------------------------ void vtkImageBSplineCoefficients::Evaluate(const double p[3], double* val) { vtkImageData* output = this->GetOutput(); int extent[6]; double spacing[3], origin[3]; output->GetExtent(extent); output->GetOrigin(origin); output->GetSpacing(spacing); int width = extent[1] - extent[0] + 1; int height = extent[3] - extent[2] + 1; int slices = extent[5] - extent[4] + 1; double x = (p[0] - origin[0]) / spacing[0] - extent[0]; double y = (p[1] - origin[1]) / spacing[1] - extent[2]; double z = (p[2] - origin[2]) / spacing[2] - extent[4]; if (width < 1 || height < 1 || slices < 1) { vtkErrorMacro("Called Evaluate() on empty extent"); return; } int numscalars = output->GetNumberOfScalarComponents(); int scalarType = output->GetScalarType(); if (scalarType == VTK_FLOAT) { float* coeffs = static_cast(output->GetScalarPointer()); float value4[4]; float* value = value4; if (numscalars > 4) { value = new float[numscalars]; } vtkImageBSplineInternals::InterpolatedValue(coeffs, value, width, height, slices, numscalars, x, y, z, this->SplineDegree, this->BorderMode); for (int i = 0; i < numscalars; i++) { val[i] = value[i]; } if (value != value4) { delete[] value; } } else if (scalarType == VTK_DOUBLE) { double* coeffs = static_cast(output->GetScalarPointer()); vtkImageBSplineInternals::InterpolatedValue(coeffs, val, width, height, slices, numscalars, x, y, z, this->SplineDegree, this->BorderMode); } else { vtkErrorMacro("Called Evaluate(), but data is not float or double."); } } //------------------------------------------------------------------------------ double vtkImageBSplineCoefficients::Evaluate(double x, double y, double z) { vtkImageData* output = this->GetOutput(); int extent[6]; double spacing[3], origin[3]; output->GetExtent(extent); output->GetOrigin(origin); output->GetSpacing(spacing); int width = extent[1] - extent[0] + 1; int height = extent[3] - extent[2] + 1; int slices = extent[5] - extent[4] + 1; x = (x - origin[0]) / spacing[0] - extent[0]; y = (y - origin[1]) / spacing[1] - extent[2]; z = (z - origin[2]) / spacing[2] - extent[4]; if (width < 1 || height < 1 || slices < 1) { vtkErrorMacro("Called Evaluate() on empty extent"); return 0.0; } int numscalars = output->GetNumberOfScalarComponents(); int scalarType = output->GetScalarType(); if (scalarType == VTK_FLOAT) { float* coeffs = static_cast(output->GetScalarPointer()); float value4[4]; float* value = value4; if (numscalars > 4) { value = new float[numscalars]; } vtkImageBSplineInternals::InterpolatedValue(coeffs, value, width, height, slices, numscalars, x, y, z, this->SplineDegree, this->BorderMode); if (value != value4) { value4[0] = value[0]; delete[] value; value = value4; } return value[0]; } else if (scalarType == VTK_DOUBLE) { double* coeffs = static_cast(output->GetScalarPointer()); double value4[4]; double* value = value4; if (numscalars > 4) { value = new double[numscalars]; } vtkImageBSplineInternals::InterpolatedValue(coeffs, value, width, height, slices, numscalars, x, y, z, this->SplineDegree, this->BorderMode); if (value != value4) { value4[0] = value[0]; delete[] value; value = value4; } return value[0]; } else { vtkErrorMacro("Called Evaluate(), but data is not float or double."); } return 0; }