OpenCV提供了一种高效且易用的图像形态学变换接口。基本上,所有的形态学操作都基于两种原始操作,接下来的讲述也将以这两点开始、循序渐进发展到更加复杂的操作,每个更加复杂的操作都将通过前面的方法来表示。
1. 膨胀和腐蚀
膨胀和腐蚀是最基础的形态学变换,可以应用到许多方面,比如消除噪声、元素分割和连接等。
膨胀是一种卷积操作,将目标像素的值替换为卷积核覆盖区域的局部最大值。这是一个非线性核的例子。通常膨胀采用的核是一个四边形或圆形的实心核,其锚点在中心。膨胀的作用是使图中填充区域。
与膨胀对应的,腐蚀是与之相反的操作,腐蚀操作计算的是核覆盖范围内的局部最小值。
膨胀和腐蚀的效果可以参考如下:
总的来说,膨胀扩张了明亮区域,腐蚀缩减了明亮区域。
膨胀填充凹面,腐蚀消除突起。
OpenCV提供函数cv::erode()实现腐蚀,提供函数cv::dilate()实现膨胀。
函数原型:
void cv::erode(
cv::InputArray src,
cv::OutputArray dst,
cv::InputArray element,
cv::Point anchor = cv::Point(-1, -1),
int iterations = 1,
int borderType = cv::BORDER_CONSTANT,
const cv::Scalar& borderValue = cv::morphologyDefaultBorderValue()
);
void cv::dilate(
cv::InputArray src,
cv::OutputArray dst,
cv::InputArray element,
cv::Point anchor = cv::Point(-1,-1),
int iterations = 1,
int borderType = cv::BORDER_CONSTANT,
const cv::Scalar& borderValue = cv::morphologyDefaultBorderValue()
);
cv::erode()和cv::dilate()都需要传入源图像和目标图像,并且都支持就地调用(即源图像和目标图像是同一幅图像)。
参数element是核,可以向它传递一个未被初始化的cv::Mat(),但会导致函数使用默认的锚点在中心的3 x 3核。
参数iterations是迭代次数,如果不为1,会自动重复多次调用这个函数。
参数borderType是边框类型,borderType = cv::BORDER_CONST时,边缘像素会填入borderValue。
腐蚀操作通常用于消除图中斑点一样的噪声,原理是斑点经过腐蚀后会消失,而大的可见区域不会收到影响。
膨胀操作通常用于发现连通分支(大的颜色或强度相似像素的离散区域)。膨胀出现的原因,在许多情况下,由于噪声或者阴影等因素,大的区域可能被分解成多个分支,一些细微的膨胀会导致这些分支相遇并组合成一个整体。
cv::erode()函数运行时,函数内部发生的是将点p处的值设置成核与点p对齐后覆盖区域的最小值,而cv::dilate()是将最小值替换成最大值。
如下是腐蚀和膨胀的示例:
2. 通用形态学函数
当处理的对象是二值图像和像素只可能是开(>0)或关(=0)的图像掩膜时,基本的腐蚀和膨胀操作就够用了,需要对灰度图或彩色图进行处理时,一些其它的操作就非常有用,这些操作可以通过函数cv::morphologyEx()实现,函数原型:
void cv::morphologyEx(
cv::InputArray src, // input image
cv::OutputArray dst, // result image
int op, // operator(eg. cv::MOP_OPEN)
cv::InputArray element, // structing element,cv::Mat()
cv::Point anchor = cv::Point(-1,-1), // location of anchor point
int iterations = 1, // number of times to apply
int borderType = cv::BORDER_DEFAULT, // border extrapolation
const cv::Scalar& borderValue = cv::morphologyDefaultBorderValue() //
);
参数op用于指定函数将进行的操作,可选的值如下:
操作值 | 形态学操作 | 是否需要临时图像 |
cv::MOP_OPEN | 开操作 | 否 |
cv::MOP_CLOSE | 闭操作 | 否 |
cv::MOP_GRADIENT | 形态学梯度 | 总是需要 |
cv::MOP_TOPHAT | 顶帽操作 | 就地调用需要(src=dst) |
cv::MOP_BLACKHAT | 黑帽操作 | 就地调用需要(src=dst) |
2.1 开操作和闭操作
开操作和闭操作实际上是腐蚀和膨胀操作的组合。
- 开操作先将图像腐蚀,然后对腐蚀结果膨胀。常用于对二值图像中的区域进行计数。例如,对显微镜载玻片上的细胞的图像做了阈值化处理,在对区域进行计数之前,需要用开操作来将相互靠的很近的细胞分开。
- 闭操作先将图像进行膨胀,然后对膨胀的结果进行腐蚀。闭操作用于复杂连通分支算法中减少无用或噪声驱动的片段。对于连通分支,通常先进性腐蚀或闭操作消除噪声,然后通过开操作连接相互靠近的大型区域。
对一幅非布尔类型的图像进行形态学操作时,闭操作最明显的效果是消除值小于邻域内的点的孤立异常值,开操作是消除大于邻域内的孤立异常值。
开操作和闭操作的iterations参数,两次的闭操作的迭代不是膨胀-腐蚀-膨胀-腐蚀,而是膨胀-膨胀-腐蚀-腐蚀,这样做不但可以消除孤立的异常值,也能消除邻域内的异常值。
开操作和闭操作对图像的结果如下:
2.2 形态学梯度
形态学梯度可以用如下表达式描述:
gradient(src) = dilate(src) – erode(src)
对于灰度图像,其结果就是计算明暗变换的趋势。形态学梯度通常用于显示明亮区域的边界,然后可以将它们看作目标或者目标的部分。用扩张的图像减去收缩的图像,这样就找出了完整的边界。
形态学梯度操作后的效果如下:
操作后的效果类似边缘检测???
2.3 顶帽和黑帽
顶帽和黑帽操作分别用于显示与其邻域相比更亮或更暗的部分。
当试图根据物体的亮度变化分离依附于物体的某些部分时,就会用到这些方法,在生物体或细胞的显微镜图像上经常会用到这些方法。这两种操作都是根据如下基础操作定义的:
TopHat(src) = src – open(src) // Isolate brighter
BlackHat(src) = close(src) – src // Isolate dimmer
顶帽操作用源图像减去对其开操作后的图像,开操作的效果是放大裂缝和局部小洞,用源图像减去对其开操作后的图像得到了比源图像更亮的环绕部分。
顶帽操作显示的是比源图像更亮的环绕部分。
黑帽操作显示的是比源图像更暗的环绕部分。
如下分别是顶帽操作和黑帽操作后的结果:
3. 自定义核
OpenCv可以创建自己的核,在形态学上,核常称为构造元素。供开发者创建自定义形态学核的函数叫cv::getStructuringElement()。
一般常用的是非矩形核,是一种非常规形状,函数cv::getStructuringElement()原型如下:
cv::Mat cv::getStructuringElement(
int shape, // element shape,eg,cv::MORPH_RECT
cv::Size ksize, // sizeof structuring element (odd num!)
cv::Point anchor = cv::Point(-1,-1) // location of anchor point
);
参数shape控制构造元素的基本形状;
参数ksize和anchor确定元素的大小和锚点位置。通常,anchor默认位cv::Point(-1,-1),,表示cv::getStructuringElement()默认锚点在元素中心。
参数shape的形状如下:
形状值 | 元素 | 描述 |
cv::MORPH_RECT | 矩形 | E[i][j] = 1 |
cv::MORPH_ELLIPSE | 椭圆形 | 以ksize.width和ksize.height为两个半径做椭圆 |
cv::MORPH_CROSS | 交叉 | E[i][j] =1, 当i= anchor.y 或 j = anchor.x |
cv::MORPH_CROSS是为旧版接口留下的。
文章出处登录后可见!