/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkImageShrink3D.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 "vtkImageShrink3D.h" #include "vtkImageData.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkObjectFactory.h" #include "vtkStreamingDemandDrivenPipeline.h" #include vtkCxxRevisionMacro(vtkImageShrink3D, "$Revision: 1.69 $"); vtkStandardNewMacro(vtkImageShrink3D); //---------------------------------------------------------------------------- // Constructor: Sets default filter to be identity. vtkImageShrink3D::vtkImageShrink3D() { this->ShrinkFactors[0] = this->ShrinkFactors[1] = this->ShrinkFactors[2] = 1; this->Shift[0] = this->Shift[1] = this->Shift[2] = 0; this->Mean = 1; this->Median = 0; this->Maximum = 0; this->Minimum = 0; } void vtkImageShrink3D::SetMean (int value) { if (value != this->Mean) { this->Mean = value; if (value == 1) { this->Minimum = 0; this->Maximum = 0; this->Median = 0; } this->Modified(); } } void vtkImageShrink3D::SetMinimum (int value) { if (value != this->Minimum) { this->Minimum = value; if (value == 1) { this->Mean = 0; this->Maximum = 0; this->Median = 0; } this->Modified(); } } void vtkImageShrink3D::SetMaximum (int value) { if (value != this->Maximum) { this->Maximum = value; if (value == 1) { this->Minimum = 0; this->Mean = 0; this->Median = 0; } this->Modified(); } } void vtkImageShrink3D::SetMedian (int value) { if (value != this->Median) { this->Median = value; if (value == 1) { this->Minimum = 0; this->Maximum = 0; this->Mean = 0; } this->Modified(); } } void vtkImageShrink3D::SetAveraging (int value) { this->SetMean(value); } //---------------------------------------------------------------------------- void vtkImageShrink3D::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); os << indent << "ShrinkFactors: (" << this->ShrinkFactors[0] << ", " << this->ShrinkFactors[1] << ", " << this->ShrinkFactors[2] << ")\n"; os << indent << "Shift: (" << this->Shift[0] << ", " << this->Shift[1] << ", " << this->Shift[2] << ")\n"; os << indent << "Averaging: " << (this->Mean ? "On\n" : "Off\n"); os << indent << "Mean: " << (this->Mean ? "On\n" : "Off\n"); os << indent << "Minimum: " << (this->Minimum ? "On\n" : "Off\n"); os << indent << "Maximum: " << (this->Maximum ? "On\n" : "Off\n"); os << indent << "Median: " << (this->Median ? "On\n" : "Off\n"); } void vtkImageShrink3D::InternalRequestUpdateExtent(int *inExt, int *outExt) { int idx; for (idx = 0; idx < 3; ++idx) { // For Min. inExt[idx*2] = outExt[idx*2] * this->ShrinkFactors[idx] + this->Shift[idx]; // For Max. inExt[idx*2+1] = outExt[idx*2+1] * this->ShrinkFactors[idx] + this->Shift[idx]; // If we are not sub sampling, we need a little more if (this->Mean || this->Minimum || this->Maximum || this->Median) { inExt[idx*2+1] += this->ShrinkFactors[idx] - 1; } } } //---------------------------------------------------------------------------- // This method computes the Region of input necessary to generate outRegion. int vtkImageShrink3D::RequestUpdateExtent ( vtkInformation * vtkNotUsed(request), vtkInformationVector **inputVector, vtkInformationVector *outputVector) { // get the info objects vtkInformation* outInfo = outputVector->GetInformationObject(0); vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); int outExt[6], inExt[6]; outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), outExt); this->InternalRequestUpdateExtent(inExt, outExt); inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), inExt, 6); return 1; } //---------------------------------------------------------------------------- // Computes any global image information associated with regions. // Any problems with roundoff or negative numbers ??? int vtkImageShrink3D::RequestInformation ( vtkInformation * vtkNotUsed(request), vtkInformationVector **inputVector, vtkInformationVector *outputVector) { // get the info objects vtkInformation* outInfo = outputVector->GetInformationObject(0); vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); int idx; int wholeExtent[6]; double spacing[3]; inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(),wholeExtent); inInfo->Get(vtkDataObject::SPACING(), spacing); for (idx = 0; idx < 3; ++idx) { // Avoid dividing by 0. if (this->ShrinkFactors[idx] == 0) { this->ShrinkFactors[idx] = 1; } // Scale the output extent wholeExtent[2*idx] = (int)(ceil((double)(wholeExtent[2*idx] - this->Shift[idx]) / (double)(this->ShrinkFactors[idx]))); wholeExtent[2*idx+1] = (int)(floor( (double)(wholeExtent[2*idx+1]-this->Shift[idx]-this->ShrinkFactors[idx]+1) / (double)(this->ShrinkFactors[idx]))); // make sure WholeExtent is valid when the ShrinkFactors are set on an // axis with no Extent beforehand if (wholeExtent[2*idx+1]ShrinkFactors[idx]); } outInfo->Set(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(),wholeExtent,6); outInfo->Set(vtkDataObject::SPACING(),spacing,3); return 1; } template #ifdef _WIN32_WCE int __cdecl vtkiscompare(const T *y1,const T *y2) #else int vtkiscompare(const T *y1,const T *y2) #endif { if ( *y1 < *y2) { return -1; } if ( *y1 == *y2) { return 0; } return 1; } extern "C" { typedef int (*vtkCompareFunction)(const void*, const void*); } //---------------------------------------------------------------------------- // The templated execute function handles all the data types. template void vtkImageShrink3DExecute(vtkImageShrink3D *self, vtkImageData *inData, T *inPtr, vtkImageData *outData, T *outPtr, int outExt[6], int id, vtkInformation *inInfo) { int outIdx0, outIdx1, outIdx2, inIdx0, inIdx1, inIdx2; vtkIdType inInc0, inInc1, inInc2; T *inPtr0, *inPtr1, *inPtr2; vtkIdType outInc0, outInc1, outInc2; vtkIdType tmpInc0, tmpInc1, tmpInc2; T *tmpPtr0, *tmpPtr1, *tmpPtr2; int factor0, factor1, factor2; double sum, norm; unsigned long count = 0; unsigned long target; int idxC, maxC, maxX; T *outPtr2; // black magic to force the correct version of the comparison function // to be instantiated AND used. #ifdef _WIN32_WCE int (__cdecl *compareF1)(const T*, const T*) = vtkiscompare; int (__cdecl *compareFn)(const void*, const void*) = (int (__cdecl *)(const void*, const void*)) compareF1; #else int (*compareF1)(const T*, const T*) = vtkiscompare; // int (*compareFn)(const void*, const void*) // = (int (*)(const void*, const void*)) compareF1; vtkCompareFunction compareFn = reinterpret_cast(compareF1); #endif self->GetShrinkFactors(factor0, factor1, factor2); // make sure we don't have a 3D shrinkfactor for a 2D image if (factor2>1 && inData && inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT())[5]==0) { factor2=1; } // Get information to march through data inData->GetIncrements(inInc0, inInc1, inInc2); tmpInc0 = inInc0 * factor0; tmpInc1 = inInc1 * factor1; tmpInc2 = inInc2 * factor2; outData->GetContinuousIncrements(outExt,outInc0, outInc1, outInc2); maxX = outExt[1] - outExt[0]; maxC = inData->GetNumberOfScalarComponents(); target = (unsigned long)(maxC*(outExt[5] - outExt[4] + 1)* (outExt[3] - outExt[2] + 1)/50.0); target++; if (self->GetMean()) { norm = 1.0 / (double)(factor0 * factor1 * factor2); // Loop through output pixels for (idxC = 0; idxC < maxC; idxC++) { tmpPtr2 = inPtr + idxC; outPtr2 = outPtr + idxC; for (outIdx2 = outExt[4]; outIdx2 <= outExt[5]; ++outIdx2) { tmpPtr1 = tmpPtr2; for (outIdx1 = outExt[2]; !self->AbortExecute && outIdx1 <= outExt[3]; ++outIdx1) { if (!id) { if (!(count%target)) { self->UpdateProgress(count/(50.0*target)); } count++; } tmpPtr0 = tmpPtr1; for (outIdx0 = 0; outIdx0 <= maxX; ++outIdx0) { sum = 0.0; // Loop through neighborhood pixels inPtr2 = tmpPtr0; for (inIdx2 = 0; inIdx2 < factor2; ++inIdx2) { inPtr1 = inPtr2; for (inIdx1 = 0; inIdx1 < factor1; ++inIdx1) { inPtr0 = inPtr1; for (inIdx0 = 0; inIdx0 < factor0; ++inIdx0) { sum += (double)(*inPtr0); inPtr0 += inInc0; } inPtr1 += inInc1; } inPtr2 += inInc2; } *outPtr2 = (T)(sum * norm); tmpPtr0 += tmpInc0; outPtr2 += maxC; } tmpPtr1 += tmpInc1; outPtr2 += outInc1; } tmpPtr2 += tmpInc2; outPtr2 += outInc2; } } } else if (self->GetMinimum()) { T minValue; // Loop through output pixels for (idxC = 0; idxC < maxC; idxC++) { tmpPtr2 = inPtr + idxC; outPtr2 = outPtr + idxC; for (outIdx2 = outExt[4]; outIdx2 <= outExt[5]; ++outIdx2) { tmpPtr1 = tmpPtr2; for (outIdx1 = outExt[2]; !self->AbortExecute && outIdx1 <= outExt[3]; ++outIdx1) { if (!id) { if (!(count%target)) { self->UpdateProgress(count/(50.0*target)); } count++; } tmpPtr0 = tmpPtr1; for (outIdx0 = 0; outIdx0 <= maxX; ++outIdx0) { minValue = (T) self->GetOutput()->GetScalarTypeMax(); // Loop through neighborhood pixels inPtr2 = tmpPtr0; for (inIdx2 = 0; inIdx2 < factor2; ++inIdx2) { inPtr1 = inPtr2; for (inIdx1 = 0; inIdx1 < factor1; ++inIdx1) { inPtr0 = inPtr1; for (inIdx0 = 0; inIdx0 < factor0; ++inIdx0) { if (*inPtr0 < minValue) { minValue = *inPtr0; } inPtr0 += inInc0; } inPtr1 += inInc1; } inPtr2 += inInc2; } *outPtr2 = minValue; tmpPtr0 += tmpInc0; outPtr2 += maxC; } tmpPtr1 += tmpInc1; outPtr2 += outInc1; } tmpPtr2 += tmpInc2; outPtr2 += outInc2; } } } else if (self->GetMaximum()) { T maxValue; // Loop through output pixels for (idxC = 0; idxC < maxC; idxC++) { tmpPtr2 = inPtr + idxC; outPtr2 = outPtr + idxC; for (outIdx2 = outExt[4]; outIdx2 <= outExt[5]; ++outIdx2) { tmpPtr1 = tmpPtr2; for (outIdx1 = outExt[2]; !self->AbortExecute && outIdx1 <= outExt[3]; ++outIdx1) { if (!id) { if (!(count%target)) { self->UpdateProgress(count/(50.0*target)); } count++; } tmpPtr0 = tmpPtr1; for (outIdx0 = 0; outIdx0 <= maxX; ++outIdx0) { maxValue = (T) self->GetOutput()->GetScalarTypeMin(); // Loop through neighborhood pixels inPtr2 = tmpPtr0; for (inIdx2 = 0; inIdx2 < factor2; ++inIdx2) { inPtr1 = inPtr2; for (inIdx1 = 0; inIdx1 < factor1; ++inIdx1) { inPtr0 = inPtr1; for (inIdx0 = 0; inIdx0 < factor0; ++inIdx0) { if (*inPtr0 > maxValue) { maxValue = *inPtr0; } inPtr0 += inInc0; } inPtr1 += inInc1; } inPtr2 += inInc2; } *outPtr2 = maxValue; tmpPtr0 += tmpInc0; outPtr2 += maxC; } tmpPtr1 += tmpInc1; outPtr2 += outInc1; } tmpPtr2 += tmpInc2; outPtr2 += outInc2; } } } else if (self->GetMedian()) { T* kernel = new T [factor0 * factor1 * factor2]; int index; // Loop through output pixels for (idxC = 0; idxC < maxC; idxC++) { tmpPtr2 = inPtr + idxC; outPtr2 = outPtr + idxC; for (outIdx2 = outExt[4]; outIdx2 <= outExt[5]; ++outIdx2) { tmpPtr1 = tmpPtr2; for (outIdx1 = outExt[2]; !self->AbortExecute && outIdx1 <= outExt[3]; ++outIdx1) { if (!id) { if (!(count%target)) { self->UpdateProgress(count/(50.0*target)); } count++; } tmpPtr0 = tmpPtr1; for (outIdx0 = 0; outIdx0 <= maxX; ++outIdx0) { // Loop through neighborhood pixels inPtr2 = tmpPtr0; index = 0; for (inIdx2 = 0; inIdx2 < factor2; ++inIdx2) { inPtr1 = inPtr2; for (inIdx1 = 0; inIdx1 < factor1; ++inIdx1) { inPtr0 = inPtr1; for (inIdx0 = 0; inIdx0 < factor0; ++inIdx0) { kernel[index++] = *inPtr0; inPtr0 += inInc0; } inPtr1 += inInc1; } inPtr2 += inInc2; } qsort(kernel,index,sizeof(T),compareFn); *outPtr2 = *(kernel + index/2); tmpPtr0 += tmpInc0; outPtr2 += maxC; } tmpPtr1 += tmpInc1; outPtr2 += outInc1; } tmpPtr2 += tmpInc2; outPtr2 += outInc2; } } delete [] kernel; } else // Just SubSample { // Loop through output pixels for (idxC = 0; idxC < maxC; idxC++) { tmpPtr2 = inPtr + idxC; outPtr2 = outPtr + idxC; for (outIdx2 = outExt[4]; outIdx2 <= outExt[5]; ++outIdx2) { tmpPtr1 = tmpPtr2; for (outIdx1 = outExt[2]; !self->AbortExecute && outIdx1 <= outExt[3]; ++outIdx1) { if (!id) { if (!(count%target)) { self->UpdateProgress(count/(50.0*target)); } count++; } tmpPtr0 = tmpPtr1; for (outIdx0 = 0; outIdx0 <= maxX; ++outIdx0) { *outPtr2 = *tmpPtr0; tmpPtr0 += tmpInc0; outPtr2 += maxC; } tmpPtr1 += tmpInc1; outPtr2 += outInc1; } tmpPtr2 += tmpInc2; outPtr2 += outInc2; } } } } //---------------------------------------------------------------------------- // This method uses the input data to fill the output data. // It can handle any type data, but the two datas must have the same // data type. void vtkImageShrink3D::ThreadedRequestData( vtkInformation * vtkNotUsed( request ), vtkInformationVector **inputVector, vtkInformationVector * vtkNotUsed( outputVector ), vtkImageData ***inData, vtkImageData **outData, int outExt[6], int id) { int inExt[6]; void *outPtr = outData[0]->GetScalarPointerForExtent(outExt); vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); this->InternalRequestUpdateExtent(inExt, outExt); void *inPtr = inData[0][0]->GetScalarPointerForExtent(inExt); if (!inPtr) { return; } // 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; } switch (inData[0][0]->GetScalarType()) { vtkTemplateMacro( vtkImageShrink3DExecute( this, inData[0][0], (VTK_TT *)(inPtr), outData[0], (VTK_TT *)(outPtr), outExt, id, inInfo) ); default: vtkErrorMacro(<< "Execute: Unknown ScalarType"); return; } }