Change ConnectedComponentImageFilter Mask Shape

Hi,
I’m a newer of itk.

I use ConnectedComponentImageFilter to label my image,
Default mask shape is probably 4 neighborhood, but i want to change the shape to 8 or 16 neighborhood.

How should i do?
Please help me!

SetFullyConnected is probably what you want.

1 Like

Thank you dzenanz!

SetFullyConnected is 3x3 region shape.
But i want to use 5x5 region shape.

How should i change?

Hi @arwtyxouymz,

The built-in methods for neighborhood in ITK usually consider only adjacent voxels, i.e. neighborhoods of size 3^ImageDimension on regular grids, which are the ones that are commonly used.
If you want to use custom neighborhood, you will have to create your own filter.

You can make your own version of the ConnectedComponentImageFilter from the code on GitHub. As this filter uses now an experimental algorithm based on raster lines, I think most of the changes you need to make will be in the ConnectedComponentAlgorithm.h file.

HTH,

Tim

If you run a BinaryDilateImageFilter before the ConnectedComponentsImageFilter you can get the effect of the combined region/neighborhood.

1 Like

Thank you, @blowekamp

If I use a BinaryDilateImageFilter before the ConnectedComponentImageFilter, will some black pixels change to white pixel?

If so, it’s not what i want to do.
I don’t want to change the number of white pixel
The number is same, and I want to check if they are connected or not by larger region.

Do you have any idea?

Thank you @tim-evain

I’m very sorry to trouble you, but please tell me how should i change ConnectedComponentAlgorighm.h.

I just want to change from 3x3 to 5x5.

I’m very sorry…
please help me.

For the algorithm file as well as for the filter one, whenever neighborhood is involved, you should modify the values to get the one you are expecting.
For example, this means changing the radius from 1 to 2 (i.e. switch from 3x3 to 5x5) in the SetupLineOffsets function, activating offsets with value 2 in setConnectivity* functions, etc.
Note that since run-length encoding is used along the first dimension, you may face some trouble if your neighborhood is discontinuous in this direction (i.e. kernels like 1 0 1 0 1).

HTH,

Tim

1 Like

You can just use the original image to mask the output of the combined Dilate/ConnectedComponents.

1 Like

Thank you, @tim-evain

I edited SetupLineOffsets in itkConnectedComponentImageFilter.hxx and setConnectivity in itkConnectedComponentAlgorithm.h.

But it doesn’t work.
I paste my edited code below, can you check it?
Especially, in itkConnectedComponentAlgorithm.h, because I execute by fullyConnected = true, please check in else statement.

######## itkConnectedComponentImageFilter.hxx ##########

template< typename TInputImage, typename TOutputImage, typename TMaskImage >
void
ConnectedComponentImageFilter< TInputImage, TOutputImage, TMaskImage >
::SetupLineOffsets(OffsetVec & LineOffsets)
{
  // Create a neighborhood so that we can generate a table of offsets
  // to "previous" line indexes
  // We are going to mis-use the neighborhood iterators to compute the
  // offset for us. All this messing around produces an array of
  // offsets that will be used to index the map
  typename TOutputImage::Pointer output = this->GetOutput();
  typedef Image< OffsetValueType, TOutputImage::ImageDimension - 1 >  PretendImageType;
  typedef typename PretendImageType::RegionType::SizeType             PretendSizeType;
  typedef typename PretendImageType::RegionType::IndexType            PretendIndexType;
  typedef ConstShapedNeighborhoodIterator< PretendImageType >         LineNeighborhoodType;

  typename PretendImageType::Pointer fakeImage;
  fakeImage = PretendImageType::New();

  typename PretendImageType::RegionType LineRegion;
  //LineRegion = PretendImageType::RegionType::New();

  OutSizeType OutSize = output->GetRequestedRegion().GetSize();

  PretendSizeType PretendSize;
  // The first dimension has been collapsed
  for ( unsigned int i = 0; i < PretendSize.GetSizeDimension(); i++ )
    {
    // PretendSize[i] = OutSize[i + 1];
    PretendSize[i] = OutSize[i + 2];
    }

  LineRegion.SetSize(PretendSize);
  fakeImage->SetRegions(LineRegion);
  PretendSizeType kernelRadius;
  kernelRadius.Fill(1);
  LineNeighborhoodType lnit(kernelRadius, fakeImage, LineRegion);

  // only activate the indices that are "previous" to the current
  // pixel and face connected (exclude the center pixel from the
  // neighborhood)
  //
  setConnectivityPrevious(&lnit, m_FullyConnected);

  typename LineNeighborhoodType::IndexListType ActiveIndexes;
  ActiveIndexes = lnit.GetActiveIndexList();

  typename LineNeighborhoodType::IndexListType::const_iterator LI;

  PretendIndexType idx = LineRegion.GetIndex();
  OffsetValueType offset = fakeImage->ComputeOffset(idx);

  for ( LI = ActiveIndexes.begin(); LI != ActiveIndexes.end(); LI++ )
    {
    LineOffsets.push_back(fakeImage->ComputeOffset( idx + lnit.GetOffset(*LI) ) - offset);
    }

  // LineOffsets is the thing we wanted.
}

######## itkConnectedComponentAlgorithm.h ##########

template< typename TIterator >
TIterator *
setConnectivity(TIterator *it, bool fullyConnected = false)
{
  typename TIterator::OffsetType offset;
  it->ClearActiveList();
  if ( !fullyConnected )
    {
    // only activate the neighbors that are face connected
    // to the current pixel. do not include the center pixel
    offset.Fill(0);
    for ( unsigned int d = 0; d < TIterator::Dimension; ++d )
      {
      // offset[d] = -1;
      offset[d] = -2;
      it->ActivateOffset(offset);
      // offset[d] = 1;
      offset[d] = 2;
      it->ActivateOffset(offset);
      offset[d] = 0;
      }
    }
  else
    {
    // activate all neighbors that are face+edge+vertex
    // connected to the current pixel. do not include the center pixel
    unsigned int centerIndex = it->GetCenterNeighborhoodIndex();
    // for ( unsigned int d = 0; d < centerIndex * 2 + 1; d++ )
    for ( unsigned int d = 0; d < centerIndex * 4 + 1; d++ )
      {
      offset = it->GetOffset(d);
      it->ActivateOffset(offset);
      }
    offset.Fill(0);
    it->DeactivateOffset(offset);
    }
  return it;
}

What is wrong?
Which part should i change?

please help me…

Hi,

There are multiple incorrect modifications that I can see.

This doesn’t seem to be about neighborhood, I don’t think you should modify that.

I’m not sure this function is called, but setConnectivityPrevious is, so you have to modify it also.

As the centerIndex will automatically increase if kernel size was correctly set previously, you don’t have to change the loop extent.

Anyway, if modifying the code to get your desired behavior troubles you, you can go with the solution that @blowekamp gave you: dilate the image first (with a box structuring element of size 3x3), then run the filter on this dilated image, and finally mask the result with initial image.

HTH,

Tim

(P.S. for the next weeks, it’s likely that I will be away)