Commit ab9fc894 authored by David Gobbi's avatar David Gobbi Committed by Kitware Robot
Browse files

Merge topic 'image-connectivity-filter'

60d9d023

 Add vtkImageConnectivityFilter
Acked-by: Kitware Robot's avatarKitware Robot <kwrobot@kitware.com>
Reviewed-by: Cory Quammen's avatarCory Quammen <cory.quammen@kitware.com>
Merge-request: !1946
parents 3c3643ea 60d9d023
set(Module_SRCS
vtkImageConnector.cxx
vtkImageConnectivityFilter.cxx
vtkImageContinuousDilate3D.cxx
vtkImageContinuousErode3D.cxx
vtkImageDilateErode3D.cxx
......
vtk_add_test_cxx(${vtk-module}CxxTests tests
TestImageThresholdConnectivity.cxx
TestImageConnectivityFilter.cxx
)
vtk_test_cxx_executable(${vtk-module}CxxTests tests
......
/*=========================================================================
Program: Visualization Toolkit
Module: ImageConnectivityFilter.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.
=========================================================================*/
// Test the ImageConnectivityFilter class
//
// The command line arguments are:
// -I => run in interactive mode
#include "vtkSmartPointer.h"
#include "vtkCamera.h"
#include "vtkImageConnectivityFilter.h"
#include "vtkImageData.h"
#include "vtkImageProperty.h"
#include "vtkImageReader2.h"
#include "vtkImageSlice.h"
#include "vtkImageSliceMapper.h"
#include "vtkInteractorStyleImage.h"
#include "vtkIdTypeArray.h"
#include "vtkIntArray.h"
#include "vtkPoints.h"
#include "vtkPointData.h"
#include "vtkPolyData.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkVersion.h"
#include "vtkTestUtilities.h"
#include <string>
int TestImageConnectivityFilter(int argc, char *argv[])
{
vtkSmartPointer<vtkRenderWindowInteractor> iren =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
vtkSmartPointer<vtkInteractorStyleImage> style =
vtkSmartPointer<vtkInteractorStyleImage>::New();
style->SetInteractionModeToImageSlicing();
vtkSmartPointer<vtkRenderWindow> renWin =
vtkSmartPointer<vtkRenderWindow>::New();
iren->SetRenderWindow(renWin);
iren->SetInteractorStyle(style);
// Use a 3D image for the test
std::string fname =
vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/headsq/quarter");
vtkSmartPointer<vtkImageReader2> reader =
vtkSmartPointer<vtkImageReader2>::New();
reader->SetDataByteOrderToLittleEndian();
reader->SetDataExtent(0, 63, 0, 63, 2, 4);
reader->SetDataSpacing(3.2, 3.2, 1.5);
reader->SetFilePrefix(fname.c_str());
// Create two seed points
vtkSmartPointer<vtkPoints> seedPoints =
vtkSmartPointer<vtkPoints>::New();
seedPoints->InsertNextPoint(25.6, 100.8, 2.25);
seedPoints->InsertNextPoint(100.8, 100.8, 2.25);
vtkSmartPointer<vtkUnsignedCharArray> seedScalars =
vtkSmartPointer<vtkUnsignedCharArray>::New();
seedScalars->InsertNextValue(2);
seedScalars->InsertNextValue(5);
vtkSmartPointer<vtkPolyData> seedData =
vtkSmartPointer<vtkPolyData>::New();
seedData->SetPoints(seedPoints);
seedData->GetPointData()->SetScalars(seedScalars);
// Generate a grid of renderers for the various tests
for (int i = 0; i < 9; i++)
{
int j = 2 - i / 3;
int k = i % 3;
vtkSmartPointer<vtkRenderer> renderer =
vtkSmartPointer<vtkRenderer>::New();
vtkCamera *camera = renderer->GetActiveCamera();
renderer->SetBackground(0.0, 0.0, 0.0);
renderer->SetViewport(k/3.0, j/3.0, (k + 1)/3.0, (j + 1)/3.0);
renWin->AddRenderer(renderer);
vtkSmartPointer<vtkImageConnectivityFilter> connectivity =
vtkSmartPointer<vtkImageConnectivityFilter>::New();
connectivity->SetInputConnection(reader->GetOutputPort());
if (i == 0)
{
connectivity->GenerateRegionExtentsOn();
connectivity->SetScalarRange(800, 1200);
// No seeds
// Default extraction mode
// Default label mode
}
else if (i == 1)
{
connectivity->SetScalarRange(800, 1200);
// No seeds
connectivity->SetExtractionModeToLargestRegion();
// Default label mode
}
else if (i == 2)
{
connectivity->SetScalarRange(800, 1200);
// No seeds
connectivity->SetSizeRange(10, 99);
// Default label mode
}
else if (i == 3)
{
connectivity->SetScalarRange(800, 1200);
connectivity->SetSeedData(seedData);
// Default extraction mode
// Default label mode (use seed scalars)
}
else if (i == 4)
{
connectivity->SetScalarRange(800, 1200);
connectivity->SetSeedData(seedData);
connectivity->SetExtractionModeToAllRegions();
connectivity->SetLabelModeToSizeRank();
}
else if (i == 5)
{
// Seeds with no scalars
connectivity->SetScalarRange(800, 1200);
seedData->GetPointData()->SetScalars(NULL);
connectivity->SetSeedData(seedData);
}
else if (i == 6)
{
connectivity->SetScalarRange(1200, 4095);
}
else if (i == 7)
{
connectivity->SetScalarRange(0, 800);
}
else if (i == 8)
{
// use default scalar range
}
if (i == 0)
{
// Test OutputExtent != InputExtent
int extent[6] = { 0, 63, 0, 63, 3, 3 };
connectivity->UpdateExtent(extent);
}
else
{
// Test updating whole extent
connectivity->Update();
}
// Test getting info about the output regions
vtkIdTypeArray *sizeArray = connectivity->GetExtractedRegionSizes();
vtkIdTypeArray *idArray = connectivity->GetExtractedRegionSeedIds();
vtkIdTypeArray *labelArray = connectivity->GetExtractedRegionLabels();
vtkIntArray *extentArray = connectivity->GetExtractedRegionExtents();
vtkIdType rn = connectivity->GetNumberOfExtractedRegions();
std::cout << "\nTest Case: " << i << std::endl;
std::cout << "number of regions: " << rn << std::endl;
for (vtkIdType r = 0; r < rn; r++)
{
std::cout << "region: " << r << ","
<< " seed: " << idArray->GetValue(r) << ","
<< " label: " << labelArray->GetValue(r) << ","
<< " size: " << sizeArray->GetValue(r) << ","
<< " extent: [";
if (connectivity->GetGenerateRegionExtents())
{
std::cout << extentArray->GetValue(6*r) << ","
<< extentArray->GetValue(6*r+1) << ","
<< extentArray->GetValue(6*r+2) << ","
<< extentArray->GetValue(6*r+3) << ","
<< extentArray->GetValue(6*r+4) << ","
<< extentArray->GetValue(6*r+5);
}
std::cout << "]" << std::endl;
}
vtkSmartPointer<vtkImageSliceMapper> imageMapper =
vtkSmartPointer<vtkImageSliceMapper>::New();
imageMapper->SetInputConnection(connectivity->GetOutputPort());
imageMapper->BorderOn();
imageMapper->SliceFacesCameraOn();
imageMapper->SliceAtFocalPointOn();
double point[3] = { 100.8, 100.8, 5.25 };
camera->SetFocalPoint(point);
point[2] += 500.0;
camera->SetPosition(point);
camera->SetViewUp(0.0, 1.0, 0.0);
camera->ParallelProjectionOn();
camera->SetParallelScale(3.2*32);
vtkSmartPointer<vtkImageSlice> image =
vtkSmartPointer<vtkImageSlice>::New();
image->SetMapper(imageMapper);
image->GetProperty()->SetColorWindow(6);
image->GetProperty()->SetColorLevel(3);
renderer->AddViewProp(image);
}
renWin->SetSize(192, 256);
iren->Initialize();
renWin->Render();
iren->Start();
return EXIT_SUCCESS;
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkImageConnectivityFilter.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.
=========================================================================*/
/*=========================================================================
Copyright (c) 2014 David Gobbi
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of David Gobbi nor the names of any contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
=========================================================================*/
#include "vtkImageConnectivityFilter.h"
#include "vtkMath.h"
#include "vtkImageData.h"
#include "vtkDataSet.h"
#include "vtkPointData.h"
#include "vtkImageStencilData.h"
#include "vtkImageStencilIterator.h"
#include "vtkImageIterator.h"
#include "vtkObjectFactory.h"
#include "vtkPoints.h"
#include "vtkIdTypeArray.h"
#include "vtkIntArray.h"
#include "vtkImageStencilData.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkTemplateAliasMacro.h"
#include "vtkTypeTraits.h"
#include "vtkSmartPointer.h"
#include "vtkVersion.h"
#include <vector>
#include <stack>
#include <algorithm>
vtkStandardNewMacro(vtkImageConnectivityFilter);
//----------------------------------------------------------------------------
// Constructor sets default values
vtkImageConnectivityFilter::vtkImageConnectivityFilter()
{
this->LabelMode = SeedScalar;
this->ExtractionMode = SeededRegions;
this->ScalarRange[0] = 0.5;
this->ScalarRange[1] = VTK_DOUBLE_MAX;
this->SizeRange[0] = 1;
this->SizeRange[1] = VTK_ID_MAX;
this->LabelConstantValue = 255;
this->ActiveComponent = 0;
this->LabelScalarType = VTK_UNSIGNED_CHAR;
this->GenerateRegionExtents = 0;
this->ExtractedRegionLabels = vtkIdTypeArray::New();
this->ExtractedRegionSizes = vtkIdTypeArray::New();
this->ExtractedRegionSeedIds = vtkIdTypeArray::New();
this->ExtractedRegionExtents = vtkIntArray::New();
this->ExtractedRegionExtents->SetNumberOfComponents(6);
this->SetNumberOfInputPorts(3);
}
//----------------------------------------------------------------------------
vtkImageConnectivityFilter::~vtkImageConnectivityFilter()
{
if (this->ExtractedRegionSizes)
{
this->ExtractedRegionSizes->Delete();
}
if (this->ExtractedRegionLabels)
{
this->ExtractedRegionLabels->Delete();
}
if (this->ExtractedRegionSeedIds)
{
this->ExtractedRegionSeedIds->Delete();
}
if (this->ExtractedRegionExtents)
{
this->ExtractedRegionExtents->Delete();
}
}
//----------------------------------------------------------------------------
int vtkImageConnectivityFilter::FillInputPortInformation(
int port, vtkInformation* info)
{
if (port == 2)
{
info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet");
info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1);
}
else if (port == 1)
{
info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageStencilData");
info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1);
}
else
{
info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageData");
}
return 1;
}
//----------------------------------------------------------------------------
void vtkImageConnectivityFilter::SetStencilConnection(
vtkAlgorithmOutput *stencil)
{
this->SetInputConnection(1, stencil);
}
//----------------------------------------------------------------------------
vtkAlgorithmOutput *vtkImageConnectivityFilter::GetStencilConnection()
{
return this->GetInputConnection(1, 0);
}
//----------------------------------------------------------------------------
void vtkImageConnectivityFilter::SetStencilData(vtkImageStencilData *stencil)
{
this->SetInputData(1, stencil);
}
//----------------------------------------------------------------------------
void vtkImageConnectivityFilter::SetSeedConnection(
vtkAlgorithmOutput *seeds)
{
this->SetInputConnection(2, seeds);
}
//----------------------------------------------------------------------------
vtkAlgorithmOutput *vtkImageConnectivityFilter::GetSeedConnection()
{
return this->GetInputConnection(2, 0);
}
//----------------------------------------------------------------------------
void vtkImageConnectivityFilter::SetSeedData(vtkDataSet *seeds)
{
this->SetInputData(2, seeds);
}
//----------------------------------------------------------------------------
const char *vtkImageConnectivityFilter::GetLabelScalarTypeAsString()
{
const char *result = "Unknown";
switch (this->LabelScalarType)
{
case VTK_UNSIGNED_CHAR:
result = "UnsignedChar";
break;
case VTK_SHORT:
result = "Short";
break;
case VTK_UNSIGNED_SHORT:
result = "UnsignedShort";
break;
case VTK_INT:
result = "Int";
break;
}
return result;
}
//----------------------------------------------------------------------------
const char *vtkImageConnectivityFilter::GetLabelModeAsString()
{
const char *result = "Unknown";
switch (this->LabelMode)
{
case SeedScalar:
result = "SeedScalar";
break;
case ConstantValue:
result = "ConstantValue";
break;
case SizeRank:
result = "SizeRank";
break;
}
return result;
}
//----------------------------------------------------------------------------
const char *vtkImageConnectivityFilter::GetExtractionModeAsString()
{
const char *result = "Unknown";
switch (this->ExtractionMode)
{
case SeededRegions:
result = "SeededRegions";
break;
case AllRegions:
result = "AllRegions";
break;
case LargestRegion:
result = "LargestRegion";
break;
}
return result;
}
//----------------------------------------------------------------------------
vtkIdType vtkImageConnectivityFilter::GetNumberOfExtractedRegions()
{
return this->ExtractedRegionLabels->GetNumberOfTuples();
}
//----------------------------------------------------------------------------
namespace {
// Methods for the connectivity algorithm
class vtkICF
{
public:
// Simple struct that holds information about a region.
struct Region;
// A class that is a vector of regions.
class RegionVector;
protected:
// Simple class that holds a seed location and a scalar value.
class Seed;
// A functor to assist in comparing region sizes.
struct CompareSize;
// Remove all but the largest region from the output image.
template<class OT>
static void PruneAllButLargest(
vtkImageData *outData, OT *outPtr, vtkImageStencilData *stencil,
int extent[6], const OT& value, vtkICF::RegionVector& regionInfo);
// Remove the smallest region from the output image.
// This is called when there are no labels left, i.e. when the label
// value reaches the maximum allowed by the output data type.
template<class OT>
static void PruneSmallestRegion(
vtkImageData *outData, OT *outPtr, vtkImageStencilData *stencil,
int extent[6], vtkICF::RegionVector& regionInfo);
// Remove all islands that aren't in the given range of sizes
template<class OT>
static void PruneBySize(
vtkImageData *outData, OT *outPtr, vtkImageStencilData *stencil,
int extent[6], vtkIdType sizeRange[2], vtkICF::RegionVector& regionInfo);
// This is the function that grows a region from a seed.
template<class OT>
static vtkIdType Fill(
OT *outPtr, vtkIdType outInc[3], int outLimits[6],
unsigned char *maskPtr, int maxIdx[3], int fillExtent[6],
std::stack<vtkICF::Seed> &seedStack);
// Add a region to the list of regions.
template<class OT>
static void AddRegion(
vtkImageData *outData, OT *outPtr, vtkImageStencilData *stencil,
int extent[6], vtkIdType sizeRange[2], vtkICF::RegionVector& regionInfo,
vtkIdType voxelCount, vtkIdType regionId, int regionExtent[6],
int extractionMode);
// Fill the ExtractedRegionSizes and ExtractedRegionLabels arrays.
static void GenerateRegionArrays(
vtkImageConnectivityFilter *self, vtkICF::RegionVector& regionInfo,
vtkDataArray *seedScalars, int extent[6], int minLabel, int maxLabel);
// Relabel the image, usually the last method called.
template<class OT>
static void Relabel(
vtkImageData *outData, OT *outPtr,
vtkImageStencilData *stencil, int extent[6],
vtkIdTypeArray *labelMap);
// Sort the ExtractedRegionLabels array and the other arrays.
static void SortRegionArrays(vtkImageConnectivityFilter *self);
// Finalize the output
template<class OT>
static void Finish(
vtkImageConnectivityFilter *self, vtkImageData *outData,
OT *outPtr, vtkImageStencilData *stencil, int extent[6],
vtkDataArray *seedScalars, vtkICF::RegionVector& regionInfo);
// Subtract the lower extent limit from "limits", and return the
// extent size subtract 1 in maxIdx.
static int *ZeroBaseExtent(
const int wholeExtent[6], int extent[6], int maxIdx[3]);
// Execute method for when point seeds are provided.
template <class OT>
static void SeededExecute(
vtkImageConnectivityFilter *self,
vtkImageData *outData, vtkDataSet *seedData, vtkImageStencilData *stencil,
OT *outPtr, unsigned char *maskPtr, int extent[6],
vtkICF::RegionVector& regionInfo);
// Execute method for when no seeds are provided.
template <class OT>
static void SeedlessExecute(
vtkImageConnectivityFilter *self,
vtkImageData *outData, vtkImageStencilData *stencil,
OT *outPtr, unsigned char *maskPtr, int extent[6],
vtkICF::RegionVector& regionInfo);
public:
// Create a bit mask from the input
template<class IT>
static void ExecuteInput(
vtkImageConnectivityFilter *self, vtkImageData *inData, IT *inPtr,
unsigned char *maskPtr, vtkImageStencilData *stencil, int extent[6]);