文章目录
1. 侵蚀 (白色区域减小)
侵蚀的基本思想就像土壤侵蚀一样,它侵蚀前景物体的边界(尽量使前景保持白色)。它是做什么的呢?内核滑动通过图像(在2D卷积中)。原始图像中的一个像素(无论是1还是0)只有当内核下的所有像素都是1时才被认为是1,否则它就会被侵蚀(变成0)。
结果是,根据内核的大小,边界附近的所有像素都会被丢弃。因此,前景物体的厚度或大小减小,或只是图像中的白色区域减小。它有助于去除小的白色噪声(正如我们在颜色空间章节中看到的),分离两个连接的对象等。
CV_EXPORTS_W void erode( InputArray src, OutputArray dst, InputArray kernel,
Point anchor = Point(-1,-1), int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue() );
在多通道图像的情况下,每个通道都是独立处理的。
@param src //输入图像; 通道数可以是任意的,但深度应为以下之一
CV_8U,CV_16U,CV_16S,CV_32F或CV_64F。
@param dst //输出与src大小和类型相同的图像。
@param kernel //用于腐蚀的结构元素; 如果`element = Mat()`,则为`3 x 3`矩形
使用结构元素。 可以使用#getStructuringElement创建内核。
@param anchor //元素内锚的位置; 默认值(-1,-1)表示锚点位于元素中心。
@param iterations //施加腐蚀的次数。
@param borderType //像素外推方法,请参见#BorderTypes
@param borderValue //边界不变时的边界值
demo
int main() {
cv::namedWindow("src",0);
cv::namedWindow("dst", 0);
cv::Mat src = cv::imread("./img/liangbaikai.jpg", 1);
cv::Mat dst;
//获取自定义核
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(10, 10));
cv::erode(src,dst,element);
cv::imshow("src", src);
cv::imshow("dst", dst);
cv::imwrite("./img/dst_liangbaikai.jpg", dst);
cv::waitKey(0);
}
效果
原图:
结果:
2. 扩张
它与侵蚀正好相反。如果内核下的至少一个像素为“ 1”,则像素元素为“ 1”。因此,它会增加图像中的白色区域或增加前景对象的大小。通常,在消除噪音的情况下,腐蚀后会膨胀。因为腐蚀会消除白噪声,但也会缩小物体。因此,我们对其进行了扩展。由于噪音消失了,它们不会回来,但是我们的目标区域增加了。在连接对象的损坏部分时也很有用。
CV_EXPORTS_W void dilate( InputArray src, OutputArray dst, InputArray kernel,
Point anchor = Point(-1,-1), int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue() );
在多通道图像的情况下,每个通道都是独立处理的。
@param src //输入图像; 通道数可以是任意的,但深度应为以下之一
CV_8U,CV_16U,CV_16S,CV_32F或CV_64F。
@param dst //输出与src大小和类型相同的图像。
@param kernel //用于扩展的结构元素; 如果elemenat = Mat(),则为3 x 3的矩形
使用结构元素。 可以使用#getStructuringElement创建内核
@param anchor //元素内锚的位置; 默认值(-1,-1)表示锚点位于元素中心。
@param iterations //进行扩张的次数。
@param borderType //像素外推方法,请参见#BorderTypes
@param borderValue //边界不变时的边界值
demo
int main() {
cv::namedWindow("src",0);
cv::namedWindow("dst", 0);
cv::Mat src = cv::imread("./img/liangbaikai.jpg", 1);
cv::Mat dst;
//获取自定义核
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
cv::dilate(src,dst,element);
cv::imshow("src", src);
cv::imshow("dst", dst);
cv::imwrite("./img/dst_liangbaikai.jpg", dst);
cv::waitKey(0);
}
效果
原图:
结果:
3. 形态学变化(组合)
CV_EXPORTS_W 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() );
在多通道图像的情况下,每个通道都是独立处理的。
@param src //源图像。通道数可以是任意的。深度应为以下之一
CV_8U,CV_16U,CV_16S,CV_32F或CV_64F。
@param dst //与源图像大小和类型相同的目标图像。
@param op //形态学操作的类型,请参见#MorphTypes
@param kernel //构造元素。可以使用#getStructuringElement创建它。
@param anchor //锚定在内核中的位置。负值表示锚点位于内核中心。
@param iterations //施加腐蚀和膨胀的次数。
@param borderType //像素外推方法,请参见#BorderTypes
@param borderValue //边界不变时的边界值。默认值有一个特殊的含义。
@note迭代次数是将应用腐蚀或膨胀操作的次数。
例如,具有两次迭代的开运算(#MORPH_OPEN)等效于应用
依次为:腐蚀->腐蚀->膨胀->膨胀(而不是腐蚀->膨胀->腐蚀->膨胀)。
3.1开运算 cv::MORPH_OPEN
开运算就是先侵蚀然后扩张,它对于消除噪音很有用
demo
int main() {
cv::namedWindow("src",0);
cv::namedWindow("dst", 0);
cv::Mat src = cv::imread("./img/lbk.jpg", 1);
cv::Mat dst;
//获取自定义核
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(10, 10));
cv::morphologyEx(src,dst, cv::MORPH_OPEN,element);
cv::imshow("src", src);
cv::imshow("dst", dst);
cv::imwrite("./img/dst_liangbaikai.jpg", dst);
cv::waitKey(0);
}
效果
原图:
结果:
3.2 闭运算 cv::MORPH_CLOSE
闭运算就是先扩张再侵蚀,在关闭前景对象内部的小孔或对象上的小黑点时很有用。
demo
int main() {
cv::namedWindow("src",0);
cv::namedWindow("dst", 0);
cv::Mat src = cv::imread("./img/lbk.jpg", 1);
cv::Mat dst;
//获取自定义核
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(10, 10));
cv::morphologyEx(src,dst, cv::MORPH_CLOSE,element);
cv::imshow("src", src);
cv::imshow("dst", dst);
cv::imwrite("./img/dst_liangbaikai.jpg", dst);
cv::waitKey(0);
}
效果
原图:
结果:
3.3 形态学梯度 cv::MORPH_GRADIENT
这是图像扩张和侵蚀之间的区别。结果将看起来像对象的轮廓。
demo
int main() {
cv::namedWindow("src",0);
cv::namedWindow("dst", 0);
cv::Mat src = cv::imread("./img/lbk.jpg", 1);
cv::Mat dst;
//获取自定义核
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(10, 10));
cv::morphologyEx(src,dst, cv::MORPH_GRADIENT,element);
cv::imshow("src", src);
cv::imshow("dst", dst);
cv::imwrite("./img/dst_liangbaikai.jpg", dst);
cv::waitKey(0);
}
效果
原图:
结果:
3.4 顶帽 cv::MORPH_TOPHAT
它是输入图像和图像开运算之差。
demo
int main() {
cv::namedWindow("src",0);
cv::namedWindow("dst", 0);
cv::Mat src = cv::imread("./img/lbk.jpg", 1);
cv::Mat dst;
//获取自定义核
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(10, 10));
cv::morphologyEx(src,dst, cv::MORPH_TOPHAT,element);
cv::imshow("src", src);
cv::imshow("dst", dst);
cv::imwrite("./img/dst_liangbaikai.jpg", dst);
cv::waitKey(0);
}
效果
原图:
结果:
3.5 黑帽 cv::MORPH_BLACKHAT
这是输入图像和图像闭运算之差。
demo
int main() {
cv::namedWindow("src",0);
cv::namedWindow("dst", 0);
cv::Mat src = cv::imread("./img/lbk.jpg", 1);
cv::Mat dst;
//获取自定义核
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(10, 10));
cv::morphologyEx(src,dst, cv::MORPH_BLACKHAT,element);
cv::imshow("src", src);
cv::imshow("dst", dst);
cv::imwrite("./img/dst_liangbaikai.jpg", dst);
cv::waitKey(0);
}
效果
原图:
结果:
4. 自定义内核的种类
CV_EXPORTS_W Mat getStructuringElement(int shape, Size ksize, Point anchor = Point(-1,-1));
enum MorphShapes {
MORPH_RECT = 0, //!< a rectangular structuring element: \f[E_{ij}=1\f]
MORPH_CROSS = 1, //!< a cross-shaped structuring element:
//!< \f[E_{ij} = \fork{1}{if i=\texttt{anchor.y} or j=\texttt{anchor.x}}{0}{otherwise}\f]
MORPH_ELLIPSE = 2 //!< an elliptic structuring element, that is, a filled ellipse inscribed
//!< into the rectangle Rect(0, 0, esize.width, 0.esize.height)
};
# 矩形内核
cv::getStructuringElement(cv::MORPH_RECT,(5,5))
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
# 椭圆内核
cv::getStructuringElement(cv::MORPH_ELLIPSE,(5,5))
0 0 1 0 0
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
0 0 1 0 0
# 十字内核
cv::getStructuringElement(cv::MORPH_CROSS,(5,5))
0 0 1 0 0
0 0 1 0 0
1 1 1 1 1
0 0 1 0 0
0 0 1 0 0