目标
在本教程中,您将学习如何:
- 应用两个非常常见的形态算子:侵蚀和扩张。为此,您将使用以下 OpenCV 函数:
注意
下面的解释属于 Bradski 和 Kaehler 合著的《学习 OpenCV》一书。
形态学操作
- 简而言之:一组基于形状处理图像的操作。形态学操作将结构化元素应用于输入图像并生成输出图像。
- 最基本的形态操作是:侵蚀和扩张。它们具有广泛的用途,即:
- 消除噪音
- 隔离单个元素并连接图像中的不同元素。
- 在图像中发现强度凸起或孔洞
-
我们将以下图为例,简要解释扩张和侵蚀:
扩张
- 此操作包括将图像 与一些内核 (B) 卷积,该内核可以具有任何形状或大小,通常是正方形或圆形。一个B
- 内核 B 有一个定义的锚点,通常是内核的中心。B
- 当内核 B 在图像上扫描时,我们计算与 B 重叠的最大像素值,并将锚点位置的图像像素替换为该最大值。可以推断,这种最大化操作会导致图像中的明亮区域“增长”(因此称为扩张)。BB
- 膨胀操作为: \texttt{dst} (x,y) = \max _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')dst(x,y)=麦克斯(x′,y′):元素(x′,y′)≠0src(x+x′,y+y′)
-
以上图为例。应用扩张,我们可以得到:
- 字母的明亮区域在背景的黑色区域周围扩张。
侵蚀
- 这种手术是扩张的姊妹。它计算给定内核面积的局部最小值。
- 当内核 B 在图像上扫描时,我们计算与 B 重叠的最小像素值,并将锚点下的图像像素替换为该最小值。BB
- 侵蚀操作为: \texttt{dst} (x,y) = \min _{(x',y'): \, \texttt{element} (x',y') \ne0 } \texttt{src} (x+x',y+y')dst(x,y)=分钟(x′,y′):元素(x′,y′)≠0src(x+x′,y+y′)
-
与膨胀示例类似,我们可以将侵蚀算子应用于原始图像(如上所示)。您可以在下面的结果中看到,图像的明亮区域变薄,而暗区变大。
法典
C++爪哇岛蟒
本教程的代码如下所示。您也可以在此处下载
#include“opencv2/imgproc.hpp”#include“opencv2/highgui.hpp”#include < iostream>使用命名空间 CV;使用命名空间 std;垫子 src, erosion_dst, dilation_dst;int erosion_elem = 0;int erosion_size = 0;int dilation_elem = 0;int dilation_size = 0;int 常量 max_elem = 2;int 常量 max_kernel_size = 21;无效侵蚀( int, void* );无效扩张( int, void* );int main( int argc, char** argv ){CommandLineParser 解析器( argc, argv, “{@input |Linux徽标.jpg |输入图像}” );src = imread( samples::findFile( parser.get<String>( “@input” ) ), IMREAD_COLOR );if( src.空() ){cout << “无法打开或找到图像!\n” << endl;cout << “用法:” << argv[0] << “ <输入图像>” << endl;返回 -1;}namedWindow( “侵蚀演示”, WINDOW_AUTOSIZE );namedWindow( “膨胀演示”, WINDOW_AUTOSIZE );moveWindow( “膨胀演示”, src.cols, 0 );createTrackbar( “元素:\n 0: 矩形 \n 1: 十字 \n 2: 椭圆”, “侵蚀演示”,&erosion_elem、max_elem、侵蚀 );createTrackbar( “内核大小:\n 2n +1”, “侵蚀演示”,&erosion_size、max_kernel_size、侵蚀 );createTrackbar( “元素:\n 0: 矩形 \n 1: 十字 \n 2: 椭圆”, “膨胀演示”,&dilation_elem、max_elem、扩张 );createTrackbar( “内核大小:\n 2n +1”, “膨胀演示”,&dilation_size、max_kernel_size、扩张 );侵蚀( 0, 0 );扩张( 0, 0 );waitKey(0);返回 0;}无效侵蚀( int, void* ){int erosion_type = 0;if( erosion_elem == 0 ){ erosion_type = MORPH_RECT; }else if( erosion_elem == 1 ){ erosion_type = MORPH_CROSS; }else if( erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }Mat 元素 = getStructuringElement( erosion_type,尺寸( 2*erosion_size + 1, 2*erosion_size+1 ),点( erosion_size, erosion_size ) );erode( src, erosion_dst, element );imshow( “侵蚀演示”, erosion_dst );}无效扩张( int, void* ){int dilation_type = 0;if( dilation_elem == 0 ){ dilation_type = MORPH_RECT; }else if( dilation_elem == 1 ){ dilation_type = MORPH_CROSS; }else if( dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; }Mat 元素 = getStructuringElement( dilation_type,尺寸( 2*dilation_size + 1, 2*dilation_size+1 ),点( dilation_size, dilation_size ) );dilate( src, dilation_dst, element );imshow( “扩张演示”, dilation_dst );}
解释
C++爪哇岛蟒
这里展示的大部分材料都是微不足道的(如果您有任何疑问,请参阅前面部分的教程)。让我们检查一下 C++ 程序的一般结构:
int main( int argc, char** argv ){CommandLineParser parser( argc, argv, “{@input |Linux徽标.jpg |输入图像}” );src = imread( samples::findFile( parser.get<String>( “@input” ) ), IMREAD_COLOR );if( src.空() ){cout << “无法打开或找到图像!\n” << endl;cout << “用法:” << argv[0] << “ <输入图像>” << endl;返回 -1;}namedWindow( “侵蚀演示”, WINDOW_AUTOSIZE );namedWindow( “膨胀演示”, WINDOW_AUTOSIZE );moveWindow( “膨胀演示”, src.cols, 0 );createTrackbar( “元素:\n 0: 矩形 \n 1: 十字 \n 2: 椭圆”, “侵蚀演示”,&erosion_elem、max_elem、侵蚀 );createTrackbar( “内核大小:\n 2n +1”, “侵蚀演示”,&erosion_size、max_kernel_size、侵蚀 );createTrackbar( “元素:\n 0: 矩形 \n 1: 十字 \n 2: 椭圆”, “膨胀演示”,&dilation_elem、max_elem、扩张 );createTrackbar( “内核大小:\n 2n +1”, “膨胀演示”,&dilation_size、max_kernel_size、扩张 );侵蚀( 0, 0 );扩张( 0, 0 );waitKey(0);返回 0;}
- 加载图像(可以是 BGR 或灰度)
- 创建两个窗口(一个用于膨胀输出,另一个用于侵蚀)
- 为每个操作创建一组两个跟踪栏:
- 第一个跟踪栏“Element”返回 erosion_elem 或 dilation_elem
- 第二个跟踪栏“内核大小”返回相应操作的erosion_size或dilation_size。
- 调用一次侵蚀和扩张以显示初始图像。
每次我们移动任何滑块时,都会调用用户的函数 Erosion 或 Dilation,它将根据当前的轨迹栏值更新输出图像。
我们来分析一下这两个函数:
侵蚀函数
{int erosion_type = 0;if( erosion_elem == 0 ){ erosion_type = MORPH_RECT; }else if( erosion_elem == 1 ){ erosion_type = MORPH_CROSS; }else if( erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }Mat 元素 = getStructuringElement( erosion_type,尺寸( 2*erosion_size + 1, 2*erosion_size+1 ),点( erosion_size, erosion_size ) );erode( src, erosion_dst, element );imshow( “侵蚀演示”, erosion_dst );}
执行侵蚀操作的函数是 cv::erode 。正如我们所看到的,它接收三个参数:
- src:源图片
- erosion_dst:输出图像
-
element:这是我们将用来执行操作的内核。如果我们不指定,则默认值为简单矩阵。否则,我们可以指定它的形状。为此,我们需要使用函数 cv::getStructuringElement :
3x3
Mat 元素 = getStructuringElement( erosion_type,尺寸( 2*erosion_size + 1, 2*erosion_size+1 ),点( erosion_size, erosion_size ) );我们可以为内核选择三种形状中的任何一种:
- 长方形盒子:MORPH_RECT
- 十字架:MORPH_CROSS
- 椭圆:MORPH_ELLIPSE
然后,我们只需要指定内核的大小和锚点。如果未指定,则假定它位于中心。
仅此而已。我们已准备好对我们的形象进行侵蚀。
扩张功能
代码如下。正如你所看到的,它与侵蚀的代码片段完全相似。在这里,我们还可以选择定义我们的内核、它的锚点和要使用的运算符的大小。
{int dilation_type = 0;if( dilation_elem == 0 ){ dilation_type = MORPH_RECT; }else if( dilation_elem == 1 ){ dilation_type = MORPH_CROSS; }else if( dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; }Mat 元素 = getStructuringElement( dilation_type,尺寸( 2*dilation_size + 1, 2*dilation_size+1 ),点( dilation_size, dilation_size ) );dilate( src, dilation_dst, element );imshow( “扩张演示”, dilation_dst );}
注意
此外,还有其他参数允许您一次执行多个侵蚀/扩张(迭代),还可以设置边界类型和值。但是,我们在这个简单的教程中没有使用它们。您可以查看参考资料了解更多详情。
结果
编译上面的代码并使用图像作为参数执行它(如果使用 python,则运行脚本)。如果不提供图像作为参数,则将使用默认示例图像 (LinuxLogo.jpg)。
例如,使用此图像:
我们得到下面的结果。自然,改变轨迹栏中的索引会给出不同的输出图像。试试看吧!您甚至可以尝试添加第三个跟踪栏来控制迭代次数。
(根据编程语言的不同,输出可能会略有不同或只有 1 个窗口)
在线教程
- 麻省理工学院人工智能视频教程 – 麻省理工人工智能课程
- 人工智能入门 – 人工智能基础学习。Peter Norvig举办的课程
- EdX 人工智能 – 此课程讲授人工智能计算机系统设计的基本概念和技术。
- 人工智能中的计划 – 计划是人工智能系统的基础部分之一。在这个课程中,你将会学习到让机器人执行一系列动作所需要的基本算法。
- 机器人人工智能 – 这个课程将会教授你实现人工智能的基本方法,包括:概率推算,计划和搜索,本地化,跟踪和控制,全部都是围绕有关机器人设计。
- 机器学习 – 有指导和无指导情况下的基本机器学习算法
- 机器学习中的神经网络 – 智能神经网络上的算法和实践经验
- 斯坦福统计学习
有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓
人工智能书籍
- OpenCV(中文版).(布拉德斯基等)
- OpenCV+3计算机视觉++Python语言实现+第二版
- OpenCV3编程入门 毛星云编著
- 数字图像处理_第三版
- 人工智能:一种现代的方法
- 深度学习面试宝典
- 深度学习之PyTorch物体检测实战
- 吴恩达DeepLearning.ai中文版笔记
- 计算机视觉中的多视图几何
- PyTorch-官方推荐教程-英文版
- 《神经网络与深度学习》(邱锡鹏-20191121)
- …
第一阶段:零基础入门(3-6个月)
新手应首先通过少而精的学习,看到全景图,建立大局观。 通过完成小实验,建立信心,才能避免“从入门到放弃”的尴尬。因此,第一阶段只推荐4本最必要的书(而且这些书到了第二、三阶段也能继续用),入门以后,在后续学习中再“哪里不会补哪里”即可。
第二阶段:基础进阶(3-6个月)
熟读《机器学习算法的数学解析与Python实现》并动手实践后,你已经对机器学习有了基本的了解,不再是小白了。这时可以开始触类旁通,学习热门技术,加强实践水平。在深入学习的同时,也可以探索自己感兴趣的方向,为求职面试打好基础。
第三阶段:工作应用
这一阶段你已经不再需要引导,只需要一些推荐书目。如果你从入门时就确认了未来的工作方向,可以在第二阶段就提前阅读相关入门书籍(对应“商业落地五大方向”中的前两本),然后再“哪里不会补哪里”。
有需要的小伙伴,可以点击下方链接免费领取或者V扫描下方二维码免费领取🆓