getStructuringElement
@brief Returns a structuring element of the specified size and shape for morphological operations.
The function constructs and returns the structuring element that can be further passed to cv::erode,
cv::dilate or cv::morphologyEx. But you can also construct an arbitrary binary mask yourself and use it as
the structuring element.
@param shape Element shape that could be one of cv::MorphShapes
@param ksize Size of the structuring element.
@param anchor Anchor position within the element. The default value \f$(-1, -1)\f$ means that the
anchor is at the center. Note that only the shape of a cross-shaped element depends on the anchor
position. In other cases the anchor just regulates how much the result of the morphological
operation is shifted.
cv::Mat cv::getStructuringElement(int shape, Size ksize, Point anchor)
{
int i, j;
int r = 0, c = 0;
double inv_r2 = 0;
CV_Assert( shape == MORPH_RECT || shape == MORPH_CROSS || shape == MORPH_ELLIPSE );
anchor = normalizeAnchor(anchor, ksize);
if( ksize == Size(1,1) )
shape = MORPH_RECT;
if( shape == MORPH_ELLIPSE )
{
r = ksize.height/2;
c = ksize.width/2;
inv_r2 = r ? 1./((double)r*r) : 0;
}
Mat elem(ksize, CV_8U);
for( i = 0; i < ksize.height; i++ )
{
uchar* ptr = elem.ptr(i);
int j1 = 0, j2 = 0;
if( shape == MORPH_RECT || (shape == MORPH_CROSS && i == anchor.y) )
j2 = ksize.width;
else if( shape == MORPH_CROSS )
j1 = anchor.x, j2 = j1 + 1;
else
{
int dy = i - r;
if( std::abs(dy) <= r )
{
int dx = saturate_cast<int>(c*std::sqrt((r*r - dy*dy)*inv_r2));
j1 = std::max( c - dx, 0 );
j2 = std::min( c + dx + 1, ksize.width );
}
}
for( j = 0; j < j1; j++ )
ptr[j] = 0;
for( ; j < j2; j++ )
ptr[j] = 1;
for( ; j < ksize.width; j++ )
ptr[j] = 0;
}
return elem;
}
erode
#ifndef FBC_CV_ERODE_HPP_
#define FBC_CV_ERODE_HPP_
/* reference: include/opencv2/imgproc.hpp
modules/imgproc/src/morph.cpp
*/
#include <typeinfo>
#include "core/mat.hpp"
#include "imgproc.hpp"
#include "filterengine.hpp"
#include "core/core.hpp"
#include "morph.hpp"
namespace fbc {
// Erodes an image by using a specific structuring element
// \f[\texttt{ dst } (x, y) = \min _{ (x',y') : \, \texttt{ element } (x',y') \ne0 } \texttt{ src } (x + x',y+y')\f]
// In case of multi - channel images, each channel is processed independently.
// Erosion can be applied several ( iterations ) times.
// support type: uchar/float, multi-channels
template<typename _Tp, int chs>
int erode(const Mat_<_Tp, chs>& src, Mat_<_Tp, chs>& dst, Mat_<uchar, 1>& kernel,
Point anchor = Point(-1, -1), int iterations = 1, int borderType = BORDER_CONSTANT, const Scalar& borderValue = Scalar::all(DBL_MAX))
{
FBC_Assert(typeid(uchar).name() == typeid(_Tp).name() || typeid(float).name() == typeid(_Tp).name()); // uchar || float
if (dst.empty()) {
dst = Mat_<_Tp, chs>(src.rows, src.cols);
} else {
FBC_Assert(src.rows == dst.rows && src.cols == dst.cols);
}
Size ksize = !kernel.empty() ? kernel.size() : Size(3, 3);
anchor = normalizeAnchor(anchor, ksize);
if (iterations == 0 || kernel.rows * kernel.cols == 1) {
src.copyTo(dst);
return 0;
}
if (kernel.empty()) {
kernel = Mat_<uchar, 1>(1 + iterations * 2, 1 + iterations * 2);
getStructuringElement(kernel, MORPH_RECT, Size(1 + iterations * 2, 1 + iterations * 2));
anchor = Point(iterations, iterations);
iterations = 1;
} else if (iterations > 1 && countNonZero(kernel) == kernel.rows * kernel.cols) {
anchor = Point(anchor.x*iterations, anchor.y*iterations);
kernel = Mat_<uchar, 1>(ksize.height + (iterations - 1)*(ksize.height - 1), ksize.width + (iterations - 1)*(ksize.width - 1));
getStructuringElement(kernel, MORPH_RECT,
Size(ksize.width + (iterations - 1)*(ksize.width - 1), ksize.height + (iterations - 1)*(ksize.height - 1)), anchor);
iterations = 1;
}
anchor = normalizeAnchor(anchor, kernel.size());
Ptr<BaseRowFilter> rowFilter;
Ptr<BaseColumnFilter> columnFilter;
Ptr<BaseFilter> filter2D;
if (countNonZero(kernel) == kernel.rows*kernel.cols) {
// rectangular structuring element
rowFilter = getMorphologyRowFilter<_Tp, chs>(0, kernel.cols, anchor.x);
columnFilter = getMorphologyColumnFilter<_Tp, chs>(0, kernel.rows, anchor.y);
} else {
filter2D = getMorphologyFilter<_Tp, chs>(0, kernel, anchor);
}
Scalar borderValue_ = borderValue;
if (borderType == BORDER_CONSTANT && borderValue_ == Scalar::all(DBL_MAX)) {
if (sizeof(_Tp) == 1) // CV_8U
borderValue_ = Scalar::all((double)UCHAR_MAX);
else // CV_32F
borderValue_ = Scalar::all((double)FLT_MAX);
}
Ptr<FilterEngine<_Tp, _Tp, _Tp, chs, chs, chs>> f = makePtr<FilterEngine<_Tp, _Tp, _Tp, chs, chs, chs>>(filter2D, rowFilter, columnFilter, borderType, borderType, borderValue_);
f->apply(src, dst);
for (int i = 1; i < iterations; i++)
f->apply(dst, dst);
return 0;
}
} // namespace fbc
#endif // FBC_CV_ERODE_HPP_
dilate
#ifndef FBC_CV_DILATE_HPP_
#define FBC_CV_DILATE_HPP_
/* reference: include/opencv2/imgproc.hpp
modules/imgproc/src/morph.cpp
*/
#include <typeinfo>
#include "core/mat.hpp"
#include "imgproc.hpp"
#include "filterengine.hpp"
#include "core/core.hpp"
#include "morph.hpp"
namespace fbc {
// Dilates an image by using a specific structuring element
// \f[\texttt{dst} (x,y) = \max _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')\f]
// In case of multi - channel images, each channel is processed independently.
// support type: uchar/float, multi-channels
template<typename _Tp, int chs>
int dilate(const Mat_<_Tp, chs>& src, Mat_<_Tp, chs>& dst, Mat_<uchar, 1>& kernel,
Point anchor = Point(-1, -1), int iterations = 1, int borderType = BORDER_CONSTANT, const Scalar& borderValue = Scalar::all(DBL_MAX))
{
FBC_Assert(typeid(uchar).name() == typeid(_Tp).name() || typeid(float).name() == typeid(_Tp).name()); // uchar || float
if (dst.empty()) {
dst = Mat_<_Tp, chs>(src.rows, src.cols);
} else {
FBC_Assert(src.rows == dst.rows && src.cols == dst.cols);
}
Size ksize = !kernel.empty() ? kernel.size() : Size(3, 3);
anchor = normalizeAnchor(anchor, ksize);
if (iterations == 0 || kernel.rows * kernel.cols == 1) {
src.copyTo(dst);
return 0;
}
if (kernel.empty()) {
kernel = Mat_<uchar, 1>(1 + iterations * 2, 1 + iterations * 2);
getStructuringElement(kernel, MORPH_RECT, Size(1 + iterations * 2, 1 + iterations * 2));
anchor = Point(iterations, iterations);
iterations = 1;
} else if (iterations > 1 && countNonZero(kernel) == kernel.rows * kernel.cols) {
anchor = Point(anchor.x*iterations, anchor.y*iterations);
kernel = Mat_<uchar, 1>(ksize.height + (iterations - 1)*(ksize.height - 1), ksize.width + (iterations - 1)*(ksize.width - 1));
getStructuringElement(kernel, MORPH_RECT,
Size(ksize.width + (iterations - 1)*(ksize.width - 1), ksize.height + (iterations - 1)*(ksize.height - 1)), anchor);
iterations = 1;
}
anchor = normalizeAnchor(anchor, kernel.size());
Ptr<BaseRowFilter> rowFilter;
Ptr<BaseColumnFilter> columnFilter;
Ptr<BaseFilter> filter2D;
if (countNonZero(kernel) == kernel.rows*kernel.cols) {
// rectangular structuring element
rowFilter = getMorphologyRowFilter<_Tp, chs>(1, kernel.cols, anchor.x);
columnFilter = getMorphologyColumnFilter<_Tp, chs>(1, kernel.rows, anchor.y);
} else {
filter2D = getMorphologyFilter<_Tp, chs>(1, kernel, anchor);
}
Scalar borderValue_ = borderValue;
if (borderType == BORDER_CONSTANT && borderValue_ == Scalar::all(DBL_MAX)) {
if (sizeof(_Tp) == 1) // CV_8U
borderValue_ = Scalar::all(0.);
else // CV_32F
borderValue_ = Scalar::all(-FLT_MAX);
}
Ptr<FilterEngine<_Tp, _Tp, _Tp, chs, chs, chs>> f = makePtr<FilterEngine<_Tp, _Tp, _Tp, chs, chs, chs>>(filter2D, rowFilter, columnFilter, borderType, borderType, borderValue_);
f->apply(src, dst);
for (int i = 1; i < iterations; i++)
f->apply(dst, dst);
return 0;
}
} // namespace fbc
#endif // FBC_CV_DILATE_HPP_
morphologyEx
#ifndef FBC_CV_MORPHOLOGYEX_HPP_
#define FBC_CV_MORPHOLOGYEX_HPP_
/* reference: include/opencv2/imgproc.hpp
modules/imgproc/src/morph.cpp
*/
#include <typeinfo>
#include "erode.hpp"
#include "dilate.hpp"
namespace fbc {
// perform advanced morphological transformations using an erosion and dilation as basic operations
// In case of multi - channel images, each channel is processed independently.
// morphologyEx can be applied several ( iterations ) times.
// op ==> enum MorphTypes
// support type: uchar/float, multi-channels
template<typename _Tp, int chs>
int morphologyEx(const Mat_<_Tp, chs>& src, Mat_<_Tp, chs>& dst, int op, const Mat_<uchar, 1>& kernel,
Point anchor = Point(-1, -1), int iterations = 1, int borderType = BORDER_CONSTANT, const Scalar& borderValue = Scalar::all(DBL_MAX))
{
FBC_Assert(typeid(uchar).name() == typeid(_Tp).name() || typeid(float).name() == typeid(_Tp).name()); // uchar || float
if (dst.empty()) {
dst = Mat_<_Tp, chs>(src.rows, src.cols);
} else {
FBC_Assert(src.rows == dst.rows && src.cols == dst.cols);
}
Mat_<uchar, 1> kernel_ = kernel;
if (kernel_.empty()) {
kernel_ = Mat_<uchar, 1>(3, 3);
getStructuringElement(kernel_, MORPH_RECT, Size(3, 3), Point(1, 1));
}
switch (op) {
case MORPH_ERODE: {
erode(src, dst, kernel_, anchor, iterations, borderType, borderValue);
break;
}
case MORPH_DILATE: {
dilate(src, dst, kernel_, anchor, iterations, borderType, borderValue);
break;
}
case MORPH_OPEN: {
erode(src, dst, kernel_, anchor, iterations, borderType, borderValue);
dilate(dst, dst, kernel_, anchor, iterations, borderType, borderValue);
break;
}
case CV_MOP_CLOSE: {
dilate(src, dst, kernel_, anchor, iterations, borderType, borderValue);
erode(dst, dst, kernel_, anchor, iterations, borderType, borderValue);
break;
}
case CV_MOP_GRADIENT: {
Mat_<_Tp, chs> temp(src.rows, src.cols);
erode(src, temp, kernel_, anchor, iterations, borderType, borderValue);
dilate(src, dst, kernel_, anchor, iterations, borderType, borderValue);
dst -= temp;
break;
}
case CV_MOP_TOPHAT: {
Mat_<_Tp, chs> temp(src.rows, src.cols);
if (src.data != dst.data)
temp = dst;
erode(src, temp, kernel_, anchor, iterations, borderType, borderValue);
dilate(temp, temp, kernel_, anchor, iterations, borderType, borderValue);
dst = src - temp;
break;
}
case CV_MOP_BLACKHAT: {
Mat_<_Tp, chs> temp(src.rows, src.cols);
if (src.data != dst.data)
temp = dst;
dilate(src, temp, kernel_, anchor, iterations, borderType, borderValue);
erode(temp, temp, kernel_, anchor, iterations, borderType, borderValue);
dst = temp - src;
break;
}
case MORPH_HITMISS: {
FBC_Assert(typeid(uchar).name() == typeid(_Tp).name() && chs == 1);
Mat_<uchar, 1> k1 = (kernel_ == Mat_<uchar, 1>(kernel_.rows, kernel_.cols, Scalar::all(1)));
Mat_<uchar, 1> k2 = (kernel_ == Mat_<int, 1>(kernel_.rows, kernel_.cols, Scalar::all(-1)));
Mat_<_Tp, chs> e1, e2;
if (countNonZero(k1) <= 0)
e1 = src;
else
erode(src, e1, k1, anchor, iterations, borderType, borderValue);
if (countNonZero(k2) <= 0) {
e2 = src;
} else {
Mat_<_Tp, chs> src_complement;
bitwise_not(src, src_complement);
erode(src_complement, e2, k2, anchor, iterations, borderType, borderValue);
}
bitwise_and(e1, e2, dst);
break;
}
default:
FBC_Assert("unknown morphological operation");
}
return 0;
}
} // namespace fbc
#endif // FBC_CV_MORPHOLOGYEX_HPP_
网友评论