膨胀图与腐蚀图之差。
gradient(src, element) = dilate(src, element) - erode(src, element)
形态学梯度可以用来保留物体的边缘轮廓。
对二值图像进行这一操作可以将团块的边缘突出出来
可以使用减法函数(subtract())或者 morphologyEx函数进行形态学梯度操作。
减法函数:
void subtract( InputArray src1, InputArray src2, OutputArray dst, InputArray mask = noArray(), int dtype = -1);
参数1,InputArray类型,一般是cv::Mat,被减数;
参数2,InputArray类型,一般是cv::Mat,减数;
参数3,OutputArray类型,输出的目标图像,和原图像有一样的尺寸和类型;
参数4,掩码可选操作掩码;这是一个8位单通道数组,指定元素;要更改的输出数组;
参数5,数据类型输出阵列的可选深度;
dst = src1 - src2
morphologyEx函数:modules\imgproc\src\morph.cpp
void morphologyEx(InputArray src, OutputArray dst, int op, InputArray kernel, Point anchor = Point(-1,-1), int iterations=1, int borderType = BORDER_CONSTANT, const Scalar& borderValue = morphologyDefaultBorderValue() );
参数1,输入图像,填Mat类的对象。图像位深应该为:CV_8U, CV_16U,CV_16S, CV_32F 或CV_64F。
参数2,目标图像,需要和源图片有一样的尺寸和类型。
参数3,表示形态学运算的类型,可以是以下标识符:
MORPH_OPEN --- 开运算(Opening operation)
MORPH_CLOSE --- 闭运算(Closing operation)
MORPH_GRADIENT --- 形态学梯度(Morphological gradient)
MORPH_TOPHAT --- 顶帽(Top hat)
MORPH_BLACKHAT --- 黑帽(Black hat)
参数4,形态学运算的内核。若为NULL时,表示的是使用参考点位于中心3x3的核。
一般使用函数 getStructuringElement配合这个参数的使用。
getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)
参数5,锚的位置,默认值Point(-1,-1),表示锚位于中心。
参数6,迭代使用函数的次数,默认值为1。
参数7,用于推断图像外部像素的某种边界模式。默认值BORDER_ CONSTANT。
参数8,当边界为常数时的边界值,默认值morphologyDefaultBorderValue(),
使用时,可以看官方文档createMorphologyFilter()函数。
源码:
void morphologyEx( InputArray _src, OutputArray _dst, int op, InputArray _kernel, Point anchor, int iterations, int borderType, const Scalar& borderValue ) { CV_INSTRUMENT_REGION(); CV_Assert(!_src.empty()); Mat kernel = _kernel.getMat(); if (kernel.empty()) { kernel = getStructuringElement(MORPH_RECT, Size(3,3), Point(1,1)); } #ifdef HAVE_OPENCL Size ksize = kernel.size(); anchor = normalizeAnchor(anchor, ksize); CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2 && _src.channels() <= 4 && anchor.x == ksize.width >> 1 && anchor.y == ksize.height >> 1 && borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue(), ocl_morphologyEx(_src, _dst, op, kernel, anchor, iterations, borderType, borderValue)) #endif Mat src = _src.getMat(), temp; _dst.create(src.size(), src.type()); Mat dst = _dst.getMat(); #if !IPP_DISABLE_MORPH_ADV //CV_IPP_RUN_FAST(ipp_morphologyEx(op, src, dst, kernel, anchor, iterations, borderType, borderValue)); #endif 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 MORPH_CLOSE: dilate( src, dst, kernel, anchor, iterations, borderType, borderValue ); erode( dst, dst, kernel, anchor, iterations, borderType, borderValue ); break; case MORPH_GRADIENT: erode( src, temp, kernel, anchor, iterations, borderType, borderValue ); dilate( src, dst, kernel, anchor, iterations, borderType, borderValue ); dst -= temp; break; case MORPH_TOPHAT: 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 MORPH_BLACKHAT: 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: CV_Assert(src.type() == CV_8UC1); if(countNonZero(kernel) <=0) { src.copyTo(dst); break; } { Mat k1, k2, e1, e2; k1 = (kernel == 1); k2 = (kernel == -1); if (countNonZero(k1) <= 0) e1 = Mat(src.size(), src.type(), Scalar(255)); else erode(src, e1, k1, anchor, iterations, borderType, borderValue); if (countNonZero(k2) <= 0) e2 = Mat(src.size(), src.type(), Scalar(255)); else { Mat src_complement; bitwise_not(src, src_complement); erode(src_complement, e2, k2, anchor, iterations, borderType, borderValue); } dst = e1 & e2; } break; default: CV_Error( CV_StsBadArg, "unknown morphological operation" ); } } }
开运算是先腐蚀后膨胀的过程。dst = open(src, element) = dilate(erode(src, element));
可以用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。结果是放大了裂缝或者局部低亮度的区域。
闭运算是先膨胀后腐蚀的过程。dst = close(src, element) = erode(dilate(src, element));
闭运算能够排除小型黑洞(黑色区域)。
用来分离比邻近点亮一些的斑块。
原图像与“开运算“的结果图之差。dst = tophat(src, element) = src - open(src, element)
效果图突出了比原图轮廓周围的区域更明亮的区域。
应用场景:
当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。
“闭运算”的结果图与原图像之差。dst = blackhat(src, element) = close(src, element) - src
效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小相关。
黑帽运算用来分离比邻近点暗一些的斑块。