/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkImageIslandRemoval2D.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 "vtkImageIslandRemoval2D.h" #include "vtkImageData.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkObjectFactory.h" #include "vtkStreamingDemandDrivenPipeline.h" vtkCxxRevisionMacro(vtkImageIslandRemoval2D, "$Revision: 1.48 $"); vtkStandardNewMacro(vtkImageIslandRemoval2D); //---------------------------------------------------------------------------- // Constructor: Sets default filter to be identity. vtkImageIslandRemoval2D::vtkImageIslandRemoval2D() { this->AreaThreshold = 0; this->SetAreaThreshold(4); this->SquareNeighborhood = 1; this->SquareNeighborhoodOff(); this->ReplaceValue = 0; this->SetReplaceValue(255); this->IslandValue = 255; this->SetIslandValue(0); } //---------------------------------------------------------------------------- void vtkImageIslandRemoval2D::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os,indent); os << indent << "AreaThreshold: " << this->AreaThreshold; if (this->SquareNeighborhood) { os << indent << "Neighborhood: Square"; } else { os << indent << "Neighborhood: Cross"; } os << indent << "IslandValue: " << this->IslandValue; os << indent << "ReplaceValue: " << this->ReplaceValue; } //---------------------------------------------------------------------------- // The templated execute function handles all the data types. // Codes: 0 => unvisited. 1 => visted don't know. // 2 => visted keep. 3 => visited replace. // Please excuse the length of this function. The easiest way to choose // neighborhoods is to check neighbors one by one directly. Also, I did // not want to break the templated function into pieces. template void vtkImageIslandRemoval2DExecute(vtkImageIslandRemoval2D *self, vtkImageData *inData, T *inPtr, vtkImageData *outData, T *outPtr, int outExt[6]) { int outIdx0, outIdx1, outIdx2; vtkIdType outInc0, outInc1, outInc2; T *outPtr0, *outPtr1, *outPtr2; vtkIdType inInc0, inInc1, inInc2; T *inPtr0, *inPtr1, *inPtr2; vtkImage2DIslandPixel *pixels; // All the pixels visited so far. int numPixels; // The number of pixels visited so far. vtkImage2DIslandPixel *newPixel; // The last pixel in the list. int nextPixelIdx; // The index of the next pixel to grow. vtkImage2DIslandPixel *nextPixel; // The next pixel to grow. int keepValue; int area; int squareNeighborhood; T islandValue; T replaceValue; T *inNeighborPtr, *outNeighborPtr; int idxC, maxC; unsigned long count = 0; unsigned long target; squareNeighborhood = self->GetSquareNeighborhood(); area = self->GetAreaThreshold(); islandValue = (T)(self->GetIslandValue()); replaceValue = (T)(self->GetReplaceValue()); // In case all 8 neighbors get added before we test the number. pixels = new vtkImage2DIslandPixel [area + 8]; outData->GetIncrements(outInc0, outInc1, outInc2); inData->GetIncrements(inInc0, inInc1, inInc2); maxC = outData->GetNumberOfScalarComponents(); // Loop through pixels setting all output to 0 (unvisited). for (idxC = 0; idxC < maxC; idxC++) { outPtr2 = outPtr + idxC; for (outIdx2 = outExt[4]; outIdx2 <= outExt[5]; ++outIdx2) { outPtr1 = outPtr2; for (outIdx1 = outExt[2]; outIdx1 <= outExt[3]; ++outIdx1) { outPtr0 = outPtr1; for (outIdx0 = outExt[0]; outIdx0 <= outExt[1]; ++outIdx0) { *outPtr0 = (T)(0); // Unvisited outPtr0 += outInc0; } outPtr1 += outInc1; } outPtr2 += outInc2; } } // update the progress and possibly abort self->UpdateProgress(0.1); if (self->AbortExecute) { return; } target = (unsigned long)(maxC*(outExt[5]-outExt[4]+1)* (outExt[3]-outExt[2]+1)/50.0); target++; // Loop though all pixels looking for islands for (idxC = 0; idxC < maxC; idxC++) { outPtr2 = outPtr + idxC; inPtr2 = inPtr + idxC; for (outIdx2 = outExt[4]; !self->AbortExecute && outIdx2 <= outExt[5]; ++outIdx2) { if (!(count%target)) { self->UpdateProgress(0.1 + 0.8*count/(50.0*target)); } count++; outPtr1 = outPtr2; inPtr1 = inPtr2; for (outIdx1 = outExt[2]; outIdx1 <= outExt[3]; ++outIdx1) { outPtr0 = outPtr1; inPtr0 = inPtr1; for (outIdx0 = outExt[0]; outIdx0 <= outExt[1]; ++outIdx0) { if (*outPtr0 == 0) { if (*inPtr0 != islandValue) { // not an island, keep and go on *outPtr0 = 2; } else { // Start an island search // Save first pixel. newPixel = pixels; newPixel->inPtr = (void *)(inPtr0); newPixel->outPtr = (void *)(outPtr0); newPixel->idx0 = outIdx0; newPixel->idx1 = outIdx1; numPixels = 1; nextPixelIdx = 0; nextPixel = pixels; *outPtr0 = 1; // visited don't know keepValue = 1; // breadth first search while (keepValue == 1) // don't know { // Check all the neighbors // left if (nextPixel->idx0 > outExt[0]) { inNeighborPtr = (T *)(nextPixel->inPtr) - inInc0; if ( *inNeighborPtr == islandValue) { outNeighborPtr = (T *)(nextPixel->outPtr) - outInc0; if ( *outNeighborPtr == 2) { // This is part of a bigger island. keepValue = 2; } if ( *outNeighborPtr == 0) { // New pixel to add ++newPixel; ++numPixels; newPixel->inPtr = (void *)(inNeighborPtr); newPixel->outPtr = (void *)(outNeighborPtr); newPixel->idx0 = nextPixel->idx0 - 1; newPixel->idx1 = nextPixel->idx1; *outNeighborPtr = 1; // visited don't know } } } // right if (nextPixel->idx0 < outExt[1]) { inNeighborPtr = (T *)(nextPixel->inPtr) + inInc0; if ( *inNeighborPtr == islandValue) { outNeighborPtr = (T *)(nextPixel->outPtr) + outInc0; if ( *outNeighborPtr == 2) { // This is part of a bigger island. keepValue = 2; } if ( *outNeighborPtr == 0) { // New pixel to add ++newPixel; ++numPixels; newPixel->inPtr = (void *)(inNeighborPtr); newPixel->outPtr = (void *)(outNeighborPtr); newPixel->idx0 = nextPixel->idx0 + 1; newPixel->idx1 = nextPixel->idx1; *outNeighborPtr = 1; // visited don't know } } } // up if (nextPixel->idx1 > outExt[2]) { inNeighborPtr = (T *)(nextPixel->inPtr) - inInc1; if ( *inNeighborPtr == islandValue) { outNeighborPtr = (T *)(nextPixel->outPtr) - outInc1; if ( *outNeighborPtr == 2) { // This is part of a bigger island. keepValue = 2; } if ( *outNeighborPtr == 0) { // New pixel to add ++newPixel; ++numPixels; newPixel->inPtr = (void *)(inNeighborPtr); newPixel->outPtr = (void *)(outNeighborPtr); newPixel->idx0 = nextPixel->idx0; newPixel->idx1 = nextPixel->idx1 - 1; *outNeighborPtr = 1; // visited don't know } } } // down if (nextPixel->idx1 < outExt[3]) { inNeighborPtr = (T *)(nextPixel->inPtr) + inInc1; if ( *inNeighborPtr == islandValue) { outNeighborPtr = (T *)(nextPixel->outPtr) + outInc1; if ( *outNeighborPtr == 2) { // This is part of a bigger island. keepValue = 2; } if ( *outNeighborPtr == 0) { // New pixel to add ++newPixel; ++numPixels; newPixel->inPtr = (void *)(inNeighborPtr); newPixel->outPtr = (void *)(outNeighborPtr); newPixel->idx0 = nextPixel->idx0; newPixel->idx1 = nextPixel->idx1 + 1; *outNeighborPtr = 1; // visited don't know } } } // Corners if (squareNeighborhood) { // upper left if (nextPixel->idx0 > outExt[0] && nextPixel->idx1 > outExt[2]) { inNeighborPtr = (T *)(nextPixel->inPtr) - inInc0 - inInc1; if ( *inNeighborPtr == islandValue) { outNeighborPtr = (T *)(nextPixel->outPtr) - outInc0 -outInc1; if ( *outNeighborPtr == 2) { // This is part of a bigger island. keepValue = 2; } if ( *outNeighborPtr == 0) { // New pixel to add ++newPixel; ++numPixels; newPixel->inPtr = (void *)(inNeighborPtr); newPixel->outPtr = (void *)(outNeighborPtr); newPixel->idx0 = nextPixel->idx0 - 1; newPixel->idx1 = nextPixel->idx1 - 1; *outNeighborPtr = 1; // visited don't know } } } // upper right if (nextPixel->idx0 < outExt[1] && nextPixel->idx1 > outExt[2]) { inNeighborPtr = (T *)(nextPixel->inPtr) + inInc0 - inInc1; if ( *inNeighborPtr == islandValue) { outNeighborPtr = (T *)(nextPixel->outPtr) + outInc0 -outInc1; if ( *outNeighborPtr == 2) { // This is part of a bigger island. keepValue = 2; } if ( *outNeighborPtr == 0) { // New pixel to add ++newPixel; ++numPixels; newPixel->inPtr = (void *)(inNeighborPtr); newPixel->outPtr = (void *)(outNeighborPtr); newPixel->idx0 = nextPixel->idx0 + 1; newPixel->idx1 = nextPixel->idx1 - 1; *outNeighborPtr = 1; // visited don't know } } } // lower left if (nextPixel->idx0 > outExt[0] && nextPixel->idx1 < outExt[3]) { inNeighborPtr = (T *)(nextPixel->inPtr) - inInc0 + inInc1; if ( *inNeighborPtr == islandValue) { outNeighborPtr = (T *)(nextPixel->outPtr) - outInc0 +outInc1; if ( *outNeighborPtr == 2) { // This is part of a bigger island. keepValue = 2; } if ( *outNeighborPtr == 0) { // New pixel to add ++newPixel; ++numPixels; newPixel->inPtr = (void *)(inNeighborPtr); newPixel->outPtr = (void *)(outNeighborPtr); newPixel->idx0 = nextPixel->idx0 - 1; newPixel->idx1 = nextPixel->idx1 + 1; *outNeighborPtr = 1; // visited don't know } } } // lower right if (nextPixel->idx0 < outExt[1] && nextPixel->idx1 < outExt[3]) { inNeighborPtr = (T *)(nextPixel->inPtr) + inInc0 + inInc1; if ( *inNeighborPtr == islandValue) { outNeighborPtr = (T *)(nextPixel->outPtr) + outInc0 +outInc1; if ( *outNeighborPtr == 2) { // This is part of a bigger island. keepValue = 2; } if ( *outNeighborPtr == 0) { // New pixel to add ++newPixel; ++numPixels; newPixel->inPtr = (void *)(inNeighborPtr); newPixel->outPtr = (void *)(outNeighborPtr); newPixel->idx0 = nextPixel->idx0 + 1; newPixel->idx1 = nextPixel->idx1 + 1; *outNeighborPtr = 1; // visited don't know } } } } // Move to the next pixel to grow. ++nextPixel; ++nextPixelIdx; // Have we visted enogh pixels to determine this is a keeper? if (keepValue == 1 && numPixels >= area) { keepValue = 2; } // Have we run out of pixels to grow? if (keepValue == 1 && nextPixelIdx >= numPixels) { // The island is too small. Set island values too replace. keepValue = 3; } } // Change "don't knows" to keep value nextPixel = pixels; for (nextPixelIdx = 0; nextPixelIdx < numPixels; ++nextPixelIdx) { *((T *)(nextPixel->outPtr)) = keepValue; ++nextPixel; } } } outPtr0 += outInc0; inPtr0 += inInc0; } outPtr1 += outInc1; inPtr1 += inInc1; } outPtr2 += outInc2; inPtr2 += inInc2; } } delete [] pixels; // update the progress and possibly abort self->UpdateProgress(0.9); if (self->AbortExecute) { return; } // Loop though all pixels actually copying and replacing. for (idxC = 0; idxC < maxC; idxC++) { outPtr2 = outPtr + idxC; inPtr2 = inPtr + idxC; for (outIdx2 = outExt[4]; outIdx2 <= outExt[5]; ++outIdx2) { outPtr1 = outPtr2; inPtr1 = inPtr2; for (outIdx1 = outExt[2]; outIdx1 <= outExt[3]; ++outIdx1) { outPtr0 = outPtr1; inPtr0 = inPtr1; for (outIdx0 = outExt[0]; outIdx0 <= outExt[1]; ++outIdx0) { if (*outPtr0 == 3) { *outPtr0 = replaceValue; } else { *outPtr0 = *inPtr0; } inPtr0 += inInc0; outPtr0 += outInc0; } inPtr1 += inInc1; outPtr1 += outInc1; } inPtr2 += inInc2; 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. Assumes that in and out have the same lower extent. int vtkImageIslandRemoval2D::RequestData( vtkInformation *vtkNotUsed(request), vtkInformationVector **inputVector, vtkInformationVector *outputVector) { int outExt[6]; // get the data object vtkInformation *inInfo = inputVector[0]->GetInformationObject(0); vtkImageData *inData = vtkImageData::SafeDownCast( inInfo->Get(vtkDataObject::DATA_OBJECT())); vtkInformation *outInfo = outputVector->GetInformationObject(0); vtkImageData *outData = vtkImageData::SafeDownCast( outInfo->Get(vtkDataObject::DATA_OBJECT())); int wholeExtent[6]; int extent[6]; // We need to allocate our own scalars. outInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), wholeExtent); outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), extent); extent[0] = wholeExtent[0]; extent[1] = wholeExtent[1]; extent[2] = wholeExtent[2]; extent[3] = wholeExtent[3]; outData->SetExtent(extent); outData->AllocateScalars(); // this filter expects that input is the same type as output. if (inData->GetScalarType() != outData->GetScalarType()) { vtkErrorMacro(<< "Execute: input ScalarType, " << vtkImageScalarTypeNameMacro(inData->GetScalarType()) << ", must match out ScalarType " << vtkImageScalarTypeNameMacro(outData->GetScalarType())); return 1; } outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), outExt); void *inPtr = inData->GetScalarPointerForExtent(outExt); void *outPtr = outData->GetScalarPointerForExtent(outExt); switch (inData->GetScalarType()) { vtkTemplateMacro( vtkImageIslandRemoval2DExecute( this, inData, (VTK_TT *)(inPtr), outData, (VTK_TT *)(outPtr), outExt)); default: vtkErrorMacro(<< "Execute: Unknown ScalarType"); return 1; } return 1; }