mirror of https://gitee.com/openkylin/vtk9.git
1168 lines
36 KiB
C++
1168 lines
36 KiB
C++
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: vtkVolumeRayCastSpaceLeapingImageFilter.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 "vtkVolumeRayCastSpaceLeapingImageFilter.h"
|
|
|
|
#include "vtkDataArray.h"
|
|
#include "vtkImageData.h"
|
|
#include "vtkInformation.h"
|
|
#include "vtkInformationVector.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkPointData.h"
|
|
#include "vtkStreamingDemandDrivenPipeline.h"
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
#ifdef vtkVolumeRayCastSpaceLeapingImageFilter_DEBUG
|
|
#include "vtkMetaImageWriter.h"
|
|
#endif
|
|
|
|
#include <cmath>
|
|
|
|
// Space leaping block size
|
|
#define VTK_SL_BLK 4
|
|
|
|
//------------------------------------------------------------------------------
|
|
vtkStandardNewMacro(vtkVolumeRayCastSpaceLeapingImageFilter);
|
|
vtkCxxSetObjectMacro(vtkVolumeRayCastSpaceLeapingImageFilter, CurrentScalars, vtkDataArray);
|
|
|
|
//------------------------------------------------------------------------------
|
|
vtkVolumeRayCastSpaceLeapingImageFilter::vtkVolumeRayCastSpaceLeapingImageFilter()
|
|
{
|
|
this->ComputeMinMax = 0;
|
|
this->ComputeGradientOpacity = 0;
|
|
this->UpdateGradientOpacityFlags = 0;
|
|
this->IndependentComponents = 1;
|
|
this->CurrentScalars = nullptr;
|
|
this->MinNonZeroScalarIndex = nullptr;
|
|
this->MinNonZeroGradientMagnitudeIndex = nullptr;
|
|
this->GradientMagnitude = nullptr;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
this->TableSize[i] = 0;
|
|
this->TableShift[i] = 0;
|
|
this->TableScale[i] = 1;
|
|
this->ScalarOpacityTable[i] = nullptr;
|
|
this->GradientOpacityTable[i] = nullptr;
|
|
}
|
|
this->Cache = nullptr;
|
|
|
|
// Ensure that no splits occur along X or Y axes when multithreading,
|
|
// there seems to be a bug with the increments that requires this.
|
|
this->SplitPath[0] = 2;
|
|
this->SplitPathLength = 1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
vtkVolumeRayCastSpaceLeapingImageFilter::~vtkVolumeRayCastSpaceLeapingImageFilter()
|
|
{
|
|
this->SetCurrentScalars(nullptr);
|
|
delete[] this->MinNonZeroScalarIndex;
|
|
delete[] this->MinNonZeroGradientMagnitudeIndex;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkVolumeRayCastSpaceLeapingImageFilter::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os, indent);
|
|
|
|
os << indent << "ComputeMinMax: " << this->ComputeMinMax << "\n";
|
|
os << indent << "ComputeGradientOpacity: " << this->ComputeGradientOpacity << "\n";
|
|
os << indent << "UpdateGradientOpacityFlags: " << this->UpdateGradientOpacityFlags << "\n";
|
|
os << indent << "IndependentComponents: " << this->IndependentComponents << "\n";
|
|
os << indent << "CurrentScalars: " << this->CurrentScalars << "\n";
|
|
// this->TableShift
|
|
// this->TableScale
|
|
// this->TableSize
|
|
// this->ScalarOpacityTable
|
|
// this->GradientOpacityTable
|
|
// this->MinNonZeroScalarIndex
|
|
// this->MinNonZeroGradientMagnitudeIndex
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkVolumeRayCastSpaceLeapingImageFilter::RequestUpdateExtent(
|
|
vtkInformation* vtkNotUsed(request), vtkInformationVector** inputVector,
|
|
vtkInformationVector* vtkNotUsed(outputVector))
|
|
{
|
|
// get the info objects
|
|
vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
|
|
|
|
// Ask for the whole input
|
|
|
|
int wholeExtent[6];
|
|
inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), wholeExtent);
|
|
inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), wholeExtent, 6);
|
|
|
|
return 1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkVolumeRayCastSpaceLeapingImageFilter ::SetCache(vtkImageData* cache)
|
|
{
|
|
// Do not reference count it to avoid reference counting loops
|
|
this->Cache = cache;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkVolumeRayCastSpaceLeapingImageFilter ::InternalRequestUpdateExtent(
|
|
int* inExt, int* wholeExtent)
|
|
{
|
|
int dim[3];
|
|
|
|
// We group four cells (which require 5 samples) into one element in the min/max tree
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
// size of the input image.
|
|
dim[i] = wholeExtent[2 * i + 1] - wholeExtent[2 * i] + 1;
|
|
|
|
inExt[2 * i] = 0; // The output extent is 0 based.
|
|
inExt[2 * i + 1] = (dim[i] < 2) ? (0) : (static_cast<int>((dim[i] - 2) / VTK_SL_BLK));
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void vtkVolumeRayCastSpaceLeapingImageFilterClearOutput(
|
|
vtkImageData* outData, int outExt[6], int nComponents)
|
|
{
|
|
unsigned short* tmpPtr = static_cast<unsigned short*>(outData->GetScalarPointerForExtent(outExt));
|
|
|
|
// Get increments to march through the thread's output extents
|
|
|
|
vtkIdType outInc0, outInc1, outInc2;
|
|
outData->GetContinuousIncrements(outExt, outInc0, outInc1, outInc2);
|
|
// A. Initialize the arrays with a blank flag.
|
|
|
|
int i, j, k;
|
|
int c;
|
|
for (k = outExt[4]; k <= outExt[5]; ++k, tmpPtr += outInc2)
|
|
{
|
|
for (j = outExt[2]; j <= outExt[3]; ++j, tmpPtr += outInc1)
|
|
{
|
|
for (i = outExt[0]; i <= outExt[1]; ++i)
|
|
{
|
|
for (c = 0; c < nComponents; ++c)
|
|
{
|
|
*(tmpPtr++) = 0xffff; // Min Scalar
|
|
*(tmpPtr++) = 0; // Max Scalar
|
|
*(tmpPtr++) = 0; // Max Gradient Magnitude and
|
|
} // Flag computed from transfer functions
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkVolumeRayCastSpaceLeapingImageFilter ::ComputeInputExtentsForOutput(
|
|
int inExt[6], int inDim[3], int outExt[6], vtkImageData* inData)
|
|
{
|
|
int inWholeExt[6];
|
|
inData->GetExtent(inWholeExt);
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
inExt[2 * i] = outExt[2 * i] * VTK_SL_BLK + inWholeExt[2 * i];
|
|
|
|
// Extra +1 needed here since we group four cells (which require 5
|
|
// samples) into one element in the min/max tree
|
|
inExt[2 * i + 1] = (outExt[2 * i + 1] + 1) * VTK_SL_BLK + inWholeExt[2 * i] + 1;
|
|
|
|
// Clip the extents with the whole extent.
|
|
if (inExt[2 * i] < inWholeExt[2 * i])
|
|
{
|
|
inExt[2 * i] = inWholeExt[2 * i];
|
|
}
|
|
if (inExt[2 * i + 1] > inWholeExt[2 * i + 1])
|
|
{
|
|
inExt[2 * i + 1] = inWholeExt[2 * i + 1];
|
|
}
|
|
|
|
inDim[i] = inExt[2 * i + 1] - inExt[2 * i] + 1;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Fill in the min-max space leaping information.
|
|
template <class T>
|
|
void vtkVolumeRayCastSpaceLeapingImageFilterMinMaxExecute(
|
|
vtkVolumeRayCastSpaceLeapingImageFilter* self, vtkImageData* inData, vtkImageData* outData,
|
|
int outExt[6], T)
|
|
{
|
|
|
|
// the number of independent components for which we need to keep track of
|
|
// min/max
|
|
vtkDataArray* scalars = self->GetCurrentScalars();
|
|
const int components = scalars->GetNumberOfComponents();
|
|
const int independent = self->GetIndependentComponents();
|
|
const int nComponents = (independent) ? components : 1;
|
|
|
|
// B. Now fill in the max-min-gradient volume structure
|
|
|
|
// B.1 First compute the extents of the input that contribute to this structure
|
|
|
|
int inExt[6], inWholeExt[6];
|
|
int inDim[3];
|
|
int outWholeDim[3];
|
|
vtkVolumeRayCastSpaceLeapingImageFilter::ComputeInputExtentsForOutput(
|
|
inExt, inDim, outExt, inData);
|
|
inData->GetExtent(inWholeExt);
|
|
outData->GetDimensions(outWholeDim);
|
|
|
|
float shift[4], scale[4];
|
|
self->GetTableShift(shift);
|
|
self->GetTableScale(scale);
|
|
|
|
// B.2 Get increments to march through the input extents
|
|
|
|
vtkIdType inInc0, inInc1, inInc2;
|
|
inData->GetContinuousIncrements(scalars, inExt, inInc0, inInc1, inInc2);
|
|
|
|
// Get increments to march through the output extents
|
|
|
|
const vtkIdType outInc0 = 3 * nComponents;
|
|
const vtkIdType outInc1 = outInc0 * outWholeDim[0];
|
|
const vtkIdType outInc2 = outInc1 * outWholeDim[1];
|
|
|
|
// B.3 Now fill in the min-max volume.
|
|
|
|
int i, j, k;
|
|
int c;
|
|
int sx1, sx2, sy1, sy2, sz1, sz2;
|
|
int x, y, z;
|
|
|
|
T* dptr = static_cast<T*>(scalars->GetVoidPointer(0));
|
|
unsigned short val;
|
|
unsigned short* outBasePtr = static_cast<unsigned short*>(outData->GetScalarPointer());
|
|
|
|
// Initialize pointer to the starting extents given by inExt.
|
|
dptr += self->ComputeOffset(inExt, inWholeExt, nComponents);
|
|
|
|
// The pointer into the space-leaping output volume.
|
|
unsigned short *tmpPtr, *tmpPtrK, *tmpPtrJ, *tmpPtrI;
|
|
|
|
for (k = 0; k < inDim[2]; k++, dptr += inInc2)
|
|
{
|
|
sz1 = (k < 1) ? (0) : ((k - 1) / 4);
|
|
sz2 = ((k) / 4);
|
|
sz2 = (k == inDim[2] - 1) ? (sz1) : (sz2);
|
|
|
|
sz1 += outExt[4];
|
|
sz2 += outExt[4];
|
|
|
|
// Bounds check
|
|
if (sz2 > outExt[5])
|
|
{
|
|
sz2 = outExt[5];
|
|
}
|
|
|
|
tmpPtrK = outBasePtr + sz1 * outInc2;
|
|
|
|
for (j = 0; j < inDim[1]; j++, dptr += inInc1)
|
|
{
|
|
sy1 = (j < 1) ? (0) : ((j - 1) / 4);
|
|
sy2 = ((j) / 4);
|
|
sy2 = (j == inDim[1] - 1) ? (sy1) : (sy2);
|
|
|
|
sy1 += outExt[2];
|
|
sy2 += outExt[2];
|
|
|
|
// Bounds check
|
|
if (sy2 > outExt[3])
|
|
{
|
|
sy2 = outExt[3];
|
|
}
|
|
|
|
tmpPtrJ = tmpPtrK + sy1 * outInc1;
|
|
|
|
for (i = 0; i < inDim[0]; i++)
|
|
{
|
|
sx1 = (i < 1) ? (0) : ((i - 1) / 4);
|
|
sx2 = ((i) / 4);
|
|
sx2 = (i == inDim[0] - 1) ? (sx1) : (sx2);
|
|
|
|
sx1 += outExt[0];
|
|
sx2 += outExt[0];
|
|
|
|
// Bounds check
|
|
if (sx2 > outExt[1])
|
|
{
|
|
sx2 = outExt[1];
|
|
}
|
|
|
|
tmpPtrI = tmpPtrJ + sx1 * outInc0;
|
|
|
|
for (c = 0; c < nComponents; c++, tmpPtrI += 3)
|
|
{
|
|
if (independent)
|
|
{
|
|
val = static_cast<unsigned short>((*dptr + shift[c]) * scale[c]);
|
|
++dptr;
|
|
}
|
|
else
|
|
{
|
|
val = static_cast<unsigned short>(
|
|
(*(dptr + components - 1) + shift[components - 1]) * scale[components - 1]);
|
|
dptr += components;
|
|
}
|
|
|
|
for (z = sz1; z <= sz2; z++)
|
|
{
|
|
for (y = sy1; y <= sy2; y++)
|
|
{
|
|
tmpPtr = tmpPtrI + (z - sz1) * outInc2 + (y - sy1) * outInc1;
|
|
for (x = sx1; x <= sx2; x++, tmpPtr += outInc0)
|
|
{
|
|
if (val < tmpPtr[0])
|
|
{
|
|
tmpPtr[0] = val;
|
|
}
|
|
if (val > tmpPtr[1])
|
|
{
|
|
tmpPtr[1] = val;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Fill in the maximum gradient magnitude space leaping information.
|
|
template <class T>
|
|
void vtkVolumeRayCastSpaceLeapingImageFilterMaxGradientMagnitudeExecute(
|
|
vtkVolumeRayCastSpaceLeapingImageFilter* self, vtkImageData* inData, vtkImageData* outData,
|
|
int outExt[6], T)
|
|
{
|
|
// the number of independent components for which we need to keep track of
|
|
// min/max
|
|
const int nComponents = self->GetNumberOfIndependentComponents();
|
|
|
|
// B. Now fill in the max-min-gradient volume structure
|
|
|
|
// B.1 First compute the extents of the input that contribute to this structure
|
|
|
|
int inExt[6], inWholeExt[6];
|
|
int inDim[3];
|
|
int outWholeDim[3];
|
|
vtkVolumeRayCastSpaceLeapingImageFilter::ComputeInputExtentsForOutput(
|
|
inExt, inDim, outExt, inData);
|
|
inData->GetExtent(inWholeExt);
|
|
outData->GetDimensions(outWholeDim);
|
|
|
|
float shift[4], scale[4];
|
|
self->GetTableShift(shift);
|
|
self->GetTableScale(scale);
|
|
|
|
// B.2 Get increments to march through the input extents
|
|
|
|
vtkIdType inInc0, inInc1, inInc2;
|
|
inData->GetContinuousIncrements(self->GetCurrentScalars(), inExt, inInc0, inInc1, inInc2);
|
|
|
|
// Get increments to march through the output extents
|
|
|
|
const vtkIdType outInc0 = 3 * nComponents;
|
|
const vtkIdType outInc1 = outInc0 * outWholeDim[0];
|
|
const vtkIdType outInc2 = outInc1 * outWholeDim[1];
|
|
|
|
// B.3 Now fill in the min-max volume.
|
|
|
|
int i, j, k;
|
|
int c;
|
|
int sx1, sx2, sy1, sy2, sz1, sz2;
|
|
int x, y, z;
|
|
|
|
unsigned char val;
|
|
unsigned short* outBasePtr = static_cast<unsigned short*>(outData->GetScalarPointer());
|
|
|
|
// The pointer into the space-leaping output volume.
|
|
unsigned short *tmpPtr, *tmpPtrK, *tmpPtrJ, *tmpPtrI;
|
|
|
|
// pointer to the slice of the gradient magnitude
|
|
unsigned char** gsptr = self->GetGradientMagnitude();
|
|
|
|
// Initialize pointer to the starting extents given by inExt.
|
|
gsptr += (inExt[4] - inWholeExt[4]);
|
|
|
|
for (k = 0; k < inDim[2]; k++, ++gsptr)
|
|
{
|
|
sz1 = (k < 1) ? (0) : ((k - 1) / 4);
|
|
sz2 = ((k) / 4);
|
|
sz2 = (k == inDim[2] - 1) ? (sz1) : (sz2);
|
|
|
|
sz1 += outExt[4];
|
|
sz2 += outExt[4];
|
|
|
|
// Bounds check
|
|
if (sz2 > outExt[5])
|
|
{
|
|
sz2 = outExt[5];
|
|
}
|
|
|
|
tmpPtrK = outBasePtr + sz1 * outInc2;
|
|
|
|
unsigned char* gptr = *gsptr;
|
|
|
|
for (j = 0; j < inDim[1]; j++, gptr += inInc1)
|
|
{
|
|
sy1 = (j < 1) ? (0) : ((j - 1) / 4);
|
|
sy2 = ((j) / 4);
|
|
sy2 = (j == inDim[1] - 1) ? (sy1) : (sy2);
|
|
|
|
sy1 += outExt[2];
|
|
sy2 += outExt[2];
|
|
|
|
// Bounds check
|
|
if (sy2 > outExt[3])
|
|
{
|
|
sy2 = outExt[3];
|
|
}
|
|
|
|
tmpPtrJ = tmpPtrK + sy1 * outInc1;
|
|
|
|
for (i = 0; i < inDim[0]; i++)
|
|
{
|
|
sx1 = (i < 1) ? (0) : ((i - 1) / 4);
|
|
sx2 = ((i) / 4);
|
|
sx2 = (i == inDim[0] - 1) ? (sx1) : (sx2);
|
|
|
|
sx1 += outExt[0];
|
|
sx2 += outExt[0];
|
|
|
|
// Bounds check
|
|
if (sx2 > outExt[1])
|
|
{
|
|
sx2 = outExt[1];
|
|
}
|
|
|
|
tmpPtrI = tmpPtrJ + sx1 * outInc0;
|
|
|
|
for (c = 0; c < nComponents; c++, tmpPtrI += 3)
|
|
{
|
|
val = *gptr;
|
|
++gptr;
|
|
|
|
for (z = sz1; z <= sz2; z++)
|
|
{
|
|
for (y = sy1; y <= sy2; y++)
|
|
{
|
|
tmpPtr = tmpPtrI + (z - sz1) * outInc2 + (y - sy1) * outInc1;
|
|
for (x = sx1; x <= sx2; x++, tmpPtr += outInc0)
|
|
{
|
|
|
|
// Need to keep track of max gradient magnitude in upper
|
|
// eight bits. No need to preserve lower eight (the flag)
|
|
// since we will be recomputing this.
|
|
if (val > (tmpPtr[2] >> 8))
|
|
{
|
|
tmpPtr[2] = (val << 8);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Optimized method that does both the following in one pass
|
|
// - Fill in the min-max space leaping information.
|
|
// - Fill in the maximum gradient magnitude space leaping information.
|
|
template <class T>
|
|
void vtkVolumeRayCastSpaceLeapingImageFilterMinMaxAndMaxGradientMagnitudeExecute(
|
|
vtkVolumeRayCastSpaceLeapingImageFilter* self, vtkImageData* inData, vtkImageData* outData,
|
|
int outExt[6], T)
|
|
{
|
|
// the number of independent components for which we need to keep track of
|
|
// min/max
|
|
vtkDataArray* scalars = self->GetCurrentScalars();
|
|
const int components = scalars->GetNumberOfComponents();
|
|
const int independent = self->GetIndependentComponents();
|
|
const int nComponents = (independent) ? components : 1;
|
|
|
|
// B.1 First compute the extents of the input that contribute to this structure
|
|
|
|
int inExt[6], inWholeExt[6];
|
|
int inDim[3];
|
|
int outWholeDim[3];
|
|
vtkVolumeRayCastSpaceLeapingImageFilter::ComputeInputExtentsForOutput(
|
|
inExt, inDim, outExt, inData);
|
|
inData->GetExtent(inWholeExt);
|
|
outData->GetDimensions(outWholeDim);
|
|
|
|
float shift[4], scale[4];
|
|
self->GetTableShift(shift);
|
|
self->GetTableScale(scale);
|
|
|
|
// B.2 Get increments to march through the input extents
|
|
|
|
vtkIdType inInc0, inInc1, inInc2;
|
|
inData->GetContinuousIncrements(scalars, inExt, inInc0, inInc1, inInc2);
|
|
|
|
// Get increments to march through the output extents
|
|
|
|
const vtkIdType outInc0 = 3 * nComponents;
|
|
const vtkIdType outInc1 = outInc0 * outWholeDim[0];
|
|
const vtkIdType outInc2 = outInc1 * outWholeDim[1];
|
|
|
|
// B.3 Now fill in the min-max and gradient max structure
|
|
|
|
int i, j, k;
|
|
int c;
|
|
int sx1, sx2, sy1, sy2, sz1, sz2;
|
|
int x, y, z;
|
|
|
|
T* dptr = static_cast<T*>(scalars->GetVoidPointer(0));
|
|
unsigned char val;
|
|
unsigned short minMaxVal;
|
|
unsigned short* outBasePtr = static_cast<unsigned short*>(outData->GetScalarPointer());
|
|
|
|
// pointer to the slice of the gradient magnitude
|
|
unsigned char** gsptr = self->GetGradientMagnitude();
|
|
|
|
// Initialize pointers to the starting extents given by inExt.
|
|
gsptr += (inExt[4] - inWholeExt[4]); // pointer to slice gradient
|
|
dptr += self->ComputeOffset(inExt, inWholeExt, nComponents);
|
|
|
|
// The pointer into the space-leaping output volume.
|
|
unsigned short *tmpPtr, *tmpPtrK, *tmpPtrJ, *tmpPtrI;
|
|
|
|
for (k = 0; k < inDim[2]; k++, dptr += inInc2, ++gsptr)
|
|
{
|
|
sz1 = (k < 1) ? (0) : ((k - 1) / 4);
|
|
sz2 = ((k) / 4);
|
|
sz2 = (k == inDim[2] - 1) ? (sz1) : (sz2);
|
|
|
|
sz1 += outExt[4];
|
|
sz2 += outExt[4];
|
|
|
|
// Bounds check
|
|
if (sz2 > outExt[5])
|
|
{
|
|
sz2 = outExt[5];
|
|
}
|
|
|
|
tmpPtrK = outBasePtr + sz1 * outInc2;
|
|
|
|
unsigned char* gptr = *gsptr;
|
|
|
|
for (j = 0; j < inDim[1]; j++, dptr += inInc1, gptr += inInc1)
|
|
{
|
|
sy1 = (j < 1) ? (0) : ((j - 1) / 4);
|
|
sy2 = ((j) / 4);
|
|
sy2 = (j == inDim[1] - 1) ? (sy1) : (sy2);
|
|
|
|
sy1 += outExt[2];
|
|
sy2 += outExt[2];
|
|
|
|
// Bounds check
|
|
if (sy2 > outExt[3])
|
|
{
|
|
sy2 = outExt[3];
|
|
}
|
|
|
|
tmpPtrJ = tmpPtrK + sy1 * outInc1;
|
|
|
|
for (i = 0; i < inDim[0]; i++)
|
|
{
|
|
sx1 = (i < 1) ? (0) : ((i - 1) / 4);
|
|
sx2 = ((i) / 4);
|
|
sx2 = (i == inDim[0] - 1) ? (sx1) : (sx2);
|
|
|
|
sx1 += outExt[0];
|
|
sx2 += outExt[0];
|
|
|
|
// Bounds check
|
|
if (sx2 > outExt[1])
|
|
{
|
|
sx2 = outExt[1];
|
|
}
|
|
|
|
tmpPtrI = tmpPtrJ + sx1 * outInc0;
|
|
|
|
for (c = 0; c < nComponents; c++, tmpPtrI += 3)
|
|
{
|
|
val = *gptr;
|
|
++gptr;
|
|
|
|
if (independent)
|
|
{
|
|
minMaxVal = static_cast<unsigned short>((*dptr + shift[c]) * scale[c]);
|
|
++dptr;
|
|
}
|
|
else
|
|
{
|
|
minMaxVal = static_cast<unsigned short>(
|
|
(*(dptr + components - 1) + shift[components - 1]) * scale[components - 1]);
|
|
dptr += components;
|
|
}
|
|
|
|
for (z = sz1; z <= sz2; z++)
|
|
{
|
|
for (y = sy1; y <= sy2; y++)
|
|
{
|
|
|
|
tmpPtr = tmpPtrI + (z - sz1) * outInc2 + (y - sy1) * outInc1;
|
|
for (x = sx1; x <= sx2; x++, tmpPtr += outInc0)
|
|
{
|
|
|
|
if (minMaxVal < tmpPtr[0])
|
|
{
|
|
tmpPtr[0] = minMaxVal;
|
|
}
|
|
if (minMaxVal > tmpPtr[1])
|
|
{
|
|
tmpPtr[1] = minMaxVal;
|
|
}
|
|
if (val > (tmpPtr[2] >> 8))
|
|
{
|
|
tmpPtr[2] = (val << 8);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkVolumeRayCastSpaceLeapingImageFilter ::FillScalarAndGradientOpacityFlags(
|
|
vtkImageData* outData, int outExt[6])
|
|
{
|
|
// Get increments to march through the output
|
|
|
|
vtkIdType outInc0, outInc1, outInc2;
|
|
outData->GetContinuousIncrements(outExt, outInc0, outInc1, outInc2);
|
|
|
|
// Now process the flags
|
|
|
|
unsigned short* tmpPtr = static_cast<unsigned short*>(outData->GetScalarPointerForExtent(outExt));
|
|
unsigned short* minNonZeroScalarIndex = this->GetMinNonZeroScalarIndex();
|
|
unsigned char* minNonZeroGradientMagnitudeIndex = this->GetMinNonZeroGradientMagnitudeIndex();
|
|
|
|
int i, j, k, c, loop;
|
|
|
|
// the number of independent components for which we need to keep track of
|
|
// min/max/gradient
|
|
const int nComponents = this->GetNumberOfIndependentComponents();
|
|
|
|
// Loop over the data with in the supplied extents
|
|
|
|
for (k = outExt[4]; k <= outExt[5]; ++k, tmpPtr += outInc2)
|
|
{
|
|
for (j = outExt[2]; j <= outExt[3]; ++j, tmpPtr += outInc1)
|
|
{
|
|
for (i = outExt[0]; i <= outExt[1]; ++i)
|
|
{
|
|
for (c = 0; c < nComponents; ++c, tmpPtr += 3)
|
|
{
|
|
|
|
// We definite have 0 opacity because our maximum scalar value in
|
|
// this region is below the minimum scalar value with non-zero opacity
|
|
// for this component
|
|
if (tmpPtr[1] < minNonZeroScalarIndex[c])
|
|
{
|
|
tmpPtr[2] &= 0xff00;
|
|
}
|
|
// We have 0 opacity because we are using gradient magnitudes and
|
|
// the maximum gradient magnitude in this area is below the minimum
|
|
// gradient magnitude with non-zero opacity for this component
|
|
else if ((tmpPtr[2] >> 8) < minNonZeroGradientMagnitudeIndex[c])
|
|
{
|
|
tmpPtr[2] &= 0xff00;
|
|
}
|
|
// We definitely have non-zero opacity because our minimum scalar
|
|
// value is lower than our first scalar with non-zero opacity, and
|
|
// the maximum scalar value is greater than this threshold - so
|
|
// we must encounter scalars with opacity in between
|
|
else if (tmpPtr[0] < minNonZeroScalarIndex[c])
|
|
{
|
|
tmpPtr[2] &= 0xff00;
|
|
tmpPtr[2] |= 0x0001;
|
|
}
|
|
// We have to search between min scalar value and the
|
|
// max scalar stored in the minmax volume to look for non-zero
|
|
// opacity since both values must be above our first non-zero
|
|
// threshold so we don't have information in this area
|
|
else
|
|
{
|
|
for (loop = tmpPtr[0]; loop <= tmpPtr[1]; ++loop)
|
|
{
|
|
if (this->ScalarOpacityTable[c][loop])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (loop <= tmpPtr[1])
|
|
{
|
|
tmpPtr[2] &= 0xff00;
|
|
tmpPtr[2] |= 0x0001;
|
|
}
|
|
else
|
|
{
|
|
tmpPtr[2] &= 0xff00;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkVolumeRayCastSpaceLeapingImageFilter ::FillScalarOpacityFlags(
|
|
vtkImageData* outData, int outExt[6])
|
|
{
|
|
// Get increments to march through the output
|
|
|
|
vtkIdType outInc0, outInc1, outInc2;
|
|
outData->GetContinuousIncrements(outExt, outInc0, outInc1, outInc2);
|
|
|
|
// Now process the flags
|
|
|
|
unsigned short* tmpPtr = static_cast<unsigned short*>(outData->GetScalarPointerForExtent(outExt));
|
|
unsigned short* minNonZeroScalarIndex = this->GetMinNonZeroScalarIndex();
|
|
|
|
int i, j, k, c, loop;
|
|
|
|
// the number of independent components for which we need to keep track of
|
|
// min/max/gradient
|
|
const int nComponents = this->GetNumberOfIndependentComponents();
|
|
|
|
// Loop over the data with in the supplied extents
|
|
|
|
for (k = outExt[4]; k <= outExt[5]; ++k, tmpPtr += outInc2)
|
|
{
|
|
for (j = outExt[2]; j <= outExt[3]; ++j, tmpPtr += outInc1)
|
|
{
|
|
for (i = outExt[0]; i <= outExt[1]; ++i)
|
|
{
|
|
for (c = 0; c < nComponents; ++c, tmpPtr += 3)
|
|
{
|
|
|
|
// We definite have 0 opacity because our maximum scalar value in
|
|
// this region is below the minimum scalar value with non-zero opacity
|
|
// for this component
|
|
if (tmpPtr[1] < minNonZeroScalarIndex[c])
|
|
{
|
|
tmpPtr[2] &= 0xff00;
|
|
}
|
|
// We definitely have non-zero opacity because our minimum scalar
|
|
// value is lower than our first scalar with non-zero opacity, and
|
|
// the maximum scalar value is greater than this threshold - so
|
|
// we must encounter scalars with opacity in between
|
|
else if (tmpPtr[0] < minNonZeroScalarIndex[c])
|
|
{
|
|
tmpPtr[2] &= 0xff00;
|
|
tmpPtr[2] |= 0x0001;
|
|
}
|
|
// We have to search between min scalar value and the
|
|
// max scalar stored in the minmax volume to look for non-zero
|
|
// opacity since both values must be above our first non-zero
|
|
// threshold so we don't have information in this area
|
|
else
|
|
{
|
|
for (loop = tmpPtr[0]; loop <= tmpPtr[1]; ++loop)
|
|
{
|
|
if (this->ScalarOpacityTable[c][loop])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (loop <= tmpPtr[1])
|
|
{
|
|
tmpPtr[2] &= 0xff00;
|
|
tmpPtr[2] |= 0x0001;
|
|
}
|
|
else
|
|
{
|
|
tmpPtr[2] &= 0xff00;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkVolumeRayCastSpaceLeapingImageFilter::ThreadedRequestData(
|
|
vtkInformation* vtkNotUsed(request), vtkInformationVector** vtkNotUsed(inputVector),
|
|
vtkInformationVector* vtkNotUsed(outputVector), vtkImageData*** inData, vtkImageData** outData,
|
|
int outExt[6], int vtkNotUsed(id))
|
|
{
|
|
#ifdef vtkVolumeRayCastSpaceLeapingImageFilter_DEBUG
|
|
std::cout << "Thread id = " << id << std::endl;
|
|
#endif
|
|
|
|
// A. Initialize the data with a blank flag.
|
|
|
|
// - Get the number of independent components for which we need to keep
|
|
// track of min/max
|
|
if (!this->GetCurrentScalars())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const int components = this->GetCurrentScalars()->GetNumberOfComponents();
|
|
const int nComponents = (this->GetIndependentComponents()) ? components : 1;
|
|
|
|
// Clear the output if we are computing the min-max. In other cases, we
|
|
// will be re-using the cache. (See the method AllocateOutputData)
|
|
|
|
if (this->ComputeMinMax)
|
|
{
|
|
vtkVolumeRayCastSpaceLeapingImageFilterClearOutput(outData[0], outExt, nComponents);
|
|
}
|
|
|
|
// If only scalar min-max need to be re-computed
|
|
|
|
if (this->ComputeMinMax && !this->ComputeGradientOpacity)
|
|
{
|
|
int scalarType = this->CurrentScalars->GetDataType();
|
|
switch (scalarType)
|
|
{
|
|
vtkTemplateMacro(vtkVolumeRayCastSpaceLeapingImageFilterMinMaxExecute(
|
|
this, inData[0][0], outData[0], outExt, static_cast<VTK_TT>(0)));
|
|
default:
|
|
vtkErrorMacro("Unknown scalar type");
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If only gradient max needs to be recomputed
|
|
|
|
else if (this->ComputeGradientOpacity && !this->ComputeMinMax)
|
|
{
|
|
int scalarType = this->CurrentScalars->GetDataType();
|
|
switch (scalarType)
|
|
{
|
|
vtkTemplateMacro(vtkVolumeRayCastSpaceLeapingImageFilterMaxGradientMagnitudeExecute(
|
|
this, inData[0][0], outData[0], outExt, static_cast<VTK_TT>(0)));
|
|
default:
|
|
vtkErrorMacro("Unknown scalar type");
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If both scalar min-max need to be re-computed and gradient max needs to
|
|
// be re-computed
|
|
|
|
else if (this->ComputeGradientOpacity && this->ComputeMinMax)
|
|
{
|
|
int scalarType = this->CurrentScalars->GetDataType();
|
|
switch (scalarType)
|
|
{
|
|
vtkTemplateMacro(vtkVolumeRayCastSpaceLeapingImageFilterMinMaxAndMaxGradientMagnitudeExecute(
|
|
this, inData[0][0], outData[0], outExt, static_cast<VTK_TT>(0)));
|
|
default:
|
|
vtkErrorMacro("Unknown scalar type");
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Update the flags now for this extent. There are two specialized methods
|
|
// here, depending on what mode we are in, so that we may do the flag update
|
|
// in one pass through the data.
|
|
|
|
if (this->UpdateGradientOpacityFlags)
|
|
{
|
|
// Process the flags based on the computed min-max volume
|
|
this->FillScalarAndGradientOpacityFlags(outData[0], outExt);
|
|
}
|
|
else
|
|
{
|
|
this->FillScalarOpacityFlags(outData[0], outExt);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Override superclass method to maintain a last successful execution time
|
|
int vtkVolumeRayCastSpaceLeapingImageFilter::RequestData(
|
|
vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector)
|
|
{
|
|
#ifdef vtkVolumeRayCastSpaceLeapingImageFilter_DEBUG
|
|
cout << "ComputingGradientOpacity: " << ComputeGradientOpacity;
|
|
cout << " ComputingMinMax: " << ComputeMinMax << " UpdatingFlags: 1" << endl;
|
|
#endif
|
|
|
|
// Find the first non-zero scalar opacity and gradient opacity points on
|
|
// the respective transfer functions
|
|
|
|
this->ComputeFirstNonZeroOpacityIndices();
|
|
|
|
// The actual work is done in the line below.
|
|
if (this->Superclass::RequestData(request, inputVector, outputVector))
|
|
{
|
|
|
|
// if we recomputed the first two shorts in the output, update this.
|
|
if (this->ComputeGradientOpacity || this->ComputeMinMax)
|
|
{
|
|
this->LastMinMaxBuildTime.Modified();
|
|
}
|
|
|
|
// flags were rebuilt. update this.
|
|
this->LastMinMaxFlagTime.Modified();
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkVolumeRayCastSpaceLeapingImageFilter::RequestInformation(
|
|
vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector)
|
|
{
|
|
this->vtkImageAlgorithm::RequestInformation(request, inputVector, outputVector);
|
|
|
|
vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);
|
|
vtkInformation* outInfo = outputVector->GetInformationObject(0);
|
|
|
|
// Output scalar type is unsigned short,
|
|
// 3 unsigned short values are needed to represent the min, max and gradient,
|
|
// flag values. This is to be done for each independent component.
|
|
|
|
vtkDataObject::SetPointDataActiveScalarInfo(
|
|
outInfo, VTK_UNSIGNED_SHORT, 3 * this->GetNumberOfIndependentComponents());
|
|
|
|
// The whole extent of the output is the whole extent of the input divided
|
|
// by the block size along each dimension
|
|
|
|
int outWholeExt[6], inWholeExtent[6];
|
|
inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), inWholeExtent);
|
|
this->InternalRequestUpdateExtent(outWholeExt, inWholeExtent);
|
|
|
|
outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), outWholeExt, 6);
|
|
outInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), outWholeExt, 6);
|
|
|
|
return 1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkVolumeRayCastSpaceLeapingImageFilter ::GetNumberOfIndependentComponents()
|
|
{
|
|
// the number of independent components for which we need to keep track of
|
|
// min/max
|
|
if (this->CurrentScalars)
|
|
{
|
|
const int components = this->CurrentScalars->GetNumberOfComponents();
|
|
return ((this->IndependentComponents) ? components : 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkVolumeRayCastSpaceLeapingImageFilter ::ComputeFirstNonZeroOpacityIndices()
|
|
{
|
|
// Find the first non-zero scalar opacity and gradient opacity points on
|
|
// the respective transfer functions
|
|
|
|
const int nComponents = this->GetNumberOfIndependentComponents();
|
|
|
|
// Initialize these arrays.
|
|
delete[] this->MinNonZeroScalarIndex;
|
|
this->MinNonZeroScalarIndex = nullptr;
|
|
delete[] this->MinNonZeroGradientMagnitudeIndex;
|
|
this->MinNonZeroGradientMagnitudeIndex = nullptr;
|
|
|
|
// Update the flags now
|
|
int i;
|
|
this->MinNonZeroScalarIndex = new unsigned short[nComponents];
|
|
for (int c = 0; c < nComponents; c++)
|
|
{
|
|
for (i = 0; i < this->TableSize[c]; i++)
|
|
{
|
|
if (this->ScalarOpacityTable[c][i])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
this->MinNonZeroScalarIndex[c] = i;
|
|
}
|
|
|
|
this->MinNonZeroGradientMagnitudeIndex = new unsigned char[nComponents];
|
|
for (int c = 0; c < nComponents; c++)
|
|
{
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
if (this->GradientOpacityTable[c][i])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
this->MinNonZeroGradientMagnitudeIndex[c] = i;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
unsigned short* vtkVolumeRayCastSpaceLeapingImageFilter ::GetMinNonZeroScalarIndex()
|
|
{
|
|
return this->MinNonZeroScalarIndex;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
unsigned char* vtkVolumeRayCastSpaceLeapingImageFilter ::GetMinNonZeroGradientMagnitudeIndex()
|
|
{
|
|
return this->MinNonZeroGradientMagnitudeIndex;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkVolumeRayCastSpaceLeapingImageFilter ::SetGradientMagnitude(
|
|
unsigned char** gradientMagnitude)
|
|
{
|
|
this->GradientMagnitude = gradientMagnitude;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
unsigned char** vtkVolumeRayCastSpaceLeapingImageFilter ::GetGradientMagnitude()
|
|
{
|
|
return this->GradientMagnitude;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkVolumeRayCastSpaceLeapingImageFilter ::SetScalarOpacityTable(int c, unsigned short* t)
|
|
{
|
|
this->ScalarOpacityTable[c] = t;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkVolumeRayCastSpaceLeapingImageFilter ::SetGradientOpacityTable(int c, unsigned short* t)
|
|
{
|
|
this->GradientOpacityTable[c] = t;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
unsigned short* vtkVolumeRayCastSpaceLeapingImageFilter ::GetMinMaxVolume(int size[4])
|
|
{
|
|
if (vtkImageData* output = this->GetOutput())
|
|
{
|
|
int dims[3];
|
|
output->GetDimensions(dims);
|
|
size[0] = dims[0];
|
|
size[1] = dims[1];
|
|
size[2] = dims[2];
|
|
size[3] = this->GetNumberOfIndependentComponents();
|
|
|
|
return static_cast<unsigned short*>(output->GetScalarPointer());
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Fill in the min-max space leaping information.
|
|
vtkIdType vtkVolumeRayCastSpaceLeapingImageFilter ::ComputeOffset(
|
|
const int ext[6], const int wholeExt[6], int nComponents)
|
|
{
|
|
int wDim[3] = { wholeExt[1] - wholeExt[0] + 1, wholeExt[3] - wholeExt[2] + 1,
|
|
wholeExt[5] - wholeExt[4] + 1 };
|
|
|
|
// computation is done in parts to avoid int overflow
|
|
vtkIdType offset = ext[4] - wholeExt[4];
|
|
offset *= wDim[1];
|
|
offset += ext[2] - wholeExt[2];
|
|
offset *= wDim[0];
|
|
offset += ext[0] - wholeExt[0];
|
|
offset *= nComponents;
|
|
|
|
return offset;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Allocate the output data, caching if necessary. Caching may result in
|
|
// invalid outputs and should be turned on, only when this filter is used
|
|
// as an internal ivar of the vtkFixedPointVolumeRayCastMapper.
|
|
void vtkVolumeRayCastSpaceLeapingImageFilter ::AllocateOutputData(
|
|
vtkImageData* output, vtkInformation* outInfo, int* uExtent)
|
|
{
|
|
// set the extent to be the update extent
|
|
output->SetExtent(uExtent);
|
|
|
|
if (this->Cache)
|
|
{
|
|
|
|
int extent[6];
|
|
this->Cache->GetExtent(extent);
|
|
if (extent[0] == uExtent[0] && extent[1] == uExtent[1] && extent[2] == uExtent[2] &&
|
|
extent[3] == uExtent[3] && extent[4] == uExtent[4] && extent[5] == uExtent[5] &&
|
|
this->Cache->GetNumberOfScalarComponents() == output->GetNumberOfScalarComponents())
|
|
{
|
|
// Reuse the cache since it has the same dimensions. We may not be
|
|
// updating all flags
|
|
|
|
// This is absolutely scary code, if used as a standard VTK imaging filter,
|
|
// but since the filter will be used only as an iVar of
|
|
// vtkFixedPointVolumeRayCastMapper, we need a caching mechanism to avoid
|
|
// reallocation of memory and re-update of certain bits in the Min-max
|
|
// structure. In the interest of speed, we resort to a wee bit of ugly
|
|
// code.
|
|
output->GetPointData()->SetScalars(this->Cache->GetPointData()->GetScalars());
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Otherwise allocate output afresh
|
|
output->AllocateScalars(outInfo);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
vtkImageData* vtkVolumeRayCastSpaceLeapingImageFilter ::AllocateOutputData(
|
|
vtkDataObject* output, vtkInformation* outInfo)
|
|
{
|
|
// Call the superclass method
|
|
return vtkImageAlgorithm::AllocateOutputData(output, outInfo);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
#ifdef vtkVolumeRayCastSpaceLeapingImageFilter_DEBUG
|
|
void vtkVolumeRayCastSpaceLeapingImageFilter ::WriteMinMaxVolume(
|
|
int component, unsigned short* minMaxVolume, int minMaxVolumeSize[4], const char* filename)
|
|
{
|
|
vtkImageData* image = vtkImageData::New();
|
|
image->SetExtent(
|
|
0, minMaxVolumeSize[0] - 1, 0, minMaxVolumeSize[1] - 1, 0, minMaxVolumeSize[2] - 1);
|
|
image->SetScalarTypeToUnsignedShort();
|
|
image->AllocateScalars();
|
|
|
|
const int nComponents = minMaxVolumeSize[3];
|
|
const int inc = nComponents * 3;
|
|
unsigned short* pSrc = minMaxVolume + component;
|
|
unsigned short* pDst = static_cast<unsigned short*>(image->GetScalarPointer());
|
|
// Do computation in parts to avoid int overfloat
|
|
vtkIdType nVoxels = minMaxVolumeSize[0];
|
|
nVoxels *= minMaxVolumeSize[1] nVoxels *= minMaxVolumeSize[2];
|
|
|
|
for (vtkIdType i = 0; i < nVoxels; ++i, pSrc += inc, ++pDst)
|
|
{
|
|
*pDst = *pSrc;
|
|
}
|
|
|
|
vtkMetaImageWriter* writer = vtkMetaImageWriter::New();
|
|
writer->SetFileName(filename);
|
|
writer->SetInput(image);
|
|
writer->Write();
|
|
|
|
writer->Delete();
|
|
image->Delete();
|
|
}
|
|
|
|
#endif
|