/*========================================================================= * * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef itkImageAlgorithm_hxx #define itkImageAlgorithm_hxx #include "itkArray.h" #include "itkImageRegionIterator.h" #include "itkImageScanlineIterator.h" namespace itk { template void ImageAlgorithm::DispatchedCopy(const InputImageType * inImage, OutputImageType * outImage, const typename InputImageType::RegionType & inRegion, const typename OutputImageType::RegionType & outRegion, FalseType) { if (inRegion.GetSize()[0] == outRegion.GetSize()[0]) { itk::ImageScanlineConstIterator it(inImage, inRegion); itk::ImageScanlineIterator ot(outImage, outRegion); while (!it.IsAtEnd()) { while (!it.IsAtEndOfLine()) { ot.Set(static_cast(it.Get())); ++ot; ++it; } ot.NextLine(); it.NextLine(); } return; } itk::ImageRegionConstIterator it(inImage, inRegion); itk::ImageRegionIterator ot(outImage, outRegion); while (!it.IsAtEnd()) { ot.Set(static_cast(it.Get())); ++ot; ++it; } } template void ImageAlgorithm::DispatchedCopy(const InputImageType * inImage, OutputImageType * outImage, const typename InputImageType::RegionType & inRegion, const typename OutputImageType::RegionType & outRegion, TrueType) { using _RegionType = typename InputImageType::RegionType; using _IndexType = typename InputImageType::IndexType; // Get the number of bytes of each pixel in the buffer. const size_t NumberOfInternalComponents = ImageAlgorithm::PixelSize::Get(inImage); // We wish to copy whole lines, otherwise just use the basic implementation. // Check that the number of internal components match if (inRegion.GetSize()[0] != outRegion.GetSize()[0] || NumberOfInternalComponents != ImageAlgorithm::PixelSize::Get(outImage)) { ImageAlgorithm::DispatchedCopy(inImage, outImage, inRegion, outRegion); return; } const typename InputImageType::InternalPixelType * in = inImage->GetBufferPointer(); typename OutputImageType::InternalPixelType * out = outImage->GetBufferPointer(); const _RegionType & inBufferedRegion = inImage->GetBufferedRegion(); const _RegionType & outBufferedRegion = outImage->GetBufferedRegion(); // Compute the number of continuous pixel which can be copied. size_t numberOfPixel = 1; unsigned int movingDirection = 0; do { numberOfPixel *= inRegion.GetSize(movingDirection); ++movingDirection; } // The copy regions must extend to the full buffered region, to // ensure continuity of pixels between dimensions. while (movingDirection < _RegionType::ImageDimension && inRegion.GetSize(movingDirection - 1) == inBufferedRegion.GetSize(movingDirection - 1) && outRegion.GetSize(movingDirection - 1) == outBufferedRegion.GetSize(movingDirection - 1) && inBufferedRegion.GetSize(movingDirection - 1) == outBufferedRegion.GetSize(movingDirection - 1)); const size_t sizeOfChunkInInternalComponents = numberOfPixel * NumberOfInternalComponents; _IndexType inCurrentIndex = inRegion.GetIndex(); _IndexType outCurrentIndex = outRegion.GetIndex(); while (inRegion.IsInside(inCurrentIndex)) { size_t inOffset = 0; // in pixels size_t outOffset = 0; size_t inSubDimensionQuantity = 1; // in pixels size_t outSubDimensionQuantity = 1; for (unsigned int i = 0; i < _RegionType::ImageDimension; ++i) { inOffset += inSubDimensionQuantity * static_cast(inCurrentIndex[i] - inBufferedRegion.GetIndex(i)); inSubDimensionQuantity *= inBufferedRegion.GetSize(i); outOffset += outSubDimensionQuantity * static_cast(outCurrentIndex[i] - outBufferedRegion.GetIndex(i)); outSubDimensionQuantity *= outBufferedRegion.GetSize(i); } const typename InputImageType::InternalPixelType * inBuffer = in + inOffset * NumberOfInternalComponents; typename OutputImageType::InternalPixelType * outBuffer = out + outOffset * NumberOfInternalComponents; CopyHelper(inBuffer, inBuffer + sizeOfChunkInInternalComponents, outBuffer); if (movingDirection == _RegionType::ImageDimension) { break; } // increment index to next chunk ++inCurrentIndex[movingDirection]; for (unsigned int i = movingDirection; i + 1 < _RegionType::ImageDimension; ++i) { // When reaching the end of the moving index in the copy region // dimension, carry to higher dimensions. if (static_cast(inCurrentIndex[i] - inRegion.GetIndex(i)) >= inRegion.GetSize(i)) { inCurrentIndex[i] = inRegion.GetIndex(i); ++inCurrentIndex[i + 1]; } } // increment index to next chunk ++outCurrentIndex[movingDirection]; for (unsigned int i = movingDirection; i + 1 < _RegionType::ImageDimension; ++i) { if (static_cast(outCurrentIndex[i] - outRegion.GetIndex(i)) >= outRegion.GetSize(i)) { outCurrentIndex[i] = outRegion.GetIndex(i); ++outCurrentIndex[i + 1]; } } } } template typename OutputImageType::RegionType ImageAlgorithm::EnlargeRegionOverBox(const typename InputImageType::RegionType & inputRegion, const InputImageType * inputImage, const OutputImageType * outputImage) { class DummyTransform { public: using PointType = Point; PointType TransformPoint(const PointType & p) const { return p; } }; return EnlargeRegionOverBox( inputRegion, inputImage, outputImage, nullptr); } template typename OutputImageType::RegionType ImageAlgorithm::EnlargeRegionOverBox(const typename InputImageType::RegionType & inputRegion, const InputImageType * inputImage, const OutputImageType * outputImage, const TransformType * transform) { typename OutputImageType::RegionType outputRegion; // Get the index of the corners of the input region, map them to input physical space. // Convert input physical corners to output physical corners using transform // and finally map output physical corners to index of output image. // The input region has 2^ImageDimension corners, each // of which is either on the inferior or superior edge // along each dimension. unsigned int numberOfInputCorners = 1; for (unsigned int dim = 0; dim < InputImageType::ImageDimension; ++dim) { numberOfInputCorners *= 2; } using ContinuousIndexValueType = double; using ContinuousInputIndexType = ContinuousIndex; using ContinuousOutputIndexType = ContinuousIndex; std::vector outputCorners(numberOfInputCorners); for (unsigned int count = 0; count < numberOfInputCorners; ++count) { ContinuousInputIndexType currentInputCornerIndex; currentInputCornerIndex.Fill(0); unsigned int localCount = count; // For each dimension, set the current index to either // the highest or lowest index along this dimension. // Since we need all the space covered by the input image to // be taken into account, including the half-pixel border, // we start half a pixel before index 0 and stop half a pixel // after size for (unsigned int dim = 0; dim < InputImageType::ImageDimension; ++dim) { if (localCount % 2) { currentInputCornerIndex[dim] = inputRegion.GetIndex(dim) + inputRegion.GetSize(dim) + 0.5; } else { currentInputCornerIndex[dim] = inputRegion.GetIndex(dim) - 0.5; } localCount /= 2; } using InputPointType = Point; using OutputPointType = Point; InputPointType inputPoint; OutputPointType outputPoint; inputImage->TransformContinuousIndexToPhysicalPoint(currentInputCornerIndex, inputPoint); if (transform != nullptr) { outputPoint = transform->TransformPoint(inputPoint); } else { // if InputDimension < OutputDimension then embed point in Output space. // else if InputDimension == OutputDimension copy the points. // else if InputDimension > OutputDimension project the point to first N-Dimensions of Output space. outputPoint.Fill(0.0); for (unsigned int d = 0; d < std::min(inputPoint.GetPointDimension(), outputPoint.GetPointDimension()); ++d) { outputPoint[d] = inputPoint[d]; } } outputCorners[count] = outputImage->template TransformPhysicalPointToContinuousIndex(outputPoint); } // Compute a rectangular region from the vector of corner indexes for (unsigned int dim = 0; dim < OutputImageType::ImageDimension; ++dim) { // Initialize index to the highest possible value outputRegion.SetIndex(dim, NumericTraits::max()); // For each dimension, set the output index to the minimum // of the corners' indexes, and the output size to their maximum for (unsigned int count = 0; count < numberOfInputCorners; ++count) { auto continuousIndexFloor = Math::Floor(outputCorners[count][dim]); if (continuousIndexFloor < outputRegion.GetIndex(dim)) { outputRegion.SetIndex(dim, continuousIndexFloor); } auto continuousIndexCeil = Math::Ceil(outputCorners[count][dim]); if (continuousIndexCeil > static_cast(outputRegion.GetSize(dim))) { outputRegion.SetSize(dim, continuousIndexCeil); } } // The size is actually the difference between maximum and minimum index, // so subtract the index outputRegion.SetSize(dim, outputRegion.GetSize(dim) - outputRegion.GetIndex(dim)); } // Make sure this region remains contained in the LargestPossibleRegion outputRegion.Crop(outputImage->GetLargestPossibleRegion()); return outputRegion; } } // end namespace itk #endif