Skip to content

Commit

Permalink
Merge pull request #1269 from rolalaro/dilatationOfSizeN
Browse files Browse the repository at this point in the history
N x N dilatation and erosion
  • Loading branch information
fspindle authored Oct 31, 2023
2 parents 6582d80 + 1a93ee0 commit a2ddf0c
Show file tree
Hide file tree
Showing 2 changed files with 218 additions and 9 deletions.
143 changes: 135 additions & 8 deletions modules/core/include/visp3/core/vpImageMorphology.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,19 +76,31 @@ class VISP_EXPORT vpImageMorphology

private:
/**
* @brief Modify the image by applying the \b operation on each of its elements on a 3x3
* \brief Modify the image by applying the \b operation on each of its elements on a 3x3
* grid.
*
* @param T Either a class such as vpRGBa or a type such as double, unsigned char ...
* @param I The image we want to modify.
* @param null_value The value that is padded to the input image to manage the borders.
* @param operation The operation to apply to its elements on a 3x3 grid.
* @param connexity Either a 4-connexity, if we want to take into account only the horizontal
* \tparam T Either a class such as vpRGBa or a type such as double, unsigned char ...
* \param[out] I The image we want to modify.
* \param[in] null_value The value that is padded to the input image to manage the borders.
* \param[in] operation The operation to apply to its elements on a 3x3 grid.
* \param[in] connexity Either a 4-connexity, if we want to take into account only the horizontal
* and vertical neighbors, or a 8-connexity, if we want to also take into account the diagonal neighbors.
*/
template <typename T>
static void imageOperation(vpImage<T> &I, const T &null_value, const T &(*operation)(const T &, const T &), const vpConnexityType &connexity = CONNEXITY_4);

/**
* \brief Modify the image by applying the \b operation on each of its elements on a \b size x \b size
* grid. The connexity that is used is a 8-connexity.
*
* \tparam T Any type such as double, unsigned char ...
* \param[out] I The image we want to modify.
* \param[in] size The size of the window with which we want to work.
* \param[in] operation The operation to apply to its elements.
*/
template <typename T>
static void imageOperation(vpImage<T> &I, const int &size, const T &(*operation)(const T &, const T &));

public:
template <class Type>
static void erosion(vpImage<Type> &I, Type value, Type value_out, vpConnexityType connexity = CONNEXITY_4);
Expand All @@ -102,6 +114,12 @@ class VISP_EXPORT vpImageMorphology
template <typename T>
static void dilatation(vpImage<T> &I, const vpConnexityType &connexity = CONNEXITY_4);

template <typename T>
static void erosion(vpImage<T> &I, const int &size);

template <typename T>
static void dilatation(vpImage<T> &I, const int &size);

#if defined(VISP_BUILD_DEPRECATED_FUNCTIONS)
/*!
@name Deprecated functions
Expand Down Expand Up @@ -351,7 +369,7 @@ void vpImageMorphology::imageOperation(vpImage<T> &I, const T &null_value, const
\param I : Image to process.
\param connexity : Type of connexity: 4 or 8.
\sa dilatation(vpImage<unsigned char> &, const vpConnexityType &)
\sa dilatation(vpImage<T> &, const vpConnexityType &)
*/
template <typename T>
void vpImageMorphology::erosion(vpImage<T> &I, const vpConnexityType &connexity)
Expand Down Expand Up @@ -381,14 +399,123 @@ void vpImageMorphology::erosion(vpImage<T> &I, const vpConnexityType &connexity)
\param I : Image to process.
\param connexity : Type of connexity: 4 or 8.
\sa erosion(vpImage<unsigned char> &, const vpConnexityType &)
\sa erosion(vpImage<T> &, const vpConnexityType &)
*/
template <typename T>
void vpImageMorphology::dilatation(vpImage<T> &I, const vpConnexityType &connexity)
{
const T &(*operation)(const T & a, const T & b) = std::max;
vpImageMorphology::imageOperation(I, std::numeric_limits<T>::min(), operation, connexity);
}

/**
* \brief Dilatation of \b size >=3 with 8-connectivity.
*
* \tparam T Any type of image, except vpRGBa .
* \param[out] I The image to which the dilatation must be applied, where the dilatation corresponds
* to a max operator on a window of size \b size.
* \param[in] size The size of the window on which is performed the max operator for each pixel.
*/
template<typename T>
void vpImageMorphology::imageOperation(vpImage<T> &I, const int &size, const T &(*operation)(const T &, const T &))
{
if (size % 2 != 1) {
throw(vpException(vpException::badValue, "Dilatation kernel must be odd."));
}

const int width_in = I.getWidth();
const int height_in = I.getHeight();
int halfKernelSize = size / 2;
vpImage<T> J = I;

for (int r = 0; r < height_in; r++) {
// Computing the rows we can explore without going outside the limits of the image
int r_iterator_start = -halfKernelSize, r_iterator_stop = halfKernelSize + 1;
if (r - halfKernelSize < 0) {
r_iterator_start = -r;
}
else if (r + halfKernelSize >= height_in) {
r_iterator_stop = height_in - r;
}
for (int c = 0; c < width_in; c++) {
T value = I[r][c];
// Computing the columns we can explore without going outside the limits of the image
int c_iterator_start = -halfKernelSize, c_iterator_stop = halfKernelSize + 1;
if (c - halfKernelSize < 0) {
c_iterator_start = -c;
}
else if (c + halfKernelSize >= width_in) {
c_iterator_stop = width_in - c;
}
for (int r_iterator = r_iterator_start; r_iterator < r_iterator_stop; r_iterator++) {
for (int c_iterator = c_iterator_start; c_iterator < c_iterator_stop; c_iterator++) {
value = operation(value, J[r + r_iterator][c + c_iterator]);
}
}
I[r][c] = value;
}
}
}

/*!
Erode an image using the given structuring element.
The erosion of \f$ A \left( x, y \right) \f$ by \f$ B \left (x, y
\right) \f$ is defined as: \f[ \left ( A \ominus B \right ) \left( x,y
\right) = \textbf{min} \left \{ A \left ( x+x', y+y' \right ) - B \left (
x', y'\right ) | \left ( x', y'\right ) \subseteq D_B \right \} \f] where
\f$ D_B \f$ is the domain of the structuring element \f$ B \f$ and \f$ A
\left( x,y \right) \f$ is assumed to be \f$ + \infty \f$ outside the domain
of the image.
In our case, the erosion is performed with a flat structuring element
\f$ \left( B \left( x,y \right) = 0 \right) \f$. The erosion using
such a structuring element is equivalent to a local-minimum operator: \f[
\left ( A \ominus B \right ) \left( x,y \right) = \textbf{min} \left \{ A
\left ( x+x', y+y' \right ) | \left ( x', y'\right ) \subseteq D_B \right \}
\f]
\param I : Image to process.
\param size : The size of the kernel
\sa dilatation(vpImage<T> &, const int &)
*/
template <typename T>
void vpImageMorphology::erosion(vpImage<T> &I, const int &size)
{
const T &(*operation)(const T & a, const T & b) = std::min;
vpImageMorphology::imageOperation(I, size, operation);
}

/*!
Dilate an image using the given structuring element.
The dilatation of \f$ A \left( x, y \right) \f$ by \f$ B \left
(x, y \right) \f$ is defined as: \f[ \left ( A \oplus B \right ) \left( x,y
\right) = \textbf{max} \left \{ A \left ( x-x', y-y' \right ) + B \left (
x', y'\right ) | \left ( x', y'\right ) \subseteq D_B \right \} \f] where
\f$ D_B \f$ is the domain of the structuring element \f$ B \f$ and \f$ A
\left( x,y \right) \f$ is assumed to be \f$ - \infty \f$ outside the domain
of the image.
In our case, the dilatation is performed with a flat structuring element
\f$ \left( B \left( x,y \right) = 0 \right) \f$. The dilatation using
such a structuring element is equivalent to a local-maximum operator: \f[
\left ( A \oplus B \right ) \left( x,y \right) = \textbf{max} \left \{ A
\left ( x-x', y-y' \right ) | \left ( x', y'\right ) \subseteq D_B \right \}
\f]
\param I : Image to process.
\param size : The size of the kernel.
\sa erosion(vpImage<T> &, const int &)
*/
template <typename T>
void vpImageMorphology::dilatation(vpImage<T> &I, const int &size)
{
const T &(*operation)(const T & a, const T & b) = std::max;
vpImageMorphology::imageOperation(I, size, operation);
}
#endif

/*
Expand Down
84 changes: 83 additions & 1 deletion modules/core/test/image/testImageMorphology.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,26 @@ TEST_CASE("Binary image morphology", "[image_morphology]")
CHECK((I_morpho_ref == I_morpho_tpl));
CHECK((I_morpho_ref == I_morpho));
}

SECTION("8-connexity-size5")
{
vpImage<unsigned char> I_dilatation_ref(8, 16, 1);
I_dilatation_ref[0][0] = 0;
I_dilatation_ref[0][1] = 0;
I_dilatation_ref[0][2] = 0;
I_dilatation_ref[6][12] = 0;
I_dilatation_ref[7][12] = 0;
vpImage<unsigned char> I_dilatation = I;
vpImage<unsigned char> I_erosion_ref(8, 16, 0);
vpImage<unsigned char> I_erosion = I;

const int size = 5;
vpImageMorphology::dilatation(I_dilatation, size);
vpImageMorphology::erosion(I_erosion, size);

CHECK((I_dilatation_ref == I_dilatation));
CHECK((I_erosion_ref == I_erosion));
}
}

SECTION("Matlab reference")
Expand Down Expand Up @@ -211,6 +231,37 @@ TEST_CASE("Gray image morphology", "[image_morphology]")

CHECK((I_morpho_ref == I_morpho));
}

SECTION("8-connexity-size5")
{
const int size = 5;
vpImage<unsigned char> I_morpho(12, 12);
unsigned char count = 1;
for (int r = 0; r < 12; r++) {
for (int c = 0; c < 12; c++) {
I_morpho[r][c] = count;
count++;
}
}
unsigned char image_data_dilatation[12 * 12] = {
27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 36, 36,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 48, 48,
51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 60, 60,
63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 72, 72,
75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 84, 84,
87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 96, 96,
99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 108, 108,
111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 120, 120,
123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 132, 132,
135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 144,
135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 144,
135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 144 };
vpImage<unsigned char> I_dilatation_ref(image_data_dilatation, 12, 12, true);

vpImageMorphology::dilatation<unsigned char>(I_morpho, size);

CHECK((I_dilatation_ref == I_morpho));
}
}

SECTION("Erosion")
Expand Down Expand Up @@ -238,6 +289,37 @@ TEST_CASE("Gray image morphology", "[image_morphology]")

CHECK((I_morpho_ref == I_morpho));
}

SECTION("8-connexity-size5")
{
const int size = 5;
vpImage<unsigned char> I_morpho(12, 12);
unsigned char count = 1;
for (int r = 0; r < 12; r++) {
for (int c = 0; c < 12; c++) {
I_morpho[r][c] = count;
count++;
}
}
unsigned char image_data_erosion[12 * 12] = {
1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
13, 13, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
25, 25, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
37, 37, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
49, 49, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
61, 61, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
73, 73, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
85, 85, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
97, 97, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
109, 109, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118 };
vpImage<unsigned char> I_erosion_ref(image_data_erosion, 12, 12, true);

vpImageMorphology::erosion<unsigned char>(I_morpho, size);

CHECK((I_erosion_ref == I_morpho));
}
}

SECTION("Matlab reference")
Expand Down Expand Up @@ -336,7 +418,7 @@ TEST_CASE("Gray image morphology", "[image_morphology]")
}
}

int main(int argc, char *argv [])
int main(int argc, char *argv[])
{
Catch::Session session; // There must be exactly one instance

Expand Down

0 comments on commit a2ddf0c

Please sign in to comment.