// Copyright (C) 2006 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_MORPHOLOGICAL_OPERATIONs_
#define DLIB_MORPHOLOGICAL_OPERATIONs_
#include "../pixel.h"
#include "thresholding.h"
#include "morphological_operations_abstract.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
namespace morphological_operations_helpers
{
template <typename image_type>
bool is_binary_image (
const image_type& img
)
/*!
ensures
- returns true if img contains only on_pixel and off_pixel values.
- returns false otherwise
!*/
{
for (long r = 0; r < img.nr(); ++r)
{
for (long c = 0; c < img.nc(); ++c)
{
if (img[r][c] != on_pixel && img[r][c] != off_pixel)
{
return false;
}
}
}
return true;
}
template <
long M,
long N
>
bool is_binary_image (
const unsigned char (&structuring_element)[M][N]
)
/*!
ensures
- returns true if structuring_element contains only on_pixel and off_pixel values.
- returns false otherwise
!*/
{
for (long m = 0; m < M; ++m)
{
for (long n = 0; n < N; ++n)
{
if (structuring_element[m][n] != on_pixel &&
structuring_element[m][n] != off_pixel)
{
return false;
}
}
}
return true;
}
}
// ----------------------------------------------------------------------------------------
template <
typename in_image_type,
typename out_image_type,
long M,
long N
>
void binary_dilation (
const in_image_type& in_img,
out_image_type& out_img,
const unsigned char (&structuring_element)[M][N]
)
{
COMPILE_TIME_ASSERT( pixel_traits<typename in_image_type::type>::has_alpha == false );
COMPILE_TIME_ASSERT( pixel_traits<typename out_image_type::type>::has_alpha == false );
using namespace morphological_operations_helpers;
COMPILE_TIME_ASSERT(M%2 == 1);
COMPILE_TIME_ASSERT(N%2 == 1);
DLIB_ASSERT((void*)&in_img != (void*)&out_img ,
"\tvoid binary_dilation()"
<< "\n\tYou must give two different image objects"
);
COMPILE_TIME_ASSERT(pixel_traits<typename in_image_type::type>::grayscale);
DLIB_ASSERT(is_binary_image(in_img) ,
"\tvoid binary_dilation()"
<< "\n\tin_img must be a binary image"
);
DLIB_ASSERT(is_binary_image(structuring_element) ,
"\tvoid binary_dilation()"
<< "\n\tthe structuring_element must be a binary image"
);
// if there isn't any input image then don't do anything
if (in_img.size() == 0)
{
out_img.clear();
return;
}
out_img.set_size(in_img.nr(),in_img.nc());
// apply the filter to the image
for (long r = 0; r < in_img.nr(); ++r)
{
for (long c = 0; c < in_img.nc(); ++c)
{
unsigned char out_pixel = off_pixel;
for (long m = 0; m < M && out_pixel == off_pixel; ++m)
{
for (long n = 0; n < N && out_pixel == off_pixel; ++n)
{
if (structuring_element[m][n] == on_pixel)
{
// if this pixel is inside the image then get it from the image
// but if it isn't just pretend it was an off_pixel value
if (r+m >= M/2 && c+n >= N/2 &&
r+m-M/2 < in_img.nr() && c+n-N/2 < in_img.nc())
{
out_pixel = in_img[r+m-M/2][c+n-N/2];
}
}
}
}
assign_pixel(out_img[r][c], out_pixel);
}
}
}
// ----------------------------------------------------------------------------------------
template <
typename in_image_type,
typename out_image_type,
long M,
long N
>
void binary_erosion (
const in_image_type& in_img,
out_image_type& out_img,
const unsigned char (&structuring_element)[M][N]
)
{
COMPILE_TIME_ASSERT( pixel_traits<typename in_image_type::type>::has_alpha == false );
COMPILE_TIME_ASSERT( pixel_traits<typename out_image_type::type>::has_alpha == false );
using namespace morphological_operations_helpers;
COMPILE_TIME_ASSERT(M%2 == 1);
COMPILE_TIME_ASSERT(N%2 == 1);
DLIB_ASSERT((void*)&in_img != (void*)&out_img ,
"\tvoid binary_erosion()"
<< "\n\tYou must give two different image objects"
);
COMPILE_TIME_ASSERT(pixel_traits<typename in_image_type::type>::grayscale);
DLIB_ASSERT(is_binary_image(in_img) ,
"\tvoid binary_erosion()"
<< "\n\tin_img must be a binary image"
);
DLIB_ASSERT(is_binary_image(structuring_element) ,
"\tvoid binary_erosion()"
<< "\n\tthe structuring_element must be a binary image"
);
// if there isn't any input image then don't do anything
if (in_img.size() == 0)
{
out_img.clear();
return;
}
out_img.set_size(in_img.nr(),in_img.nc());
// apply the filter to the image
for (long r = 0; r < in_img.nr(); ++r)
{
for (long c = 0; c < in_img.nc(); ++c)
{
unsigned char out_pixel = on_pixel;
for (long m = 0; m < M && out_pixel == on_pixel; ++m)
{
for (long n = 0; n < N && out_pixel == on_pixel; ++n)
{
if (structuring_element[m][n] == on_pixel)
{
// if this pixel is inside the image then get it from the image
// but if it isn't just pretend it was an off_pixel value
if (r+m >= M/2 && c+n >= N/2 &&
r+m-M/2 < in_img.nr() && c+n-N/2 < in_img.nc())
{
out_pixel = in_img[r+m-M/2][c+n-N/2];
}
else
{
out_pixel = off_pixel;
}
}
}
}
assign_pixel(out_img[r][c], out_pixel);
}
}
}
// ----------------------------------------------------------------------------------------
template <
typename in_image_type,
typename out_image_type,
long M,
long N
>
void binary_open (
const in_image_type& in_img,
out_image_type& out_img,
const unsigned char (&structuring_element)[M][N],
const unsigned long iter = 1
)
{
COMPILE_TIME_ASSERT( pixel_traits<typename in_image_type::type>::has_alpha == false );
COMPILE_TIME_ASSERT( pixel_traits<typename out_image_type::type>::has_alpha == false );
using namespace morphological_operations_helpers;
COMPILE_TIME_ASSERT(M%2 == 1);
COMPILE_TIME_ASSERT(N%2 == 1);
DLIB_ASSERT((void*)&in_img != (void*)&out_img ,
"\tvoid binary_open()"
<< "\n\tYou must give two different image objects"
);
COMPILE_TIME_ASSERT(pixel_traits<typename in_image_type::type>::grayscale);
DLIB_ASSERT(is_binary_image(in_img) ,
"\tvoid binary_open()"
<< "\n\tin_img must be a binary image"
);
DLIB_ASSERT(is_binary_image(structuring_element) ,
"\tvoid binary_open()"
<< "\n\tthe structuring_element must be a binary image"
);
// if there isn't any input image then don't do anything
if (in_img.size() == 0)
{
out_img.clear();
return;
}
out_img.set_size(in_img.nr(),in_img.nc());
if (iter == 0)
{
// just copy the image over
assign_image(out_img, in_img);
}
else if (iter == 1)
{
in_image_type temp;
binary_erosion(in_img,temp,structuring_element);
binary_dilation(temp,out_img,structuring_element);
}
else
{
in_image_type temp1, temp2;
binary_erosion(in_img,temp1,structuring_element);
// do the extra erosions
for (unsigned long i = 1; i < iter; ++i)
{
temp1.swap(temp2);
binary_erosion(temp2,temp1,structuring_element);
}
// do the extra dilations
for (unsigned long i = 1; i < iter; ++i)
{
temp1.swap(temp2);
binary_dilation(temp2,temp1,structuring_element);
}
binary_dilation(temp1,out_img,structuring_element);
}
}
// ----------------------------------------------------------------------------------------
template <
typename in_image_type,
typename out_image_type,
long M,
long N
>
void binary_close (
const in_image_type& in_img,
out_image_type& out_img,
const unsigned char (&structuring_element)[M][N],
const unsigned long iter = 1
)
{
COMPILE_TIME_ASSERT( pixel_traits<typename in_image_type::type>::has_alpha == false );
COMPILE_TIME_ASSERT( pixel_traits<typename out_image_type::type>::has_alpha == false );
using namespace morphological_operations_helpers;
COMPILE_TIME_ASSERT(M%2 == 1);
COMPILE_TIME_ASSERT(N%2 == 1);
DLIB_ASSERT((void*)&in_img != (void*)&out_img ,
"\tvoid binary_close()"
<< "\n\tYou must give two different image objects"
);
COMPILE_TIME_ASSERT(pixel_traits<typename in_image_type::type>::grayscale);
DLIB_ASSERT(is_binary_image(in_img) ,
"\tvoid binary_close()"
<< "\n\tin_img must be a binary image"
);
DLIB_ASSERT(is_binary_image(structuring_element) ,
"\tvoid binary_close()"
<< "\n\tthe structuring_element must be a binary image"
);
// if there isn't any input image then don't do anything
if (in_img.size() == 0)
{
out_img.clear();
return;
}
out_img.set_size(in_img.nr(),in_img.nc());
if (iter == 0)
{
// just copy the image over
assign_image(out_img, in_img);
}
else if (iter == 1)
{
in_image_type temp;
binary_dilation(in_img,temp,structuring_element);
binary_erosion(temp,out_img,structuring_element);
}
else
{
in_image_type temp1, temp2;
binary_dilation(in_img,temp1,structuring_element);
// do the extra dilations
for (unsigned long i = 1; i < iter; ++i)
{
temp1.swap(temp2);
binary_dilation(temp2,temp1,structuring_element);
}
// do the extra erosions
for (unsigned long i = 1; i < iter; ++i)
{
temp1.swap(temp2);
binary_erosion(temp2,temp1,structuring_element);
}
binary_erosion(temp1,out_img,structuring_element);
}
}
// ----------------------------------------------------------------------------------------
template <
typename in_image_type1,
typename in_image_type2,
typename out_image_type
>
void binary_intersection (
const in_image_type1& in_img1,
const in_image_type2& in_img2,
out_image_type& out_img
)
{
COMPILE_TIME_ASSERT( pixel_traits<typename in_image_type1::type>::has_alpha == false );
COMPILE_TIME_ASSERT( pixel_traits<typename in_image_type2::type>::has_alpha == false );
COMPILE_TIME_ASSERT( pixel_traits<typename out_image_type::type>::has_alpha == false );
using namespace morphological_operations_helpers;
COMPILE_TIME_ASSERT(pixel_traits<typename in_image_type1::type>::grayscale);
COMPILE_TIME_ASSERT(pixel_traits<typename in_image_type2::type>::grayscale);
DLIB_ASSERT(is_binary_image(in_img1) ,
"\tvoid binary_intersection()"
<< "\n\tin_img1 must be a binary image"
);
DLIB_ASSERT(is_binary_image(in_img2) ,
"\tvoid binary_intersection()"
<< "\n\tin_img2 must be a binary image"
);
DLIB_ASSERT(in_img1.nc() == in_img2.nc(),
"\tvoid binary_intersection()"
<< "\n\tin_img1 and in_img2 must have the same ncs."
<< "\n\tin_img1.nc(): " << in_img1.nc()
<< "\n\tin_img2.nc(): " << in_img2.nc()
);
DLIB_ASSERT(in_img1.nr() == in_img2.nr(),
"\tvoid binary_intersection()"
<< "\n\tin_img1 and in_img2 must have the same nrs."
<< "\n\tin_img1.nr(): " << in_img1.nr()
<< "\n\tin_img2.nr(): " << in_img2.nr()
);
// if there isn't any input image then don't do anything
if (in_img1.size() == 0)
{
out_img.clear();
return;
}
out_img.set_size(in_img1.nr(),in_img1.nc());
for (long r = 0; r < in_img1.nr(); ++r)
{
for (long c = 0; c < in_img1.nc(); ++c)
{
if (in_img1[r][c] == on_pixel && in_img2[r][c] == on_pixel)
assign_pixel(out_img[r][c], on_pixel);
else
assign_pixel(out_img[r][c], off_pixel);
}
}
}
// ----------------------------------------------------------------------------------------
template <
typename in_image_type1,
typename in_image_type2,
typename out_image_type
>
void binary_union (
const in_image_type1& in_img1,
const in_image_type2& in_img2,
out_image_type& out_img
)
{
COMPILE_TIME_ASSERT( pixel_traits<typename in_image_type1::type>::has_alpha == false );
COMPILE_TIME_ASSERT( pixel_traits<typename in_image_type2::type>::has_alpha == false );
COMPILE_TIME_ASSERT( pixel_traits<typename out_image_type::type>::has_alpha == false );
using namespace morphological_operations_helpers;
COMPILE_TIME_ASSERT(pixel_traits<typename in_image_type1::type>::grayscale);
COMPILE_TIME_ASSERT(pixel_traits<typename in_image_type2::type>::grayscale);
DLIB_ASSERT(is_binary_image(in_img1) ,
"\tvoid binary_intersection()"
<< "\n\tin_img1 must be a binary image"
);
DLIB_ASSERT(is_binary_image(in_img2) ,
"\tvoid binary_intersection()"
<< "\n\tin_img2 must be a binary image"
);
DLIB_ASSERT(in_img1.nc() == in_img2.nc(),
"\tvoid binary_intersection()"
<< "\n\tin_img1 and in_img2 must have the same ncs."
<< "\n\tin_img1.nc(): " << in_img1.nc()
<< "\n\tin_img2.nc(): " << in_img2.nc()
);
DLIB_ASSERT(in_img1.nr() == in_img2.nr(),
"\tvoid binary_intersection()"
<< "\n\tin_img1 and in_img2 must have the same nrs."
<< "\n\tin_img1.nr(): " << in_img1.nr()
<< "\n\tin_img2.nr(): " << in_img2.nr()
);
// if there isn't any input image then don't do anything
if (in_img1.size() == 0)
{
out_img.clear();
return;
}
out_img.set_size(in_img1.nr(),in_img1.nc());
for (long r = 0; r < in_img1.nr(); ++r)
{
for (long c = 0; c < in_img1.nc(); ++c)
{
if (in_img1[r][c] == on_pixel || in_img2[r][c] == on_pixel)
assign_pixel(out_img[r][c], on_pixel);
else
assign_pixel(out_img[r][c], off_pixel);
}
}
}
// ----------------------------------------------------------------------------------------
template <
typename in_image_type1,
typename in_image_type2,
typename out_image_type
>
void binary_difference (
const in_image_type1& in_img1,
const in_image_type2& in_img2,
out_image_type& out_img
)
{
COMPILE_TIME_ASSERT( pixel_traits<typename in_image_type1::type>::has_alpha == false );
COMPILE_TIME_ASSERT( pixel_traits<typename in_image_type2::type>::has_alpha == false );
COMPILE_TIME_ASSERT( pixel_traits<typename out_image_type::type>::has_alpha == false );
using namespace morphological_operations_helpers;
COMPILE_TIME_ASSERT(pixel_traits<typename in_image_type1::type>::grayscale);
COMPILE_TIME_ASSERT(pixel_traits<typename in_image_type2::type>::grayscale);
DLIB_ASSERT(is_binary_image(in_img1) ,
"\tvoid binary_difference()"
<< "\n\tin_img1 must be a binary image"
);
DLIB_ASSERT(is_binary_image(in_img2) ,
"\tvoid binary_difference()"
<< "\n\tin_img2 must be a binary image"
);
DLIB_ASSERT(in_img1.nc() == in_img2.nc(),
"\tvoid binary_difference()"
<< "\n\tin_img1 and in_img2 must have the same ncs."
<< "\n\tin_img1.nc(): " << in_img1.nc()
<< "\n\tin_img2.nc(): " << in_img2.nc()
);
DLIB_ASSERT(in_img1.nr() == in_img2.nr(),
"\tvoid binary_difference()"
<< "\n\tin_img1 and in_img2 must have the same nrs."
<< "\n\tin_img1.nr(): " << in_img1.nr()
<< "\n\tin_img2.nr(): " << in_img2.nr()
);
// if there isn't any input image then don't do anything
if (in_img1.size() == 0)
{
out_img.clear();
return;
}
out_img.set_size(in_img1.nr(),in_img1.nc());
for (long r = 0; r < in_img1.nr(); ++r)
{
for (long c = 0; c < in_img1.nc(); ++c)
{
if (in_img1[r][c] == on_pixel && in_img2[r][c] == off_pixel)
assign_pixel(out_img[r][c], on_pixel);
else
assign_pixel(out_img[r][c], off_pixel);
}
}
}
// ----------------------------------------------------------------------------------------
template <
typename in_image_type,
typename out_image_type
>
void binary_complement (
const in_image_type& in_img,
out_image_type& out_img
)
{
COMPILE_TIME_ASSERT( pixel_traits<typename in_image_type::type>::has_alpha == false );
COMPILE_TIME_ASSERT( pixel_traits<typename out_image_type::type>::has_alpha == false );
using namespace morphological_operations_helpers;
COMPILE_TIME_ASSERT(pixel_traits<typename in_image_type::type>::grayscale);
DLIB_ASSERT(is_binary_image(in_img) ,
"\tvoid binary_complement()"
<< "\n\tin_img must be a binary image"
);
// if there isn't any input image then don't do anything
if (in_img.size() == 0)
{
out_img.clear();
return;
}
out_img.set_size(in_img.nr(),in_img.nc());
for (long r = 0; r < in_img.nr(); ++r)
{
for (long c = 0; c < in_img.nc(); ++c)
{
if (in_img[r][c] == on_pixel)
assign_pixel(out_img[r][c], off_pixel);
else
assign_pixel(out_img[r][c], on_pixel);
}
}
}
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_MORPHOLOGICAL_OPERATIONs_