【OpenCV】形态学滤波(2):开运算、形态学梯度、顶帽、黑帽

简介: 【OpenCV】形态学滤波(2):开运算、形态学梯度、顶帽、黑帽

1、开运算

开运算(Opening Operation),其实就是先腐蚀后膨胀的过程,其数学表达式如下:

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),其数学表达式如下:

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)是膨胀土与腐蚀图之差,数学表达式如下:

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)又常常被译为“礼帽”运算,是原图像与上文刚刚介绍的“开运算”的结果图之差。

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)运算是闭运算的结果图与原图像之差,数学表达式为:

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,表示形态学运算的类型,可以是如下表中的任意之一的标识符

第四个参数: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);
}

原图像:

开运算、腐蚀、顶帽图像

闭运算、膨胀、黑帽图像

相关文章
|
5月前
|
存储 计算机视觉
OpenCv形态学(一)
OpenCv形态学(一)
|
6月前
|
算法 计算机视觉
【Qt&OpenCV 图像的形态学变换 morpholgyEx】
【Qt&OpenCV 图像的形态学变换 morpholgyEx】
41 0
|
6月前
|
资源调度 算法 计算机视觉
【Qt&OpenCV 图像平滑/滤波处理 -- Blur/Gaussian/Median/Bilateral】
【Qt&OpenCV 图像平滑/滤波处理 -- Blur/Gaussian/Median/Bilateral】
79 0
|
7月前
|
编解码 计算机视觉 C++
【OpenCV】—形态学滤波(1):腐蚀与膨胀
【OpenCV】—形态学滤波(1):腐蚀与膨胀
|
7月前
|
计算机视觉
OpenCV按位逻辑运算
OpenCV按位逻辑运算
30 0
|
2月前
|
计算机视觉
Opencv学习笔记(三):图像二值化函数cv2.threshold函数详解
这篇文章详细介绍了OpenCV库中的图像二值化函数`cv2.threshold`,包括二值化的概念、常见的阈值类型、函数的参数说明以及通过代码实例展示了如何应用该函数进行图像二值化处理,并展示了运行结果。
527 0
Opencv学习笔记(三):图像二值化函数cv2.threshold函数详解
|
3月前
|
算法 计算机视觉
opencv图像形态学
图像形态学是一种基于数学形态学的图像处理技术,它主要用于分析和修改图像的形状和结构。
55 4
|
3月前
|
存储 计算机视觉
Opencv的基本操作(一)图像的读取显示存储及几何图形的绘制
本文介绍了使用OpenCV进行图像读取、显示和存储的基本操作,以及如何绘制直线、圆形、矩形和文本等几何图形的方法。
Opencv的基本操作(一)图像的读取显示存储及几何图形的绘制
|
4月前
|
算法 计算机视觉 Python
python利用opencv进行相机标定获取参数,并根据畸变参数修正图像附有全部代码(流畅无痛版)
该文章详细介绍了使用Python和OpenCV进行相机标定以获取畸变参数,并提供了修正图像畸变的全部代码,包括生成棋盘图、拍摄标定图像、标定过程和畸变矫正等步骤。
python利用opencv进行相机标定获取参数,并根据畸变参数修正图像附有全部代码(流畅无痛版)
WK
|
4月前
|
编解码 计算机视觉 Python
如何在OpenCV中进行图像转换
在OpenCV中,图像转换涉及颜色空间变换、大小调整及类型转换等操作。常用函数如`cvtColor`可实现BGR到RGB、灰度图或HSV的转换;`resize`则用于调整图像分辨率。此外,通过`astype`或`convertScaleAbs`可改变图像数据类型。对于复杂的几何变换,如仿射或透视变换,则可利用`warpAffine`和`warpPerspective`函数实现。这些技术为图像处理提供了强大的工具。
WK
130 1
下一篇
DataWorks