Permalink
Fetching contributors…
Cannot retrieve contributors at this time
738 lines (624 sloc) 29.5 KB
/************************************************************************/
/* */
/* Copyright 2006-2007 by F. Heinrich, B. Seppke, Ullrich Koethe */
/* */
/* This file is part of the VIGRA computer vision library. */
/* The VIGRA Website is */
/* http://hci.iwr.uni-heidelberg.de/vigra/ */
/* Please direct questions, bug reports, and contributions to */
/* ullrich.koethe@iwr.uni-heidelberg.de or */
/* vigra@informatik.uni-hamburg.de */
/* */
/* Permission is hereby granted, free of charge, to any person */
/* obtaining a copy of this software and associated documentation */
/* files (the "Software"), to deal in the Software without */
/* restriction, including without limitation the rights to use, */
/* copy, modify, merge, publish, distribute, sublicense, and/or */
/* sell copies of the Software, and to permit persons to whom the */
/* Software is furnished to do so, subject to the following */
/* conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the */
/* Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
/* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
/* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
/* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
/* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
/* OTHER DEALINGS IN THE SOFTWARE. */
/* */
/************************************************************************/
#ifndef VIGRA_LABELVOLUME_HXX
#define VIGRA_LABELVOLUME_HXX
#include "voxelneighborhood.hxx"
#include "multi_array.hxx"
#include "union_find.hxx"
namespace vigra{
/** \addtogroup Labeling Connected Components Labeling
The 3-dimensional connected components algorithms may use either 6 or 26 connectivity.
By means of a functor the merge criterion can be defined arbitrarily.
*/
//@{
/********************************************************/
/* */
/* labelVolume */
/* */
/********************************************************/
/** \brief Find the connected components of a segmented volume.
Deprecated. Use \ref labelMultiArray() instead.
Connected components are defined as regions with uniform voxel
values. Thus, <TT>T1</TT> either must be equality comparable,
or an EqualityFunctor must be provided explicitly that realizes
the desired equivalence predicate. The destination's value type
<tt>T2</tt> should be large enough to hold the labels
without overflow. Region numbers will be a consecutive sequence
starting with one and ending with the region number returned by
the function (inclusive).
Return: the number of regions found (= largest region label)
See \ref labelMultiArray() for a dimension-independent implementation of
connected components labelling.
<b> Declarations:</b>
pass 3D array views:
\code
namespace vigra {
template <class T1, class S1,
class T2, class S2,
class Neighborhood3D,
class EqualityFunctor = std::equal_to<T1> >
unsigned int
labelVolume(MultiArrayView<3, T1, S1> const & source,
MultiArrayView<3, T2, S2> dest,
Neighborhood3D neighborhood3D,
EqualityFunctor equal = EqualityFunctor());
}
\endcode
\deprecatedAPI{labelVolume}
pass \ref MultiIteratorPage "MultiIterators" and \ref DataAccessors :
\code
namespace vigra {
template <class SrcIterator, class SrcAccessor,class SrcShape,
class DestIterator, class DestAccessor,
class Neighborhood3D>
unsigned int labelVolume(SrcIterator s_Iter, SrcShape srcShape, SrcAccessor sa,
DestIterator d_Iter, DestAccessor da,
Neighborhood3D neighborhood3D);
template <class SrcIterator, class SrcAccessor,class SrcShape,
class DestIterator, class DestAccessor,
class Neighborhood3D, class EqualityFunctor>
unsigned int labelVolume(SrcIterator s_Iter, SrcShape srcShape, SrcAccessor sa,
DestIterator d_Iter, DestAccessor da,
Neighborhood3D neighborhood3D, EqualityFunctor equal);
}
\endcode
use argument objects in conjunction with \ref ArgumentObjectFactories :
\code
namespace vigra {
template <class SrcIterator, class SrcAccessor,class SrcShape,
class DestIterator, class DestAccessor,
class Neighborhood3D>
unsigned int labelVolume(triple<SrcIterator, SrcShape, SrcAccessor> src,
pair<DestIterator, DestAccessor> dest,
Neighborhood3D neighborhood3D);
template <class SrcIterator, class SrcAccessor,class SrcShape,
class DestIterator, class DestAccessor,
class Neighborhood3D, class EqualityFunctor>
unsigned int labelVolume(triple<SrcIterator, SrcShape, SrcAccessor> src,
pair<DestIterator, DestAccessor> dest,
Neighborhood3D neighborhood3D, EqualityFunctor equal);
}
\endcode
use with 3D-Six-Neighborhood:
\code
namespace vigra {
template <class SrcIterator, class SrcAccessor,class SrcShape,
class DestIterator, class DestAccessor>
unsigned int labelVolumeSix(triple<SrcIterator, SrcShape, SrcAccessor> src,
pair<DestIterator, DestAccessor> dest);
}
\endcode
\deprecatedEnd
<b> Usage:</b>
<b>\#include</b> \<vigra/labelvolume.hxx\><br>
Namespace: vigra
\code
typedef MultiArray<3,int> IntVolume;
IntVolume src(Shape3(w,h,d));
IntVolume dest(Shape3(w,h,d));
// find 6-connected regions
int max_region_label = labelVolumeSix(src, dest);
// find 26-connected regions
int max_region_label = labelVolume(src, dest, NeighborCode3DTwentySix());
\endcode
\deprecatedUsage{labelVolume}
\code
typedef vigra::MultiArray<3,int> IntVolume;
IntVolume src(IntVolume::difference_type(w,h,d));
IntVolume dest(IntVolume::difference_type(w,h,d));
// find 6-connected regions
int max_region_label = vigra::labelVolumeSix(srcMultiArrayRange(src), destMultiArray(dest));
// find 26-connected regions
int max_region_label = vigra::labelVolume(srcMultiArrayRange(src), destMultiArray(dest), NeighborCode3DTwentySix());
\endcode
<b> Required Interface:</b>
\code
SrcIterator src_begin;
SrcShape shape;
DestIterator dest_begin;
SrcAccessor src_accessor;
DestAccessor dest_accessor;
SrcAccessor::value_type u = src_accessor(src_begin);
u == u // first form
EqualityFunctor equal; // second form
equal(u, u) // second form
int i;
dest_accessor.set(i, dest_begin);
\endcode
\deprecatedEnd
*/
doxygen_overloaded_function(template <...> unsigned int labelVolume)
template <class SrcIterator, class SrcAccessor,class SrcShape,
class DestIterator, class DestAccessor,
class Neighborhood3D, class EqualityFunctor>
unsigned int labelVolume(SrcIterator s_Iter, SrcShape srcShape, SrcAccessor sa,
DestIterator d_Iter, DestAccessor da,
Neighborhood3D, EqualityFunctor equal)
{
typedef typename DestAccessor::value_type LabelType;
//basically needed for iteration and border-checks
int w = srcShape[0], h = srcShape[1], d = srcShape[2];
int x,y,z;
// temporary image to store region labels
UnionFindArray<LabelType> label;
//Declare traversers for all three dims at target
SrcIterator zs = s_Iter;
DestIterator zd = d_Iter;
// initialize the neighborhood traversers
NeighborOffsetCirculator<Neighborhood3D> nce(Neighborhood3D::CausalLast);
++nce;
// pass 1: scan image from upper left front to lower right back
// to find connected components
// Each component will be represented by a tree of pixels. Each
// pixel contains the scan order address of its parent in the
// tree. In order for pass 2 to work correctly, the parent must
// always have a smaller scan order address than the child.
// Therefore, we can merge trees only at their roots, because the
// root of the combined tree must have the smallest scan order
// address among all the tree's pixels/ nodes. The root of each
// tree is distinguished by pointing to itself (it contains its
// own scan order address). This condition is enforced whenever a
// new region is found or two regions are merged
for(z = 0; z != d; ++z, ++zs.dim2(), ++zd.dim2())
{
SrcIterator ys(zs);
DestIterator yd(zd);
for(y = 0; y != h; ++y, ++ys.dim1(), ++yd.dim1())
{
SrcIterator xs(ys);
DestIterator xd(yd);
for(x = 0; x != w; ++x, ++xs.dim0(), ++xd.dim0())
{
LabelType currentIndex = label.nextFreeIndex();
//check whether there is a special border treatment to be used or not
AtVolumeBorder atBorder = isAtVolumeBorderCausal(x,y,z,w,h,d);
//We are not at the border!
if(atBorder == NotAtBorder)
{
NeighborOffsetCirculator<Neighborhood3D> nc(Neighborhood3D::CausalFirst);
do
{
// if colors are equal
if(equal(sa(xs), sa(xs, *nc)))
{
currentIndex = label.makeUnion(da(xd,*nc), currentIndex);
}
++nc;
}
while(nc!=nce);
}
else //we are at a border - handle this!!
{
NeighborOffsetCirculator<Neighborhood3D> nc(Neighborhood3D::nearBorderDirectionsCausal(atBorder,0));
int j=0;
while(nc.direction() != Neighborhood3D::Error)
{
int dummy = x+(*nc)[0]; // prevents an apparently incorrect optimization in gcc 4.8
if (dummy<0)
{
std::cerr << "internal error " << dummy << std::endl;
}
// colors equal???
if(equal(sa(xs), sa(xs, *nc)))
{
currentIndex = label.makeUnion(da(xd,*nc), currentIndex);
}
nc.turnTo(Neighborhood3D::nearBorderDirectionsCausal(atBorder,++j));
}
}
da.set(label.finalizeIndex(currentIndex), xd);
}
}
}
LabelType count = label.makeContiguous();
// pass 2: assign one label to each region (tree)
// so that labels form a consecutive sequence 1, 2, ...
zd = d_Iter;
for(z=0; z != d; ++z, ++zd.dim2())
{
DestIterator yd(zd);
for(y=0; y != h; ++y, ++yd.dim1())
{
DestIterator xd(yd);
for(x = 0; x != w; ++x, ++xd.dim0())
{
da.set(label.findLabel(da(xd)), xd);
}
}
}
return count;
}
template <class SrcIterator, class SrcAccessor,class SrcShape,
class DestIterator, class DestAccessor,
class Neighborhood3D>
unsigned int labelVolume(SrcIterator s_Iter, SrcShape srcShape, SrcAccessor sa,
DestIterator d_Iter, DestAccessor da,
Neighborhood3D neighborhood3D)
{
return labelVolume(s_Iter, srcShape, sa, d_Iter, da, neighborhood3D, std::equal_to<typename SrcAccessor::value_type>());
}
template <class SrcIterator, class SrcShape, class SrcAccessor,
class DestIterator, class DestAccessor,
class Neighborhood3D>
unsigned int labelVolume(triple<SrcIterator, SrcShape, SrcAccessor> src,
pair<DestIterator, DestAccessor> dest,
Neighborhood3D neighborhood3D)
{
return labelVolume(src.first, src.second, src.third, dest.first, dest.second, neighborhood3D, std::equal_to<typename SrcAccessor::value_type>());
}
template <class SrcIterator, class SrcShape, class SrcAccessor,
class DestIterator, class DestAccessor,
class Neighborhood3D, class EqualityFunctor>
unsigned int labelVolume(triple<SrcIterator, SrcShape, SrcAccessor> src,
pair<DestIterator, DestAccessor> dest,
Neighborhood3D neighborhood3D, EqualityFunctor equal)
{
return labelVolume(src.first, src.second, src.third, dest.first, dest.second, neighborhood3D, equal);
}
template <class T1, class S1,
class T2, class S2,
class Neighborhood3D, class EqualityFunctor>
inline unsigned int
labelVolume(MultiArrayView<3, T1, S1> const & source,
MultiArrayView<3, T2, S2> dest,
Neighborhood3D neighborhood3D,
EqualityFunctor equal)
{
vigra_precondition(source.shape() == dest.shape(),
"labelVolume(): shape mismatch between input and output.");
return labelVolume(srcMultiArrayRange(source), destMultiArray(dest), neighborhood3D, equal);
}
template <class T1, class S1,
class T2, class S2,
class Neighborhood3D>
inline unsigned int
labelVolume(MultiArrayView<3, T1, S1> const & source,
MultiArrayView<3, T2, S2> dest,
Neighborhood3D neighborhood3D)
{
vigra_precondition(source.shape() == dest.shape(),
"labelVolume(): shape mismatch between input and output.");
return labelVolume(srcMultiArrayRange(source), destMultiArray(dest), neighborhood3D, std::equal_to<T1>());
}
/********************************************************/
/* */
/* labelVolumeSix */
/* */
/********************************************************/
/** \brief Find the connected components of a segmented volume
using the 6-neighborhood.
See \ref labelVolume() for detailed documentation.
*/
template <class SrcIterator, class SrcAccessor,class SrcShape,
class DestIterator, class DestAccessor>
unsigned int labelVolumeSix(triple<SrcIterator, SrcShape, SrcAccessor> src,
pair<DestIterator, DestAccessor> dest)
{
return labelVolume(src.first, src.second, src.third, dest.first, dest.second, NeighborCode3DSix(), std::equal_to<typename SrcAccessor::value_type>());
}
template <class T1, class S1,
class T2, class S2>
unsigned int labelVolumeSix(MultiArrayView<3, T1, S1> const & source,
MultiArrayView<3, T2, S2> dest)
{
return labelVolume(srcMultiArrayRange(source), destMultiArray(dest),
NeighborCode3DSix(), std::equal_to<T1>());
}
/********************************************************/
/* */
/* labelVolumeWithBackground */
/* */
/********************************************************/
/** \brief Find the connected components of a segmented volume,
excluding the background from labeling.
Deprecated. Use \ref labelMultiArray() instead.
This function works like \ref labelVolume(), but considers all background voxels
(i.e. voxels having the given '<TT>background_value</TT>') as a single region that
is ignored when determining connected components and remains untouched in the
destination array. Usually, you will zero-initialize the output array, so that
the background gets label 0 (remember that actual region labels start at one).
Return: the number of regions found (= largest region label)
See \ref labelMultiArrayWithBackground() for a dimension-independent implementation
if this algorithm.
<b> Declarations:</b>
pass 3D array views:
\code
namespace vigra {
template <class T1, class S1,
class T2, class S2,
class Neighborhood3D,
class ValueType,
class EqualityFunctor = std::equalt_to<T1> >
unsigned int
labelVolumeWithBackground(MultiArrayView<3, T1, S1> const & source,
MultiArrayView<3, T2, S2> dest,
Neighborhood3D neighborhood3D,
ValueType backgroundValue,
EqualityFunctor equal = EqualityFunctor());
}
\endcode
\deprecatedAPI{labelVolumeWithBackground}
pass \ref MultiIteratorPage "MultiIterators" and \ref DataAccessors :
\code
namespace vigra {
template <class SrcIterator, class SrcAccessor,class SrcShape,
class DestIterator, class DestAccessor,
class Neighborhood3D, class ValueType>
unsigned int labelVolumeWithBackground( SrcIterator s_Iter, SrcShape srcShape, SrcAccessor sa,
DestIterator d_Iter, DestAccessor da,
Neighborhood3D neighborhood3D, ValueType background_value);
template <class SrcIterator, class SrcAccessor,class SrcShape,
class DestIterator, class DestAccessor,
class Neighborhood3D, class ValueType, class EqualityFunctor>
unsigned int labelVolumeWithBackground( SrcIterator s_Iter, SrcShape srcShape, SrcAccessor sa,
DestIterator d_Iter, DestAccessor da,
Neighborhood3D neighborhood3D, ValueType background_value,
EqualityFunctor equal);
}
\endcode
use argument objects in conjunction with \ref ArgumentObjectFactories :
\code
namespace vigra {
template <class SrcIterator, class SrcAccessor,class SrcShape,
class DestIterator, class DestAccessor,
class Neighborhood3D, class ValueType>
unsigned int labelVolumeWithBackground( triple<SrcIterator, SrcShape, SrcAccessor> src,
pair<DestIterator, DestAccessor> dest,
Neighborhood3D neighborhood3D, ValueType background_value);
template <class SrcIterator, class SrcAccessor,class SrcShape,
class DestIterator, class DestAccessor,
class Neighborhood3D, class ValueType, class EqualityFunctor>
unsigned int labelVolumeWithBackground( triple<SrcIterator, SrcShape, SrcAccessor> src,
pair<DestIterator, DestAccessor> dest,
Neighborhood3D neighborhood3D, ValueType background_value,
EqualityFunctor equal);
}
\endcode
\deprecatedEnd
<b> Usage:</b>
<b>\#include</b> \<vigra/labelvolume.hxx\><br>
Namespace: vigra
\code
typedef vigra::MultiArray<3,int> IntVolume;
IntVolume src(Shape3(w,h,d));
IntVolume dest(Shape3(w,h,d));
// find 6-connected regions
int max_region_label = labelVolumeWithBackground(src, dest, NeighborCode3DSix(), 0);
\endcode
\deprecatedUsage{labelVolumeWithBackground}
\code
typedef vigra::MultiArray<3,int> IntVolume;
IntVolume src(IntVolume::difference_type(w,h,d));
IntVolume dest(IntVolume::difference_type(w,h,d));
// find 6-connected regions
int max_region_label = vigra::labelVolumeWithBackground(
srcMultiArrayRange(src), destMultiArray(dest), NeighborCode3DSix(), 0);
\endcode
<b> Required Interface:</b>
\code
SrcIterator src_begin;
SrcShape shape;
DestIterator dest_begin;
SrcAccessor src_accessor;
DestAccessor dest_accessor;
SrcAccessor::value_type u = src_accessor(src_begin);
u == u // first form
EqualityFunctor equal; // second form
equal(u, u) // second form
int i;
dest_accessor.set(i, dest_begin);
\endcode
\deprecatedEnd
*/
doxygen_overloaded_function(template <...> unsigned int labelVolumeWithBackground)
template <class SrcIterator, class SrcAccessor,class SrcShape,
class DestIterator, class DestAccessor,
class Neighborhood3D,
class ValueType, class EqualityFunctor>
unsigned int labelVolumeWithBackground(SrcIterator s_Iter, SrcShape srcShape, SrcAccessor sa,
DestIterator d_Iter, DestAccessor da,
Neighborhood3D,
ValueType backgroundValue, EqualityFunctor equal)
{
typedef typename DestAccessor::value_type LabelType;
//basically needed for iteration and border-checks
int w = srcShape[0], h = srcShape[1], d = srcShape[2];
int x,y,z;
// temporary image to store region labels
UnionFindArray<LabelType> label;
//Declare traversers for all three dims at target
SrcIterator zs = s_Iter;
DestIterator zd = d_Iter;
// initialize the neighborhood traversers
NeighborOffsetCirculator<Neighborhood3D> nce(Neighborhood3D::CausalLast);
++nce;
// pass 1: scan image from upper left front to lower right back
// to find connected components
// Each component will be represented by a tree of pixels. Each
// pixel contains the scan order address of its parent in the
// tree. In order for pass 2 to work correctly, the parent must
// always have a smaller scan order address than the child.
// Therefore, we can merge trees only at their roots, because the
// root of the combined tree must have the smallest scan order
// address among all the tree's pixels/ nodes. The root of each
// tree is distinguished by pointing to itself (it contains its
// own scan order address). This condition is enforced whenever a
// new region is found or two regions are merged
for(z = 0; z != d; ++z, ++zs.dim2(), ++zd.dim2())
{
SrcIterator ys(zs);
DestIterator yd(zd);
for(y = 0; y != h; ++y, ++ys.dim1(), ++yd.dim1())
{
SrcIterator xs(ys);
DestIterator xd(yd);
for(x = 0; x != w; ++x, ++xs.dim0(), ++xd.dim0())
{
if(equal(sa(xs), backgroundValue))
{
//da.set(label.getIndex(0), xd);
da.set(0, xd);
continue;
}
LabelType currentIndex = label.nextFreeIndex();
//check whether there is a special border treatment to be used or not
AtVolumeBorder atBorder = isAtVolumeBorderCausal(x,y,z,w,h,d);
//We are not at the border!
if(atBorder == NotAtBorder)
{
NeighborOffsetCirculator<Neighborhood3D> nc(Neighborhood3D::CausalFirst);
do
{
// if colors are equal
if(equal(sa(xs), sa(xs, *nc)))
{
currentIndex = label.makeUnion(da(xd,*nc), currentIndex);
}
++nc;
}
while(nc!=nce);
}
else //we are at a border - handle this!!
{
NeighborOffsetCirculator<Neighborhood3D> nc(Neighborhood3D::nearBorderDirectionsCausal(atBorder,0));
int j=0;
while(nc.direction() != Neighborhood3D::Error)
{
int dummy = x+(*nc)[0]; // prevents an apparently incorrect optimization in gcc 4.8
if (dummy<0)
{
std::cerr << "internal error " << dummy << std::endl;
}
// colors equal???
if(equal(sa(xs), sa(xs, *nc)))
{
currentIndex = label.makeUnion(da(xd,*nc), currentIndex);
}
nc.turnTo(Neighborhood3D::nearBorderDirectionsCausal(atBorder,++j));
}
}
da.set(label.finalizeIndex(currentIndex), xd);
}
}
}
LabelType count = label.makeContiguous();
// pass 2: assign one label to each region (tree)
// so that labels form a consecutive sequence 1, 2, ...
zd = d_Iter;
for(z=0; z != d; ++z, ++zd.dim2())
{
DestIterator yd(zd);
for(y=0; y != h; ++y, ++yd.dim1())
{
DestIterator xd(yd);
for(x = 0; x != w; ++x, ++xd.dim0())
{
da.set(label.findLabel(da(xd)), xd);
}
}
}
return count;
}
template <class SrcIterator, class SrcAccessor,class SrcShape,
class DestIterator, class DestAccessor,
class Neighborhood3D,
class ValueType>
inline unsigned int
labelVolumeWithBackground(SrcIterator s_Iter, SrcShape srcShape, SrcAccessor sa,
DestIterator d_Iter, DestAccessor da,
Neighborhood3D neighborhood3D, ValueType backgroundValue)
{
return labelVolumeWithBackground(s_Iter, srcShape, sa, d_Iter, da, neighborhood3D, backgroundValue, std::equal_to<typename SrcAccessor::value_type>());
}
template <class SrcIterator, class SrcShape, class SrcAccessor,
class DestIterator, class DestAccessor,
class Neighborhood3D,
class ValueType,
class EqualityFunctor>
inline unsigned int
labelVolumeWithBackground(triple<SrcIterator, SrcShape, SrcAccessor> src,
pair<DestIterator, DestAccessor> dest,
Neighborhood3D neighborhood3D, ValueType backgroundValue, EqualityFunctor equal)
{
return labelVolumeWithBackground(src.first, src.second, src.third, dest.first, dest.second, neighborhood3D, backgroundValue, equal);
}
template <class SrcIterator, class SrcShape, class SrcAccessor,
class DestIterator, class DestAccessor,
class Neighborhood3D,
class ValueType>
inline unsigned int
labelVolumeWithBackground(triple<SrcIterator, SrcShape, SrcAccessor> src,
pair<DestIterator, DestAccessor> dest,
Neighborhood3D neighborhood3D, ValueType backgroundValue)
{
return labelVolumeWithBackground(src.first, src.second, src.third, dest.first, dest.second,
neighborhood3D, backgroundValue, std::equal_to<typename SrcAccessor::value_type>());
}
template <class T1, class S1,
class T2, class S2,
class Neighborhood3D,
class ValueType,
class EqualityFunctor>
inline unsigned int
labelVolumeWithBackground(MultiArrayView<3, T1, S1> const & source,
MultiArrayView<3, T2, S2> dest,
Neighborhood3D neighborhood3D,
ValueType backgroundValue,
EqualityFunctor equal)
{
vigra_precondition(source.shape() == dest.shape(),
"labelVolumeWithBackground(): shape mismatch between input and output.");
return labelVolumeWithBackground(srcMultiArrayRange(source), destMultiArray(dest),
neighborhood3D, backgroundValue, equal);
}
template <class T1, class S1,
class T2, class S2,
class Neighborhood3D,
class ValueType>
inline unsigned int
labelVolumeWithBackground(MultiArrayView<3, T1, S1> const & source,
MultiArrayView<3, T2, S2> dest,
Neighborhood3D neighborhood3D,
ValueType backgroundValue)
{
vigra_precondition(source.shape() == dest.shape(),
"labelVolumeWithBackground(): shape mismatch between input and output.");
return labelVolumeWithBackground(srcMultiArrayRange(source), destMultiArray(dest),
neighborhood3D, backgroundValue,
std::equal_to<T1>());
}
//@}
} //end of namespace vigra
#endif //VIGRA_LABELVOLUME_HXX