OpenCV数字图像处理基于C++:图像形态学处理
1、图像腐蚀
原理:腐蚀用来收缩或细化二值图像中的前景,借此实现去噪声、元素分割等功能。和所有形态学滤波器一样,腐蚀和膨胀这两个滤波器的作用范围是由结构元素定义的像素集。在某个像素上应用结构元素时,结构元素的锚点与该像素对齐,所有与结构元素相交的像素就包含在当前集合中。腐蚀就是把当前像素替换成所定义像素集合中的最小像素值;膨胀是腐蚀的反运算,它把当前像素替换成所定义像素集合中的最大像素值。由于输入的二值图像只包含黑色(值为0)和白色(值为255)像素,因此每个像素都会被替换成白色或黑色像素。
要形象地理解这两种运算的作用,可考虑背景(黑色)和前景(白色)的物体。腐蚀时,如果结构元素放到某个像素位置时碰到了背景(即交集中有一个像素是黑色的),那么这个像素就变为背景;膨胀时,如果结构元素放到某个背景像素位置时碰到了前景物体,那么这个像素就被标为白色。正因如此,图像腐蚀后物体尺寸会缩小(形状被腐蚀),而图像膨胀后物体会扩大。在腐蚀图像中,有些面积较小的物体(可看作背景中的“噪声”像素)会彻底消失。与之类似,膨胀后的物体会变大.而物体中一些“空隙”会被填满。OpenCV默认使用3×3正方形结构元素。在调用函数时,就能得到默认的结构元素。你也可以通过提供一个矩阵来指定结构元素的大小(以及形状),矩阵中的非零元素将构成结构元素。
作用:去除图像中的某些部分以及会缩小细化目标
A 被 B 腐 蚀 即 为 A ⊖ B , 定 义 为 A ⊖ B = { z ∣ ( B ^ ) z ∩ A c ≠ ∅ } A被B腐蚀即为A\ominus B,定义为 A \ominus B=\lbrace z|(\hat{B})_z \cap A^c \neq \emptyset \rbrace A被B腐蚀即为A⊖B,定义为A⊖B={
z∣(B^)z∩Ac=∅}
1.1 CV腐蚀函数
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()
);
src 输入图像;通道的数量可以是任意的,但是深度值应该是以下之一:
CV_8U, CV_16U, CV_16S, CV_32F or CV_64F.
dst 和源图像同样大小和类型的输出图像。
kernel 用于腐蚀的结构元素;如果element=Mat(),是一个3 x 3的矩形结构元素. Kernel 可以通过使用getStructuringElement来创建。
anchor 素中的锚点的位置,默认是值(-1,-1),也就是说锚点在元素的中心位置。
iterations 腐蚀的迭代次数。
borderType像素外推方法。参见#BorderTypes, BORDER_WRAP不支持。
borderValue 固定边缘的情况下的边缘值。
参考 dilate,morphologyEx,getStructuringElement
Mat cv::getStructuringElement(
int shape,
Size ksize,
Point anchor = Point(-1,-1)
)
参数1:结构元的形状(0:矩形结构元;1:十字架结构元;2:椭圆结构元);
参数2:结构元大小(size(m, n));
参数3:结构元中心点所在位置(point(x, y))默认为中心点
Mat element = getStructuringElement(0, Size(3, 3)); //构造矩形结构元素
Mat image_erosion;
erode(image_bw, image_erosion, element, Point(-1, -1), 3); //执行腐蚀操作
imshow("image_erosion", image_erosion);
原图
二值图
CV腐蚀
1.2 自定义腐蚀函数
void erode_my(Mat &src,Mat &dst,int size) {
for (int i = 0; i < src.rows; ++i)
{
for (int j = 0; j < src.cols; ++j)
{
uchar minV = 255;
//uchar maxV = 0;
//遍历周围最小像素值
for (int yi = i - size / 2; yi <= i + size / 2; yi++)
{
for (int xi = j - size / 2; xi <= j + size / 2; xi++)
{
if (xi < 0 || xi >= src.cols || yi < 0 || yi >= src.rows)
{
continue;
}
minV = (std::min<uchar>)(minV, src.at<uchar>(yi, xi));
//maxV = (std::max<uchar>)(maxV, src.at<uchar>(yi, xi));
}
}
dst.at<uchar>(i, j) = minV;
}
}
}
自定义腐蚀
1.3 对比
erode_my(image_bw, image_erosion_my,9);
当腐蚀结构扩大到9×9时效果基本与cv自带函数一致。
2、图像膨胀
原理:腐蚀用来收缩或细化二值图像中的前景,借此实现去噪声、元素分割等功能。腐蚀过程:用结构元来逐个像素扫描被腐蚀图像,并根据结构元和被腐蚀图像的关系确定腐蚀结果。
注意,腐蚀操作等形态学操作,是逐个像素地来确定值的,每次判定的点都与结构元中心点所对应,如果结构元完全处于前景图像中,就将结构元中心点所对应的腐蚀结果图像中的像素点处理为前景色(如1)。如果结构元不完全处于前景图像中,就将结构元中心点所对应的腐蚀结果图像中的像素点处理为背景色(如0)。(结构元也被称为核)
作用:增大图像中的目标,或者填充、连接某些目标。
2.1 CV膨胀函数
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()
);
src 输入图像;通道的数量可以是任意的,但是深度值应该是以下之一:CV_8U, CV_16U, CV_16S, CV_32F or CV_64F.
dst 和源图像同样大小和类型的输出图像。
kernel 膨胀核元素,如果elemenat=Mat(), 是一个3 x 3的矩形核元素,核可以使用getStructuringElement来创建。
anchor 元素中的锚点的位置,默认是值(-1,-1),也就是说锚点在元素的中心位置。
iterations 膨胀的迭代次数。
borderType 像素外推方法。参见#BorderTypes, BORDER_WRAP不支持。
borderValue 固定边缘的情况下的边缘值。
参考:erode,morphologyEx,getStructuringElement
//膨胀
Mat element = getStructuringElement(0, Size(3, 3)); //构造矩形结构元素
Mat image_dilate;
dilate(image_bw, image_dilate, element, Point(-1, -1), 9); //执行膨胀操作
imshow("image_dilate", image_dilate);
二值图
CV膨胀
2.2 自定义膨胀函数
void dilate_my(Mat& src, Mat& dst, int size) {
for (int i = 0; i < src.rows; ++i)
{
for (int j = 0; j < src.cols; ++j)
{
//uchar minV = 255;
uchar maxV = 0;
//遍历周围最大像素值
for (int yi = i - size / 2; yi <= i + size / 2; yi++)
{
for (int xi = j - size / 2; xi <= j + size / 2; xi++)
{
if (xi < 0 || xi >= src.cols || yi < 0 || yi >= src.rows)
{
continue;
}
//minV = (std::min<uchar>)(minV, src.at<uchar>(yi, xi));
maxV = (std::max<uchar>)(maxV, src.at<uchar>(yi, xi));
}
}
dst.at<uchar>(i, j) = maxV;
}
}
}
自定义膨胀
2.3 对比
dilate_my(image_bw, image_dilate_my, 9);
当膨胀结构扩大到9×9时效果基本与cv自带函数一致。
腐蚀图像相当于对其反色图像膨胀后再取反色;
膨胀图像相当于对其反色图像腐蚀后再取反色。
3、开运算
原理:先腐蚀,再膨胀。
作用:平滑物体轮廓、断开狭窄的狭颈、消除细长的突出和物体。可以去掉图像上一些小的对象。(假设图像前景色是白色,背景色是黑色)
原图
3.1 方法一
//先腐蚀后膨胀
Mat open_image;
Mat element = getStructuringElement(0, Size(3, 3)); //构造矩形结构元素
erode(image_bw, image_erosion, element, Point(-1, -1), 3); //执行腐蚀操作
dilate(image_erosion, open_image, element, Point(-1, -1), 3); //执行膨胀操作
imshow("open", open_image);
3.2 方法二
morphologyEx(image_bw, open_image, MORPH_OPEN, element);
imshow("open2", open_image);
4、闭运算
原理:先膨胀,再腐蚀。
作用:闭运算可以填充小洞,填充小的噪点。
4.1 方法一
Mat close_image;
Mat element = getStructuringElement(0, Size(3, 3)); //构造矩形结构元素
dilate(image_bw, image_dilate, element, Point(-1, -1), 3); //执行膨胀操作
erode(image_dilate, close_image, element, Point(-1, -1), 3); //执行腐蚀操作
imshow("close", close_image);
4.2 方法二
morphologyEx(image_bw, close_image, MORPH_CLOSE, element);
imshow("close2", close_image);
4.3 morphologyEx函数介绍
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)参数1:InputArray src
输入图像,图像数据类型必须为CV_8U, CV_16U, CV_16S, CV_32F or CV_64F中的一种。
(2)参数2:OutputArray dst
输出图像,数据类型与大小和输入图像一样。
(3)参数3:int op 形态学处理的类型:
MORPH_ERODE = 0:腐蚀处理
MORPH_DILATE = 1:膨胀处理
MORPH_OPEN = 2:开运算处理
MORPH_CLOSE = 3:闭运算处理
MORPH_GRADIENT = 4:形态学梯度
MORPH_TOPHAT = 5:顶帽变换
MORPH_BLACKHAT = 6:黑帽变换
MORPH_HITMISS = 7 :击中-击不中变换
(4)参数4:InputArray kernel结构元矩阵
(5)参数5:Point anchor = Point(-1,-1)
结构元中心点, 默认值Point(-1,-1), 表示正中心
(6)参数6:int iterations = 1
腐蚀膨胀处理的次数,默认值为1;
如果是开运算闭运算,次数表示先腐蚀或者膨胀几次,再膨胀腐蚀几次,而不是开运算闭运算几次:
如开运算且次数为2:erode -> erode -> dilate -> dilate
(7)参数7:int borderType = BORDER_CONSTANT
图像边框插值类型,默认类型为固定值填充
BORDER_CONSTANT = 0:固定值 i 填充:iiiiii | abcdefgh | iiiiiii
BORDER_REPLICATE = 1:两端复制:aaaaaa | abcdefgh | hhhhhhh
BORDER_REFLECT = 2:镜像复制:fedcba | abcdefgh | hgfedcb
BORDER_WRAP = 3:去除一端的值然后复制: cdefgh | abcdefgh | abcdefg
BORDER_REFLECT_101 = 4:去除一端的值然后镜像复制: gfedcb|abcdefgh|gfedcba
BORDER_TRANSPARENT = 5:推导赋值 uvwxyz | abcdefgh | ijklmno
BORDER_REFLECT101 = BORDER_REFLECT_101: same as BORDER_REFLECT_101
BORDER_DEFAULT = BORDER_REFLECT_101: same as BORDER_REFLECT_101
BORDER_ISOLATED = 16:< do not look outside of ROI
(8)参数8:const Scalar& borderValue = morphologyDefaultBorderValue()
文档中说明:param borderValue Border value in case of a constant border. The default value has a special meaning.
5、顶帽运算
原理:顶帽(或礼帽)运算是原始图像减去图像开运算的结果。
顶帽运算:原始图像 — 图像开运算
作用:得到图像的噪声。如下图所示:
5.1 方法一
//构造结构元
Mat kernel = getStructuringElement(0, Size(5, 5));
Mat image_lm;
//顶帽变换(礼帽变换)
morphologyEx(image_bw, image_lm, 5, kernel);
cv::imshow("image_lm", image_lm);
5.2 方法二
Mat image_lm2; //**顶帽运算:原始图像 — 图像开运算**
//dst=img1-img2;//这两个减法效果相同 若dst<0,则dst=0
subtract(image_bw,open_image2, image_lm2);//注意:要求被处理图片尺寸一致
cv::imshow("image_lm2",image_lm2);
Mat image_lm2;
absdiff(image_bw, open_image2, image_lm2);//若dst<0,则dst=|dst|>=0 用于检测两幅相似图像的不同点,效果比上面的两种减法好
cv::imshow("image_lm2", image_lm2);
6、黑帽运算
原理:黑帽运算是图像闭运算操作减去原始图像的结果。
黑帽运算:闭运算 — 原始图像
作用:得到图像内部的小孔,或者前景色中的小黑点。如下图所示:
6.1 方法一
//构造结构元
Mat kernel = getStructuringElement(0, Size(5, 5));
//底帽变换(黑帽变换)
Mat image_hm;
morphologyEx(image_bw, image_hm, 6, kernel);
cv::imshow("image_hm", image_hm);
6.2 方法二
Mat image_hm2;
subtract(image_bw, close_image2, image_hm2);//注意:要求被处理图片尺寸一致
cv::imshow("image_hm2", image_hm2);
Mat image_hm2;
absdiff(image_bw, close_image2, image_hm2);//若dst<0,则dst=|dst|>=0 用于检测两幅相似图像的不同点,效果比上面的两种减法好
cv::imshow("image_hm2", image_hm2);
7、形态学梯度
原理:膨胀图与腐蚀图之差;
作用:对二值图进行这一操作可以得到图像中白色区域的边界,因此可以用形态学梯度来保留物体的边界轮廓。
7.1 方法一
//构造结构元
Mat kernel = getStructuringElement(0, Size(5, 5));
Mat image_lm;
//形态学梯度
morphologyEx(image_bw, image_td, 4, kernel);
cv::imshow("image_td", image_td);
7.2 方法二
Mat image_tw2;
subtract(image_dilate, image_erosion, image_tw2);//注意:要求被处理图片尺寸一致
cv::imshow("image_tw2", image_tw2);
Mat image_tw2;
absdiff(image_dilate, image_erosion, image_tw2);//若dst<0,则dst=|dst|>=0 用于检测两幅相似图像的不同点,效果比上面的两种减法好
cv::imshow("image_tw2", image_tw2);
部分参考来自:数字图像处理(c++ opencv)–持续更新 - 知乎 (zhihu.com)
转载:https://blog.csdn.net/qq_43784519/article/details/127820189