当前位置:网站首页>【OpenCV】形态学滤波(2):开运算、形态学梯度、顶帽、黑帽
【OpenCV】形态学滤波(2):开运算、形态学梯度、顶帽、黑帽
2022-07-07 01:41:00 【我菜就爱学】
1、开运算
开运算(Opening Operation),其实就是先腐蚀后膨胀的过程,其数学表达式如下:
d s t = o p e n ( s r c , e l e m e n t ) = d i l a t e ( e r o d e ( s r c , e l e m e n t ) ) dst=open(src,element)=dilate(erode(src,element)) dst=open(src,element)=dilate(erode(src,element))
作用:开运算可以消除小物体,在纤细点处分离物体,并且在平滑较大物体的边界的同时不明显改变其面积。
示例代码:
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int main()
{
//载入原始图
Mat image = imread("E:\\Pec\\美国队长.jpg");
//创建窗口
namedWindow("【原始图】");
namedWindow("【效果图】");
//显示原始图
imshow("【原始图】", image);
//定义内核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
//进行形态学操作
morphologyEx(image, image, MORPH_OPEN, element);
//显示原始图
imshow("【效果图】", image);
waitKey(0);
}
2、闭运算
先膨胀后腐蚀的过程称为闭运算(Closing Operation),其数学表达式如下:
d s t = c l o s e ( s r c , e l e m e n t ) = e r o d e ( d i l a t e ( s r c , e l e m e n t ) ) dst=close(src,element)=erode(dilate(src,element)) dst=close(src,element)=erode(dilate(src,element))
作用:闭运算能够排除小型黑洞(黑色区域)
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int main()
{
//载入原始图
Mat image = imread("E:\\Pec\\美国队长.jpg");
//创建窗口
namedWindow("【原始图】");
namedWindow("【效果图】");
//显示原始图
imshow("【原始图】", image);
//定义内核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
//进行形态学操作
morphologyEx(image, image, MORPH_CLOSE, element);
//显示原始图
imshow("【效果图】", image);
waitKey(0);
}
3、形态学梯度
形态学梯度(Morphological Gradient)是膨胀土与腐蚀图之差,数学表达式如下:
d s t = m o r p h − g r a d ( s r c , e l e m e n t ) = d i l a t e ( s r c , e l e m e n t ) − e r o d e ( s r c , e l e m e n t ) dst=morph-grad(src,element)=dilate(src,element)-erode(src,element) dst=morph−grad(src,element)=dilate(src,element)−erode(src,element)
作用:二值图像进行这一操作可以将团块(blob)的边缘突出出来,利用形态学梯度来保留物体的边缘轮廓。
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int main()
{
//载入原始图
Mat image = imread("E:\\Pec\\美国队长.jpg");
//创建窗口
namedWindow("【原始图】");
namedWindow("【效果图】");
//显示原始图
imshow("【原始图】", image);
//定义内核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
//进行形态学操作
morphologyEx(image, image, MORPH_GRADIENT, element);
//显示原始图
imshow("【效果图】", image);
waitKey(0);
}
4、顶帽
顶帽运算(Top Hat)又常常被译为“礼帽”运算,是原图像与上文刚刚介绍的“开运算”的结果图之差。
d s t = t o p h a t ( s r c , e l e m e n t ) = s r c − o p e n ( s r c , e l e m e n t ) dst=tophat(src,element)=src-open(src,element) dst=tophat(src,element)=src−open(src,element)
因为开运算带来的结果是放大了裂缝或者局部低亮度的区域。因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作与选择的核的大小相关。
作用:顶帽运算往往用来分离比邻近点亮一些的斑块。在一副图像具有大幅的背景,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提前。
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int main()
{
//载入原始图
Mat image = imread("E:\\Pec\\美国队长.jpg");
//创建窗口
namedWindow("【原始图】");
namedWindow("【效果图】");
//显示原始图
imshow("【原始图】", image);
//定义内核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
//进行形态学操作
morphologyEx(image, image, MORPH_TOPHAT, element);
//显示原始图
imshow("【效果图】", image);
waitKey(0);
}
5、黑帽
黑帽(Black Hat)运算是闭运算的结果图与原图像之差,数学表达式为:
d s t = b l a c k h a t ( s r c , e l e m e n t ) = c l o s e ( s r c , e l e m e n t ) − s r c dst=blackhat(src,element)=close(src,element)-src dst=blackhat(src,element)=close(src,element)−src
黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小有关。
作用:黑帽运算用来分离比邻近点暗一些的斑块,效果图有着非常完美的轮廓。
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<iostream>
using namespace cv;
using namespace std;
int main()
{
//载入原始图
Mat image = imread("E:\\Pec\\美国队长.jpg");
//创建窗口
namedWindow("【原始图】");
namedWindow("【效果图】");
//显示原始图
imshow("【原始图】", image);
//定义内核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
//进行形态学操作
morphologyEx(image, image, MORPH_BLACKHAT, element);
//显示原始图
imshow("【效果图】", image);
waitKey(0);
}
6、核心API函数:morphologyEx()
void morphologyEx(
InputArray src,
OutputArray dst,
int op,
InputArray kernel,
Pointanchor=Point(-1,-1),
int iteration=1,
int borderType=BORDER_CONSTANT,
const Scalar& borderValue=morphologyDefaultBorderValue()
);
第一个参数:输入图像,即源图像,填Mat类的对象即可。图像位深应该为以下5种之一:CV_8U、CV_16U、CV_16S、CV_32F和CV_64F
第二个参数:即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型
第三个参数:int类型的op,表示形态学运算的类型,可以是如下表中的任意之一的标识符
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G93BBVtQ-1656987857859)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20220705095322440.png)]
第四个参数:InputArray 类型的kernel,形态学运算的内核。若为NULL,表示的是使用参考点位于中心3x3的核。一般使用函数getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定的形状和尺寸的结构元素(内核矩阵)
getStructuringElement函数的第一个参数表示内核的形状:
- (1)矩形——MORPH_RECT
- (2)交叉形——MORPH_CROSS
- (3)椭圆形——MORPH_ELLIPSE
而getStructuringElement函数的第二个参数和第三个参数分别是内核的尺寸以及锚点的位置
一般在调用erode和dilate函数之前,要先定义一个Mat类型的变量来获得getStructuringElement函数的返回值,对于锚点的位置,有默认值(-1,-1),表示锚点位于中心。另外注意:十字形的element形状唯一依赖于锚点的位置。而在其他情况下,锚点只是影响形态学运算结果的偏移
//getStructuringElement函数相关调用如下: int g_nStructuringElement(MORPH_RECT,Size(2*g_nStructuringElement+1, 2*g_nStructuringElement+1),Point(g_nStructuringElement,g_nStructuringElement));
之后便可以调用erode、dilate或morphologyEx函数时,由kernel参数填保存getStructuringElement返回值的Mat类型变量。
第五个参数:锚点的位置,默认值(-1,-1),锚点位于中心
第六个参数:迭代使用函数的次数,默认值为1.
第七个参数:用于推断图像外部像素的某种边界模式。有默认值BORDER_CONSTANT
第八个参数:当边界为常数时的边界值,有默认值morphologyDEfaultBorderValue(),一般不需要考虑。这些操作都是可以进行就地(in-place)操作,且对于多通道图像,每一个通道都单独进行操作。
7、综合示例:形态学滤波
说明:下面的程序运行示例一共会出现4个显示图像的窗口,包含原始图一个、开\闭运算一个,膨胀\腐蚀一个,顶帽\黑帽运算一个。它们分别使用滚动条,来控制得到形态学效果,且迭代值为10的时候为中间点。此外,还可以通过键盘1、2、3以及空格键来调节称不同的元素结构
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<iostream>
using namespace cv;
using namespace std;
//-----------------
//全局变量声明
//---------------------
Mat g_srcImage, g_dstImage;
int g_nElementShape = MORPH_RECT;//元素结果的形状
//-----------------------------------------
//变量接受的TrackBar位置参数
//------------------------------------------
int g_nMaxIterationNum = 10;
int g_nOpenCloseNum = 0;
int g_nErodeDilateNum = 0;
int g_nTopBlackHatNum = 0;
//--------------------------------------------
//全局函数声明
//-------------------------------------------
static void on_OpenClose(int, void*);
static void on_ErodeDilate(int, void*);
static void on_TopBlackHat(int ,void*);
static void ShowHelpText();//帮我文字显示
int main()
{
system("color 5E");
//载入原始图
g_srcImage = imread("E:\\Pec\\钢铁侠.jpg");
if (!g_srcImage.data) {
printf("图片读取错误\n");
return false;
}
//显示原始图
namedWindow("【原始图】");
imshow("【原始图】", g_srcImage);
//创建三个窗口
namedWindow("【开运算/闭运算】", 1);
namedWindow("【腐蚀/膨胀】", 1);
namedWindow("【顶帽/黑帽】", 1);
//参数赋值
g_nOpenCloseNum = 9;
g_nErodeDilateNum = 9;
g_nTopBlackHatNum = 9;
//分别创建三个窗口的轨迹条
createTrackbar("迭代值", "【开运算/闭运算】", &g_nOpenCloseNum, g_nMaxIterationNum * 2 + 1, on_OpenClose);
createTrackbar("迭代值", "【腐蚀/膨胀】", &g_nErodeDilateNum, g_nErodeDilateNum * 2 + 1, on_ErodeDilate);
createTrackbar("迭代值", "【顶帽/黑帽】", &g_nTopBlackHatNum, g_nTopBlackHatNum * 2 + 1, on_TopBlackHat);
printf("----------------------------------------------------------------------\n");
printf("| 操作说明 |\n");
printf("| 按下键盘Q或者ESC,程序退出 |\n");
printf("| 按下键盘1,使用椭圆(Elliptic)结构原始结构元素MORPH_ELLIPSE| |\n");
printf("| 按下键盘2,使用矩形(Rectangle)结构原始结构元素MORPH_RECT |\n");
printf("| 按下键盘3,使用十字形(Cross-shaped)结构原始结构元素MORPH_CROSS |\n");
printf("| 按下键盘space,在矩形、椭圆和十字形之间循环 |\n");
printf("-------------------------------------------------------------------\n");
//轮询获取按键信息
while (1)
{
int c;
//执行回调函数
on_OpenClose(g_nOpenCloseNum, 0);
on_ErodeDilate(g_nErodeDilateNum, 0);
on_TopBlackHat(g_nTopBlackHatNum, 0);
c = waitKey(0);
//按下键盘Q或者ESC,程序退出
if ((char)c == 'q' || (char)c == 27)
break;
//按下键盘1,使用椭圆(Elliptic)结构原始结构元素MORPH_ELLIPSE
else if ((char)c == 49)//1的ASII码为49
g_nElementShape = MORPH_ELLIPSE;
//按下键盘2,使用矩形(Rectangle)结构原始结构元素MORPH_RECT
else if ((char)c == 50)
g_nElementShape = MORPH_RECT;
//按下键盘3,使用十字形(Cross-shaped)结构原始结构元素MORPH_CROSS
else if ((char)c == 51)
g_nElementShape = MORPH_CROSS;
//按下键盘space,在矩形、椭圆和十字形之间循环
else if ((char)c == ' ')
g_nElementShape = (g_nElementShape + 1) % 3;
}
return 0;
}
static void on_OpenClose(int, void*)
{
//偏移量的定义
int offset = g_nOpenCloseNum - g_nMaxIterationNum;//偏移量
int Absolute_offset = offset > 0 ? offset : -offset;//取正值
//自定义核
Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset * 2 + 1, Absolute_offset * 2 + 1), Point(Absolute_offset, Absolute_offset));
//进行操作
if (offset < 0)
morphologyEx(g_srcImage, g_dstImage, CV_MOP_OPEN, element);
else
morphologyEx(g_srcImage, g_dstImage,MORPH_CLOSE, element);
imshow("【开运算/闭运算】", g_dstImage);
}
static void on_ErodeDilate(int, void*)
{
//偏移量的定义
int offset = g_nErodeDilateNum - g_nMaxIterationNum;//偏移量
int Absolute_offset = offset > 0 ? offset : -offset;//取正值
//自定义核
Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset * 2 + 1, Absolute_offset * 2 + 1), Point(Absolute_offset, Absolute_offset));
//进行操作
if (offset < 0)
erode(g_srcImage, g_dstImage,element);
else
dilate(g_srcImage, g_dstImage, element);
imshow("【腐蚀/膨胀】", g_dstImage);
}
static void on_TopBlackHat(int, void*)
{
//偏移量的定义
int offset = g_nTopBlackHatNum - g_nMaxIterationNum;//偏移量
int Absolute_offset = offset > 0 ? offset : -offset;//取正值
//自定义核
Mat element = getStructuringElement(g_nElementShape, Size(Absolute_offset * 2 + 1, Absolute_offset * 2 + 1), Point(Absolute_offset, Absolute_offset));
//进行操作
if (offset < 0)
morphologyEx(g_srcImage, g_dstImage, MORPH_TOPHAT, element);
else
morphologyEx(g_srcImage, g_dstImage, MORPH_BLACKHAT, element);
imshow("【顶帽/黑帽】", g_dstImage);
}
原图像:
开运算、腐蚀、顶帽图像
闭运算、膨胀、黑帽图像
边栏推荐
- C. colonne Swapping [tri + Simulation]
- Qt多线程的多种方法之一 QThread
- Value range of various datetimes in SQL Server 2008
- Rk3399 platform development series explanation (WiFi) 5.52. Introduction to WiFi framework composition
- 693. 行程排序
- Dc-7 target
- Swagger3 configuration
- C language sorting (to be updated)
- Ideas of high concurrency and high traffic seckill scheme
- 【FPGA教程案例14】基于vivado核的FIR滤波器设计与实现
猜你喜欢
JVM命令之 jstack:打印JVM中线程快照
@Detailed differences between pathvariable and @requestparam
【FPGA教程案例13】基于vivado核的CIC滤波器设计与实现
Ideas of high concurrency and high traffic seckill scheme
A freshman's summary of an ordinary student [I don't know whether we are stupid or crazy, but I know to run forward all the way]
Find duplicate email addresses
postgresql 数据库 timescaledb 函数time_bucket_gapfill()报错解决及更换 license
If you don't know these four caching modes, dare you say you understand caching?
Bbox regression loss function in target detection -l2, smooth L1, IOU, giou, Diou, ciou, focal eiou, alpha IOU, Siou
VMware安装后打开就蓝屏
随机推荐
window下面如何安装swoole
C. colonne Swapping [tri + Simulation]
Developers don't miss it! Oar hacker marathon phase III chain oar track registration opens
Rk3399 platform development series explanation (WiFi) 5.52. Introduction to WiFi framework composition
Go language learning notes - Gorm use - native SQL, named parameters, rows, tosql | web framework gin (IX)
Storage of dental stem cells (to be continued)
Peripheral driver library development notes 43: GPIO simulation SPI driver
Find duplicate email addresses
ML's shap: Based on the adult census income binary prediction data set (whether the predicted annual income exceeds 50K), use the shap decision diagram combined with the lightgbm model to realize the
postgresql 数据库 timescaledb 函数time_bucket_gapfill()报错解决及更换 license
Introduction to the extension implementation of SAP Spartacus checkout process
3531. 哈夫曼树
laravel 使用腾讯云 COS5全教程
PTA ladder game exercise set l2-004 search tree judgment
「解析」FocalLoss 解决数据不平衡问题
[FPGA tutorial case 13] design and implementation of CIC filter based on vivado core
Array proof during st table preprocessing
基于FPGA的VGA协议实现
安装mongodb数据库
Solve pod install error: FFI is an incompatible architecture