形态学滤波

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_  
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容

  • 数字形态学是图像处理的基本理论,这里简单介绍一下基本的形态学运算,针对的是灰度图像,包括:腐蚀与膨胀,开闭运算,形...
    MisakaMikotoSAM阅读 3,270评论 0 1
  • 形态学运算中腐蚀(erode),膨胀(dilate),开运算(open)和闭运算(close)。 1. 腐蚀是一种...
    Wangcy阅读 9,931评论 0 2
  • 一、形态学概述 我们图像处理中指的形态学,往往表示的是数学形态学。下面一起来了解数学形态学的概念。下面是来自百度百...
    一个三要不起阅读 4,310评论 0 7
  • 我常常反思李小龙的行为。还有施瓦辛格。李小龙花了大量时间联系功夫。借着功夫,发展了很多东西。爱国精神,赚钱,自我成...
    月下奔跑的驴阅读 172评论 0 0
  • 每个人内心都是爱交流的,并也愿意分享,可却在有些场合却不愿意沟通? 有的人会出现两面性的自己,与不同的人在一起,其...
    维一成长阅读 3,229评论 0 1