2019-02-17 23:35:32 garrulousabyss 阅读数 58
  • OpenCV3.2 Java图像处理视频学习教程

    OpenCV3.2 Java图像处理视频培训课程:基于OpenCV新版本3.2.0详细讲述Java OpenCV图像处理部分内容,包括Mat对象使用、图像读写、 基于常用核心API讲述基本原理、使用方法、参数、代码演示、图像处理思路与流程讲授。主要内容包括opencv像素操作、滤波、边缘提取、直线与圆检测、形态学操作与分水岭、图像金子塔融合重建、多尺度模板匹配、opencv人脸检测、OpenCV跟Tomcat使用实现服务器端图像处理服务。

    4127 人正在学习 去看看 贾志刚

继膨胀、腐蚀、开运算和闭运算之后的有一个基本操作就是击中击不中变换(HMT),HMT变换可以同时探测图像的内部和外部。在研究图像中的目标物体与图像背景之间的关系上,HMT能够取得很好的效果。所以常被用于解决目标图像识别和模式识别等领域。

1,结构元素是形态学变换中的基本元素,是为了探测图像的某种结构信息而设计的特定形状和尺寸的图像,也可以称为收集图像结构信息的探针。结构元素有许多的种类,如圆形、方形、线型等,还有二值的和灰度值的,模糊集中的等等多种结构元素。在击中击不中变换中将结构元素分解成两个,一个定义为前景结构元素,一个定义为背景结构元素,定义如下:B=(E,F),     其中E的F交集为空集

2,HMT的标准变换的定义就是:

                            

  腐蚀操作的结果就是结构元素S平移x但任包含在输入图像A内部的所有结构元素的原点集合,对于HMT变换,当且仅当结构元素E平移到某一点可以填入A的内部,且F平移到该点时可以填入A的外部时,该点才能在HMT变换的结果中输出。由于需要精确的匹配,因此该算法对识别的要求很高,但是对于实际的图像,往往含有各种未知的噪声,即会出现误差,所以实际的意义不是很大

它的原理就是使用腐蚀;如果要在一幅图像A上找到B形状的目标,我们要做的是:

首先,建立一个比B大的模板W;使用此模板对图像A进行腐蚀,得到图像假设为Process1;

其次,用B减去W,从而得到V模板(W-B);使用V模板对图像A的补集进行腐蚀,得到图像假设为Process2;

然后,Process1与Process2取交集;得到的结果就是B的位置。这里的位置可能不是B的中心位置,要视W-B时对齐的位置而异;

  其实很简单,两次腐蚀,然后交集,结果就出来了;

HMT是基于多个物体分开的假设,才有背景结构元素的,在某些情况,我们只对某个物体的模式匹配感兴趣, 此时HMT简化为腐蚀。

一些形态学算法

1.边界提取 原图与腐蚀结果做差

2.空洞填充 从第一个边界内的0点膨胀,不断迭代

3.连通分量提取 ,从连通分量区域内第一个1值开始膨胀,不断迭代。

4.凸壳C(A),其实就是包含一个集合A的最小区域,可通过算法限制其各个方向的生长,保证凸性需要最小尺寸。通过Bi结构元,让每个结构元腐蚀集合A,直到收敛,而后对i个结构元的结果取并集。

5.细化/粗化,细化是A-(B对A的HMT),粗化则是A+B对A的HMT.但实际中,并不按此公式来做,而是先对问题集的背景细化,再对结果求补。

6.骨架S(A)

7.裁剪,是对骨架和细化操作的补充。不断删除寄生分支的终点来抑制分支。

8.形态学重建,包括测地腐蚀重建和测地膨胀重建。所谓测地腐蚀或者膨胀,实际上是利用模板限制标记图像的生长。最终会收敛。形成膨胀/腐蚀形态学重建。所以有用的地方是,重建开/闭操作,填充空洞,边界清除等。

一些灰度级形态学算法

1.平滑,利用开操作抑制亮细节,闭操作抑制暗细节

2.梯度,即膨胀-腐蚀

3.顶帽 原图像-开,底帽变换 闭-原图像,常用来删除物体,而非拟合物体。

4.粒度测定,即表面积随着SE增大而减小

5.纹理分割,即先闭消除小点,后开,消除大点。

灰度级形态学重建

与二值类似。只是交集取小值,并集取大值。
--------------------- 
作者:EasonApp 
来源:CSDN 
原文:https://blog.csdn.net/App_12062011/article/details/39577609 
版权声明:本文为博主原创文章,转载请附上博文链接!

2018-08-04 00:51:57 qq_40962368 阅读数 3583
  • OpenCV3.2 Java图像处理视频学习教程

    OpenCV3.2 Java图像处理视频培训课程:基于OpenCV新版本3.2.0详细讲述Java OpenCV图像处理部分内容,包括Mat对象使用、图像读写、 基于常用核心API讲述基本原理、使用方法、参数、代码演示、图像处理思路与流程讲授。主要内容包括opencv像素操作、滤波、边缘提取、直线与圆检测、形态学操作与分水岭、图像金子塔融合重建、多尺度模板匹配、opencv人脸检测、OpenCV跟Tomcat使用实现服务器端图像处理服务。

    4127 人正在学习 去看看 贾志刚

图像的形态学处理

        数学形态学(Mathematical morphology)是一门 建立在格论和拓扑学基础之上的图像分析学科,是数学形态学图像处理的基本理论。其基本的运算包括:腐蚀和膨胀、开运算和闭运算、骨架抽取、极限腐蚀、击中击不中变换、形态学梯度、Top-hat变换、颗粒分析、流域变换等。

        膨胀、腐蚀、开运算和闭运算是数学形态学的四个基本运算,它们在二值图像和灰度图像中各有特点。基于这些运算还可推导和组合成各种数学形态学实用算法,用它们可以进行图像形状和结构的分析和处理,包括图像分割、特征提取、边缘检测、图像滤波、图像增强和恢复等。有关数学形态学更多的介绍,可以查看百度词条:数学形态学

(一)图像的膨胀和腐蚀

定义结构元素是数学形态学处理的核心,在OpenCV中可以使用其自带的getStructuringElemet函数,也可以直接使用numpy数组来定义一个结构元素。

先进行腐蚀操作

img = cv2.imread('luotuo.jpg', 0)
# 先对图像进行一个二值化处理
ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
# 用numpy定义结构元素
npKernel = np.uint8(np.zeros((5, 5)))
for i in range(5):
    npKernel[2, i] = 1
    npKernel[i, 2] = 1
print(npKernel)
# 用OpenCV中的getStructuringElement()函数定义结构元素
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 进行腐蚀操作
npKernel_eroded = cv2.erode(th2, npKernel)
kernel_eroded = cv2.erode(th2, kernel)
cv2.imshow('img', th2)
cv2.imshow('npKernel Eroded Image', npKernel_eroded)
cv2.imshow('kernel Eroded Image', kernel_eroded)
cv2.waitKey(0)
cv2.destroyAllWindows()
[[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]]

Process finished with exit code 0

然后进行膨胀操作

img = cv2.imread('4.jpg', 0)
# 先对图像进行一个二值化处理
ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
# 用numpy定义结构元素
npKernel = np.uint8(np.zeros((5, 5)))
for i in range(5):
    npKernel[2, i] = 1
    npKernel[i, 2] = 1
# 用OpenCV中的getStructuringElement()函数定义结构元素
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 进行膨胀操作
npKernel_dilated = cv2.dilate(th2, npKernel)
kernel_dilated = cv2.dilate(th2, kernel)
cv2.imshow('img', th2)
cv2.imshow('npKernel Dilated Image', npKernel_dilated)
cv2.imshow('kernel Dilated Image', kernel_dilated)
cv2.waitKey(0)
cv2.destroyAllWindows()

关于图像的腐蚀和膨胀,就两个函数cv2.erode(),cv2.dilate(),都需要传入两个参数,一个是需要处理的二值化图像,第二个是结构元素,返回处理好的图像。

(二)开运算

开运算就是指图像先进行腐蚀再膨胀的运算,腐蚀可以使图像外的小点点去掉,再膨胀就可以去除掉图像外的噪声。

# 开运算操作
img = cv2.imread('luotuo.jpg', 0)
ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
for i in range(2000):  # 添加椒盐噪声
    _x = np.random.randint(0, th2.shape[0])
    _y = np.random.randint(0, th2.shape[1])
    th2[_x][_y] = 255
kernel = np.ones((5, 5), np.uint8)
erosion = cv2.morphologyEx(th2, cv2.MORPH_OPEN, kernel)  # 开运算函数
cv2.imshow('th2', th2)
cv2.imshow('morph_open', erosion)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

从结果来看,骆驼之外的小白点全部填充为黑色了,去除骆驼之外的白色噪声。

(三)闭运算

与开运算相反,闭运算是指先进行膨胀运算再进行腐蚀运算,膨胀可以将图像内的小白点去掉,然后把主图像腐蚀回来,实现对图像内噪声的去除。

# 闭运算
img = cv2.imread('luotuo.jpg', 0)
ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
for i in range(20000):  # 添加椒盐噪声
    _x = np.random.randint(0, th2.shape[0])
    _y = np.random.randint(0, th2.shape[1])
    th2[_x][_y] = 0
kernel = np.ones((5, 5), np.uint8)
erosion = cv2.morphologyEx(th2, cv2.MORPH_CLOSE, kernel)
cv2.imshow('th2', th2)
cv2.imshow('erosion', erosion)
cv2.waitKey(0)
cv2.destroyAllWindows()

重点:对图像的膨胀和腐蚀操作是对二值化后的白色图像区域的膨胀和腐蚀。

(四)形态学梯度

通过利用对图像的膨胀和腐蚀的组合使用,使得处理后的图像如同提取了物体的轮廓。

# 形态学梯度
img = cv2.imread('luotuo.jpg', 0)
ret, th = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
kernel = np.ones((5, 5), np.uint8)
# morphology  形态学
gradient = cv2.morphologyEx(th, cv2.MORPH_GRADIENT, kernel)  # morph gradient 形态梯度
cv2.imshow('th', th)
cv2.imshow('gradient', gradient)
cv2.waitKey(0)
cv2.destroyAllWindows()

(五)礼帽和黑帽

礼帽指的是原始图像与其进行开运算后的图像进行一个差,对于差别之处显示其原有图色。

# 礼帽
img = cv2.imread('4.jpg', 0)
ret, th = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
kernel = np.ones((5, 5), np.uint8)
tophat = cv2.morphologyEx(th, cv2.MORPH_TOPHAT, kernel)
cv2.imshow('th', th)
cv2.imshow('tophat', tophat)
cv2.waitKey(0)
cv2.destroyAllWindows()

黑帽指的是原始图像与其进行闭运算后的图像进行一个差,对于差别之处显示原有图色的反颜色。

# 黑帽
img = cv2.imread('4.jpg', 0)
ret, th = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
kernel = np.ones((5, 5), np.uint8)
blackhat = cv2.morphologyEx(th, cv2.MORPH_BLACKHAT, kernel)  # blackhat
cv2.imshow('th', th)
cv2.imshow('blackhat', blackhat)
cv2.waitKey(0)
cv2.destroyAllWindows()

2019-02-23 12:05:06 weixin_43534296 阅读数 89
  • OpenCV3.2 Java图像处理视频学习教程

    OpenCV3.2 Java图像处理视频培训课程:基于OpenCV新版本3.2.0详细讲述Java OpenCV图像处理部分内容,包括Mat对象使用、图像读写、 基于常用核心API讲述基本原理、使用方法、参数、代码演示、图像处理思路与流程讲授。主要内容包括opencv像素操作、滤波、边缘提取、直线与圆检测、形态学操作与分水岭、图像金子塔融合重建、多尺度模板匹配、opencv人脸检测、OpenCV跟Tomcat使用实现服务器端图像处理服务。

    4127 人正在学习 去看看 贾志刚
  1. 图像的形态学操作简介:
    形态学操作是基于图像的形态来进行一系列的操作的,图像的膨胀与腐蚀在前面我们已经学习过,图像的膨胀与腐蚀是最基本的形态学操作。图像的形态学操作主要是在二值图像或灰度图像上来进行分析及操作的。

  2. 图像的开操作:
    开操作的本质是先腐蚀后膨胀,可以去掉一些小的图像,例如黑色图像中的小白点,但是能否去除还要决定于所生成的结构元素能否将其覆盖,可以用来除去噪声。消除(白色)小物体。
    在这里插入图片描述

  3. 图像的闭操作:
    闭操作的本质是先膨胀后腐蚀,可以用来填充小的洞,例如将图像中的小黑点填充为白色。其具体过程为先将白色部分变大,把黑色部分腐蚀掉,再恢复。消除(黑色)小物体。
    在这里插入图片描述

  4. 形态学梯度操作:
    本质是膨胀减去腐蚀,对二值图可以将其的边缘凸显出来,可以用其来保留边缘轮廓。膨胀减去腐蚀为图像的基本梯度操作,图像的梯度操作还包括内部梯度、方向梯度等操作。
    在这里插入图片描述

  5. 图像的顶帽操作:
    顶帽操作是原图像与开图像之间的差值图像,可以用来分离比临近点亮一些的斑块。
    在这里插入图片描述

  6. 图像的黑帽操作:
    黑帽操作是闭操作图像与源图像的差值图像,可以用来分离比临近点暗一些的斑块。
    在这里插入图片描述

  7. 实验代码:

    #include <opencv2/opencv.hpp>
    #include <iostream>
    
    using namespace std;
    using namespace cv;
    
    int main()
    {
    
    	Mat src,open,close,grandient,top_hat,black_hat,kernel;
    	src = imread("C:/Users/he104/Desktop/bintest.jpg");
    	if (src.empty())
    	{
    		cout << "could not load the image..." << endl;
    		return -1;
    	}
    	namedWindow("orign_image", CV_WINDOW_AUTOSIZE);
    	imshow("orign_image", src);
    	kernel = getStructuringElement(MORPH_RECT, Size(10, 10), Point(-1, -1));
    	morphologyEx(src, open, CV_MOP_OPEN, kernel);
    	morphologyEx(src, close, CV_MOP_CLOSE, kernel);
    	morphologyEx(src, grandient, CV_MOP_GRADIENT, kernel);
    	morphologyEx(src, top_hat, CV_MOP_TOPHAT, kernel);
    	morphologyEx(src, black_hat, CV_MOP_BLACKHAT, kernel);
    	imshow("open_image", open);
    	imshow("colse_image", close);
    	imshow("grandient_image", grandient);
    	imshow("top_hat_image", top_hat);
    	imshow("black_hat_image", black_hat);
    	waitKey(0);
    	destroyAllWindows();
    	return 0;
    
    }
    
2017-11-10 14:19:27 u013162035 阅读数 802
  • OpenCV3.2 Java图像处理视频学习教程

    OpenCV3.2 Java图像处理视频培训课程:基于OpenCV新版本3.2.0详细讲述Java OpenCV图像处理部分内容,包括Mat对象使用、图像读写、 基于常用核心API讲述基本原理、使用方法、参数、代码演示、图像处理思路与流程讲授。主要内容包括opencv像素操作、滤波、边缘提取、直线与圆检测、形态学操作与分水岭、图像金子塔融合重建、多尺度模板匹配、opencv人脸检测、OpenCV跟Tomcat使用实现服务器端图像处理服务。

    4127 人正在学习 去看看 贾志刚

1.3形态学图像处理:膨胀与腐蚀

1.3.1理论与概念讲解

<1>形态学概述
形态学(morphology)一词通常表示生物学的一个分支,该分支主要研究动植物的形态和结构。而我们图像处理中指的形态学,往往表示的是数学形态学。下面一起来了解数学形态学的概念。
数学形态学(Mathematical morphology) 是一门建立在格论和拓扑学基础之上的图像分析学科,是数学形态学图像处理的基本理论。其基本的运算包括:二值腐蚀和膨胀、二值开闭运算、骨架抽取、极限腐蚀、击中击不中变换、形态学梯度、Top-hat变换、颗粒分析、流域变换、灰值腐蚀和膨胀、灰值开闭运算、灰值形态学梯度等。
简单来讲,形态学操作就是基于形状的一系列图像处理操作。opencv为进行图像的形态学变换提供了快捷、方便的函数。最基本的形态学操作有二种,他们是:膨胀与腐蚀(Dilation与Erosion)。
膨胀与腐蚀能实现多种多样的功能,主要如下:
 消除噪声;
 分割(isolate)出独立的图像元素,在图像中连接(join)相邻的元素;
 寻找图像中的明显的极大值区域或极小值区域;
 求出图像的梯度。

我们在这里给出下文会用到的,用于对比膨胀与腐蚀运算的“i”字样毛笔字原图:

这里写图片描述

图1

【注】图片来自OpenCV_Tutorials网站。
在进行腐蚀和膨胀的讲解之前,首先需要注意,腐蚀和膨胀是对白色部分(高亮部分)而言的,不是黑色部分。膨胀就是图像中的高亮部分进行膨胀,“领域扩张”,效果图拥有比原图更大的高亮区域。腐蚀就是原图中的高亮部分被腐蚀,“领域被蚕食”,效果图拥有比原图更小的高亮区域。
<2>膨胀
其实,膨胀就是求局部最大值的操作。按数学方面来说,膨胀或者腐蚀操作就是将图像(或图像的一部分区域,我们称之为A)与核(我们称之为B)进行卷积。
核可以是任何的形状和大小,它拥有一个单独定义出来的参考点,我们称其为锚点(anchorpoint)。多数情况下,核是一个小的中间带有参考点和实心正方形或者圆盘,其实,我们可以把核视为模板或者掩码。
而膨胀就是求局部最大值的操作,核B与图形卷积,即计算核B覆盖的区域的像素点的最大值,并把这个最大值赋值给参考点指定的像素。这样就会使图像中的高亮区域逐渐增长。如下图所示,这就是膨胀操作的初衷。
这里写图片描述

图2

膨胀的数学表达式:

这里写图片描述

膨胀效果图(毛笔字):
这里写图片描述

图3 左图像:原始图像反向,右图像: 原始图像反向的膨胀图像

<3>腐蚀
再来看一下腐蚀,大家应该知道,膨胀和腐蚀是一对好基友,是相反的一对操作,所以腐蚀就是求局部最小值的操作。
我们一般都会把腐蚀和膨胀对应起来理解和学习。下文就可以看到,两者的函数原型也是基本上一样的。
原理图:
这里写图片描述

图4

腐蚀的数学表达式:
这里写图片描述

腐蚀效果图(毛笔字):
这里写图片描述

图5左图像:原始图像反向,右图像: 原始图像反向造成的侵蚀

1.3.2 OpenCV源码分析

/*【erode ( )函数源代码】*************************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的对应版本源码完全一样,均在对应的安装目录下)  
 * @源码路径:…\opencv\sources\modules\imgproc\src\morph.cpp
 * @起始行数:1743行   
********************************************************************************/
void cv::erode( InputArray src, OutputArray dst, InputArray kernel,
                Point anchor, int iterations,
                int borderType, const Scalar& borderValue )
{
//调用morphOp函数,并设定标识符为MORPH_ERODE  
    morphOp( MORPH_ERODE, src, dst, kernel, anchor, iterations, borderType, borderValue );
}

/*【dilate( )函数源代码】*************************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的对应版本源码完全一样,均在对应的安装目录下)  
 * @源码路径:…\opencv\sources\modules\imgproc\src\morph.cpp
 * @起始行数:1751行   
********************************************************************************/
void cv::dilate( InputArray src, OutputArray dst, InputArray kernel,
                 Point anchor, int iterations,
                 int borderType, const Scalar& borderValue )
{
//调用morphOp函数,并设定标识符为MORPH_DILATE  
    morphOp( MORPH_DILATE, src, dst, kernel, anchor, iterations, borderType, borderValue );
}

可以发现erode和dilate这两个函数内部就是调用了一下morphOp,只是他们调用morphOp时,第一个参数标识符不同,一个为MORPH_ERODE(腐蚀),一个为MORPH_DILATE(膨胀)。
morphOp函数的源码在… opencv\sources\modules\imgproc\src\morph.cpp中的第1677行,有兴趣的朋友们可以研究研究,这里就不费时费力花篇幅展开分析了。
【注】笔者分析的是3.0.0源码,对于3.0版本以上的源码大同小异,请读者自行对比学习。

1.3.3 API函数讲解

<1>形态学膨胀——dilate函数
erode函数,使用像素邻域内的局部极大运算符来膨胀一张图片,从src输入,由dst输出。支持就地(in-place)操作。

C++: void dilate( InputArray src,  
                  OutputArray dst,  
                  InputArray kernel,  
                  Point anchor=Point(-1,-1),  
                  int iterations=1,  
                  int borderType=BORDER_CONSTANT,  
                  const Scalar& borderValue=morphologyDefaultBorderValue()   );  

【参数】
第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。图像通道的数量可以是任意的,但图像深度应为CV_8U,CV_16U,CV_16S,CV_32F或 CV_64F其中之一。
第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。
第三个参数,InputArray类型的kernel,膨胀操作的核。若为NULL时,表示的是使用参考点位于中心3x3的核。
我们一般使用函数 getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。
其中,getStructuringElement函数的第一个参数表示内核的形状,我们可以选择如下三种形状之一:
 矩形: MORPH_RECT
 交叉形: MORPH_CROSS
 椭圆形: MORPH_ELLIPSE

而getStructuringElement函数的第二和第三个参数分别是内核的尺寸以及锚点的位置。我们一般在调用erode以及dilate函数之前,先定义一个Mat类型的变量来获得getStructuringElement函数的返回值。对于锚点的位置,有默认值Point(-1,-1),表示锚点位于中心。且需要注意,十字形的element形状唯一依赖于锚点的位置。而在其他情况下,锚点只是影响了形态学运算结果的偏移。
getStructuringElement函数相关的调用示例代码如下:

int g_nStructElementSize = 3; //结构元素(内核矩阵)的尺寸  
//获取自定义核  
Mat element = getStructuringElement(MORPH_RECT,  
    Size(2*g_nStructElementSize+1,2*g_nStructElementSize+1),  
    Point( g_nStructElementSize, g_nStructElementSize ));  

调用这样之后,我们便可以在接下来调用erode或dilate函数时,第三个参数填保存了getStructuringElement返回值的Mat类型变量。对应于我们上面的示例,就是填element变量。
第四个参数,Point类型的anchor,锚的位置,其有默认值(-1,-1),表示锚位于中心。
第五个参数,int类型的iterations,迭代使用erode()函数的次数,默认值为1。
第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_DEFAULT。
第七个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释。
使用erode函数,一般我们只需要填前面的三个参数,后面的四个参数都有默认值。而且往往结合getStructuringElement一起使用。
调用范例:

//载入原图   
Mat image = imread("1.jpg");  
//获取自定义核  
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));  
Mat out;  
//进行膨胀操作  
dilate(image, out, element);  

用上面核心代码架起来的完整程序代码。
参见附件【demo1】
运行截图。

这里写图片描述

图6膨胀

<2>形态学腐蚀——erode函数
erode函数,使用像素邻域内的局部极小运算符来腐蚀一张图片,从src输入,由dst输出。支持就地(in-place)操作。

C++: void erode(InputArray src,  
                OutputArray dst,  
                InputArray kernel,  
                Point anchor=Point(-1,-1),  
                int iterations=1,  
                int borderType=BORDER_CONSTANT,  
                const Scalar& borderValue=morphologyDefaultBorderValue()  
 );  

【参数】
第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。图像通道的数量可以是任意的,但图像深度应为CV_8U,CV_16U,CV_16S,CV_32F或 CV_64F其中之一。
第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。
第三个参数,InputArray类型的kernel,腐蚀操作的内核。若为NULL时,表示的是使用参考点位于中心3x3的核。我们一般使用函数 getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。(具体看上文中浅出部分dilate函数的第三个参数讲解部分)
第四个参数,Point类型的anchor,锚的位置,其有默认值(-1,-1),表示锚位于单位(element)的中心,我们一般不用管它。
第五个参数,int类型的iterations,迭代使用erode()函数的次数,默认值为1。
第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_DEFAULT。
第七个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释。
同样的,使用erode函数,一般我们只需要填前面的三个参数,后面的四个参数都有默认值。而且往往结合getStructuringElement一起使用。
调用范例:

//载入原图   
Mat image = imread("1.jpg");  
//获取自定义核  
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));  
Mat out;  
//进行腐蚀操作  
 erode(image,out, element);  

用上面核心代码架起来的完整程序代码。
参见附件【demo2】
运行结果:

这里写图片描述

图7腐蚀操作

1.3.4膨胀与腐蚀综合实例

这个示例程序中的效果图窗口有两个滚动条,顾名思义,第一个滚动条“腐蚀/膨胀”用于在腐蚀/膨胀之间进行切换;第二个滚动条”内核尺寸”用于调节形态学操作时的内核尺寸,以得到效果不同的图像。
参见附件【demo3】
放出一些效果图吧。

这里写图片描述

图8

这里写图片描述

图9

参考链接:

英文:https://docs.opencv.org/master/db/df6/tutorial_erosion_dilatation.html
中文:
http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/erosion_dilatation/erosion_dilatation.html#morphology-1

1.4形态学图像处理:开运算、闭运算、形态学梯度、顶帽、黑帽合辑

1.4.1理论与概念讲解

首先呢,要知道形态学的高级形态,往往都是建立在腐蚀和膨胀这两个基本操作之上的。而关于腐蚀和膨胀,概念和细节以及相关代码可以看前文。对膨胀和腐蚀心中有数了,接下来的高级形态学操作,应该就不难理解。另外,为了下面对比和演示以及理解的方便,笔者自己制作了一张毛笔字图,这里先上原图:

这里写图片描述

图10

<1>开运算(Opening Operation)
开运算(Opening Operation),其实就是先腐蚀后膨胀的过程。其数学表达式如下:
dst=open(src,element)=dilate(erode(src,element))

开运算可以用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。

这里写图片描述

图11左图像:原始图像反向,右图像: 原始图像反向的开运算

<2>闭运算(Closing Operation)
先膨胀后腐蚀的过程称为闭运算(Closing Operation),其数学表达式如下:
dst=close(src,element)=erode(dilate(src,element))

闭运算能够排除小型黑洞(黑色区域)。效果图如下所示:

这里写图片描述

图12左图像:原始图像反向,右图像: 原始图像反向的闭运算

<3>形态学梯度(MorphologicalGradient)
形态学梯度(Morphological Gradient)为膨胀图与腐蚀图之差,数学表达式如下:
dst=morphgrad(src,element)=dilate(src,element)erode(src,element)

对二值图像进行这一操作可以将团块(blob)的边缘突出出来。我们可以用形态学梯度来保留物体的边缘轮廓,如下所示:

这里写图片描述

图13

<4>顶帽(Top Hat)
顶帽运算(Top Hat)又常常被译为”礼帽“运算。为原图像与上文刚刚介绍的“开运算“的结果图之差,数学表达式如下:
dst=tophat(src,element)=srcopen(src,element)

因为开运算带来的结果是放大了裂缝或者局部低亮度的区域,因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作和选择的核的大小相关。
顶帽运算往往用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。

这里写图片描述

图14

<5> 黑帽(Black Hat)
黑帽(Black Hat)运算为”闭运算“的结果图与原图像之差。数学表达式为:
dst=blackhat(src,element)=close(src,element)src

黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小相关。所以,黑帽运算用来分离比邻近点暗一些的斑块。非常完美的轮廓效果图:

这里写图片描述

图15

1.4.2 OpenCV源码分析

本文的主角是OpenCV中的morphologyEx函数,它利用基本的膨胀和腐蚀技术,来执行更加高级的形态学变换,如开闭运算,形态学梯度,“顶帽”、“黑帽”等等。这一节我们来一起看一下morphologyEx函数的源代码。

/*【morphologyEx ( )函数源代码】******************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的对应版本源码完全一样,均在对应的安装目录下)  
 * @源码路径:…\opencv\sources\modules\imgproc\src\morph.cpp
 * @起始行数:1822行   
********************************************************************************/
void cv::morphologyEx( InputArray _src, OutputArray _dst, int op,
                       InputArray _kernel, Point anchor, int iterations,
                       int borderType, const Scalar& borderValue )
{
    Mat kernel = _kernel.getMat();
    if (kernel.empty())
    {
        kernel = getStructuringElement(MORPH_RECT, Size(3,3), Point(1,1));
    }
#ifdef HAVE_OPENCL
    Size ksize = kernel.size();
    anchor = normalizeAnchor(anchor, ksize);

    CV_OCL_RUN(_dst.isUMat() && _src.dims() <= 2 && _src.channels() <= 4 &&
        anchor.x == ksize.width >> 1 && anchor.y == ksize.height >> 1 &&
        borderType == cv::BORDER_CONSTANT && borderValue == morphologyDefaultBorderValue(),
        ocl_morphologyEx(_src, _dst, op, kernel, anchor, iterations, borderType, borderValue))
#endif

    Mat src = _src.getMat(), temp; //拷贝Mat数据到临时变量  
    _dst.create(src.size(), src.type());
    Mat dst = _dst.getMat();
//一个大switch,根据不同的标识符取不同的操作  
    switch( op )
    {
    case MORPH_ERODE:
        erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
        break;
    case MORPH_DILATE:
        dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
        break;
    case MORPH_OPEN:
        erode( src, dst, kernel, anchor, iterations, borderType, borderValue );
        dilate( dst, dst, kernel, anchor, iterations, borderType, borderValue );
        break;
    case CV_MOP_CLOSE:
        dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
        erode( dst, dst, kernel, anchor, iterations, borderType, borderValue );
        break;
    case CV_MOP_GRADIENT:
        erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
        dilate( src, dst, kernel, anchor, iterations, borderType, borderValue );
        dst -= temp;
        break;
    case CV_MOP_TOPHAT:
        if( src.data != dst.data )
            temp = dst;
        erode( src, temp, kernel, anchor, iterations, borderType, borderValue );
        dilate( temp, temp, kernel, anchor, iterations, borderType, borderValue );
        dst = src - temp;
        break;
    case CV_MOP_BLACKHAT:
        if( src.data != dst.data )
            temp = dst;
        dilate( src, temp, kernel, anchor, iterations, borderType, borderValue );
        erode( temp, temp, kernel, anchor, iterations, borderType, borderValue );
        dst = temp - src;
        break;
    default:
        CV_Error( CV_StsBadArg, "unknown morphological operation" );
    }
}

看上面的源码可以发现,其实morphologyEx函数其实就是内部一个大switch而已。根据不同的标识符取不同的操作。比如开运算MORPH_OPEN,按我们上文中讲解的数学表达式,就是先腐蚀后膨胀,即依次调用erode和dilate函数,为非常简明干净的代码。
【注】笔者分析的是2.4.9源码,对于3.0版本以上的源码大同小异,请读者自行对比学习。

1.4.3 API函数讲解

<1> morphologyEx函数详解
上面我们已经讲到,morphologyEx函数利用基本的膨胀和腐蚀技术,来执行更加高级形态学变换,如开闭运算,形态学梯度,“顶帽”、“黑帽”等等。这一节我们来了解它的参数意义和使用方法。

C++: void morphologyEx( InputArray src,  
                        OutputArray   dst,  
                        int op,  
                        InputArray kernel,  
                        Pointanchor=Point(-1,-1),  
                        int iterations=1,  
                        int borderType=BORDER_CONSTANT,  
                        constScalar& borderValue=morphologyDefaultBorderValue() );  

【参数】
第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。图像位深应该为以下五种之一:CV_8U, CV_16U,CV_16S, CV_32F 或CV_64F。
第二个参数,OutputArray类型的dst,即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。
第三个参数,int类型的op,表示形态学运算的类型,可以是如下之一的标识符:
 MORPH_OPEN – 开运算(Opening operation)
 MORPH_CLOSE – 闭运算(Closing operation)
 MORPH_GRADIENT -形态学梯度(Morphological gradient)
 MORPH_TOPHAT - “顶帽”(“Top hat”)
 MORPH_BLACKHAT - “黑帽”(“Black hat“)
另有CV版本的标识符也可选择,如CV_MOP_CLOSE,CV_MOP_GRADIENT,CV_MOP_TOPHAT,CV_MOP_BLACKHAT,这应该是OpenCV1.0系列版本遗留下来的标识符,和上面的“MORPH_OPEN”一样的效果。
第四个参数,InputArray类型的kernel,形态学运算的内核。若为NULL时,表示的是使用参考点位于中心3x3的核。我们一般使用函数 getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。关于getStructuringElement我们上篇文章中讲过了,这里为了大家参阅方便,再写一遍:
其中,getStructuringElement函数的第一个参数表示内核的形状,我们可以选择如下三种形状之一:
 矩形: MORPH_RECT
 交叉形: MORPH_CROSS
 椭圆形: MORPH_ELLIPSE
而getStructuringElement函数的第二和第三个参数分别是内核的尺寸以及锚点的位置。
我们一般在调用erode以及dilate函数之前,先定义一个Mat类型的变量来获得getStructuringElement函数的返回值。对于锚点的位置,有默认值Point(-1,-1),表示锚点位于中心。且需要注意,十字形的element形状唯一依赖于锚点的位置。而在其他情况下,锚点只是影响了形态学运算结果的偏移。
getStructuringElement函数相关的调用示例代码如下:

int g_nStructElementSize = 3; //结构元素(内核矩阵)的尺寸  
//获取自定义核  
Mat element =getStructuringElement(MORPH_RECT,  
       Size(2*g_nStructElementSize+1,2*g_nStructElementSize+1),  
       Point(g_nStructElementSize, g_nStructElementSize ));  

调用这样之后,我们便可以在接下来调用erode、dilate或morphologyEx函数时,kernel参数填保存getStructuringElement返回值的Mat类型变量。对应于我们上面的示例,就是填element变量。
第五个参数,Point类型的anchor,锚的位置,其有默认值(-1,-1),表示锚位于中心。
第六个参数,int类型的iterations,迭代使用函数的次数,默认值为1。
第七个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_ CONSTANT。
第八个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释。
其中的这些操作都可以进行就地(in-place)操作。且对于多通道图像,每一个通道都是单独进行操作。 OK,讲解完毕,下面就是使用的范例。
为了方便大家需要的时候随时取用。下面我们依次列举出开运算,闭运算,形态学梯度,顶帽,黑帽,腐蚀,膨胀的效果实现简化版完整代码。其实说白了,这些代码基本上内容一致,其实就是改一下morphologyEx里面的第三个标识符参数而已。核都是选的MORPH_RECT,矩形元素结构。另外,通过看源码我们发现,最基本的腐蚀和膨胀操作也可以用morphologyEx函数来实现,他们由morphologyEx函数源码中switch的前两个case来实现(虽然在case体内就是简单地各自调用了一下erode和dilation函数,但还是有写出来的必要)。所以在这里,我们也用morphologyEx再重新来实现一遍他们。
按着顺序来列出吧,就直接列详细注释好的代码和运行结果了。
<2>开运算示例程序
OpenCV中调用morphologyEx函数进行开运算操作的示例程序如下:
参考附件【demo4】,运行效果图。

这里写图片描述

图16

<3>闭运算示例程序
OpenCV中调用morphologyEx函数进行闭运算操作的示例程序如下。
参见附件【demo5】,运行效果图。
这里写图片描述

图17

<4>形态学梯度示例程序
OpenCV中调用morphologyEx函数进行形态学梯度操作的示例程序如下。
参见附件【demo6】,运行效果图。
这里写图片描述

图18

<5>顶帽运算(Top Hat)示例程序
OpenCV中调用morphologyEx函数进行顶帽运算操作的示例程序如下。
参见附件【demo7】,运行效果图。
这里写图片描述

图19

<6>黑帽运算(BlackHat)示例程序
OpenCV中调用morphologyEx函数进行黑帽运算操作的示例程序如下:
参见附件【demo8】,运行效果图。
这里写图片描述

图20

<7>腐蚀(morphologyEx调用版)示例程序
OpenCV中调用morphologyEx函数进行腐蚀操作的示例程序如下。
参见附件【demo9】,运行效果图。
这里写图片描述

图21

<8>膨胀(morphologyEx调用版)示例程序
OpenCV中调用morphologyEx函数进行膨胀操作的示例程序如下。
参见附件【demo10】,运行效果图。
这里写图片描述

图22

1.4.4形态学滤波综合实例

这个示例程序中,一共有四个显示图像的窗口。原始图一个,开/闭运算为一个,腐蚀/膨胀为一个,顶帽/黑帽运算为一个。分别使用滚动条,来控制得到的形态学效果。且迭代值为10的时候,为中间。另外,还可以通过键盘按键1,2,3以及空格,来调节成不同的元素结构(矩形、椭圆、十字形)。
参看附件【demo11】

这里写图片描述

图23腐蚀/膨胀效果图

这里写图片描述

图24开/闭运算效果图

这里写图片描述

图25顶帽/黑帽运算效果图

有没有感觉很酷呢,继续跟着博主继续学习吧,你会发现不一样的惊奇哟!

参考链接:

英文:https://docs.opencv.org/master/d3/dbe/tutorial_opening_closing_hats.html
中文:
http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/opening_closing_hats/opening_closing_hats.html#morphology-2

本章附件:

请点击代码链接

【注意】博主在附件中的代码只有Linux版本的,如何使用Windows使用该代码请参看博主的另一篇博文
Opencv环境搭建(Visual Studio+Windows)- 请点击
有任何问题请联系博主。

2019-08-16 12:44:14 qq_37394634 阅读数 178
  • OpenCV3.2 Java图像处理视频学习教程

    OpenCV3.2 Java图像处理视频培训课程:基于OpenCV新版本3.2.0详细讲述Java OpenCV图像处理部分内容,包括Mat对象使用、图像读写、 基于常用核心API讲述基本原理、使用方法、参数、代码演示、图像处理思路与流程讲授。主要内容包括opencv像素操作、滤波、边缘提取、直线与圆检测、形态学操作与分水岭、图像金子塔融合重建、多尺度模板匹配、opencv人脸检测、OpenCV跟Tomcat使用实现服务器端图像处理服务。

    4127 人正在学习 去看看 贾志刚

个人博客:http://www.chenjianqu.com/

原文链接:http://www.chenjianqu.com/show-10.html

数学形态学

数学形态学(Mathematical morphology) 是一门建立在格论和拓扑学基础之上的图像分析学科,是数学形态学图像处理的基本理论。其基本的运算包括:腐蚀和膨胀、开运算和闭运算、骨架抽取、极限腐蚀、击中击不中变换、形态学梯度、Top-hat变换、颗粒分析、流域变换等。

数学形态学操作可以分为二值形态学和灰度形态学,灰度形态学由二值形态学扩展而来。数学形态学有2个基本的运算,即腐蚀和膨胀,而腐蚀和膨胀通过结合又形成了开运算和闭运算。 开运算就是先腐蚀再膨胀,闭运算就是先膨胀再腐蚀。本文介绍二值形态学变换。

 

二值形态学变换

二值形态学变换就是对二值图像进行数学形态学操作。常用操作有腐蚀、膨胀、开运算、闭运算等。腐蚀和膨胀是对白色部分(高亮部分)而言的,不是黑色部分。 膨胀就是图像中的高亮部分进行膨胀,“邻域扩张”,效果图拥有比原图更大的高亮区域。腐蚀就是原图中高亮部分被腐蚀,“邻域被蚕食”,效果图拥有比原图更小的高亮区域。

 

腐蚀

腐蚀操作会收缩(细化)图像中物体的轮廓,可以用来断开(分离)物体间的连接,消除离散点,代价是导致物体的面积比原来的面积要小。。

腐蚀的数学表达式为:

1.png

该式子表示用结构B腐蚀A,需要在B中定义一个参考点。B像在A上面移动(如图像卷积操作一般)。

1. 用结构元素,扫描图像的每一个像素

2. 用结构元素与其覆盖的二值图像做“与”操作

3. 如果都为1,结果图像的该像素为1。否则为0

也就是查找被处理图像中能不能找到和结构元素相同的矩阵。如果存在那么中心点所对应的点就为1,否则为0.下图为腐蚀操作的示意图:

2.png

 

代码实现:

Mat ErosionTransform(Mat &src,int kernelSize)
{
    Mat dst(src.rows, src.cols, src.type(), Scalar(0));
    Mat kernel = Mat::ones(kernelSize, kernelSize, CV_8UC1);
    if (src.channels() == 1) {
        int rowsSub = int(kernel.rows / 2);
        int colsSub = int(kernel.cols / 2);
        for (int i = rowsSub; i < src.rows-rowsSub; i++) {
            for (int j = colsSub; j < src.cols-colsSub; j++)
            {
                int flag = 0;
                for (int ki = 0; ki < kernel.rows; ki++)
                {
                    for (int kj = 0; kj < kernel.cols; kj++)
                    {
                            int i_ = i+ki - rowsSub;
                            int j_ = j+kj - colsSub;
                            if (src.at<uchar>(i_, j_) == 0 && kernel.at<uchar>(ki, kj) >0)
                                flag = 1;
                    }
                }
                if(!flag)
                    dst.at<uchar>(i, j) = 255;
                else
                    dst.at<uchar>(i, j) = 0;
            }
        }
    }
    return dst;
}

说明: 

当输入的核是一个3x3的全1矩阵时,运行效果图如下:

3.png

 

膨胀

膨胀操作会扩大(粗化)图像中物体的轮廓,可以用来弥补(填充)物体间的孔洞,强化离散点,代价是导致物体的面积比原来的面积要大。

膨胀的公式如下:

4.png

膨胀是和腐蚀运算相反的操作,膨胀的步骤如下:

1. 用结构元素,扫描图像的每一个像素

2. 用结构元素与其覆盖的二值图像做“与”操作

3. 如果都为0,结果图像的该像素为0。否则为1

也就是在结构元素覆盖范围下,只要有一个像素符和结构元素像素相同,那么中心点对应点就为1,否则为0.

5.png

代码实现如下:

Mat DialateTransform(Mat &src, int kernelSize)
{
    Mat dst(src.rows, src.cols, src.type(), Scalar(0));
    Mat kernel = Mat::ones(kernelSize, kernelSize, CV_8UC1);
    if (src.channels() == 1) {
        int rowsSub = int(kernel.rows / 2);
        int colsSub = int(kernel.cols / 2);
        for (int i = rowsSub; i < src.rows - rowsSub; i++) {
            for (int j = colsSub; j < src.cols - colsSub; j++)
            {
                int flag = 0;
                for (int ki = 0; ki < kernel.rows; ki++)
                {
                    for (int kj = 0; kj < kernel.cols; kj++)
                    {
                        int i_ = i + ki - rowsSub;
                        int j_ = j + kj - colsSub;
                        if (src.at<uchar>(i_, j_) >0 && kernel.at<uchar>(ki, kj) >0)
                            flag = 1;
                    }
                }
                if (!flag)
                    dst.at<uchar>(i, j) =0;
                else
                    dst.at<uchar>(i, j) = 255;
            }
        }
    }
    return dst;
}

当kernelSize=3时,运行效果图如下:

6.png

 

 

 

闭运算

闭运算是使用同一结构元对图像进行先膨胀后腐蚀的操作,可以用来弥合较窄的间断和细长的沟壑,消除物体间小的孔洞,填补轮廓线中的断裂。

闭运算=先进行膨胀运算,在进行腐蚀运算,其运行示意图如下,

7.png

 

代码实现:

Mat ClosingTransform(Mat &src, int kernelSize)
{
    Mat dialateImg = DialateTransform(src, kernelSize);
    return ErosionTransform(dialateImg, kernelSize);
}

8.png

 

开运算

开运算是使用同一结构元对图像进行先腐蚀后膨胀的操作,可以用来平滑物体的轮廓,断开物体间较窄的连接,消除物体边沿尖锐的突出部分。

开运算 = 先腐蚀运算,再膨胀运算.示意图如下:

9.png

代码实现:

Mat OpeningTransform(Mat &src, int kernelSize)
{
    Mat erosionImg = ErosionTransform(src, kernelSize);
    return DialateTransform(erosionImg, kernelSize);
}

kernelSize=5

10.png

 

 

参考文献

[1] 百度百科.数学形态学.https://baike.baidu.com/item/%E6%95%B0%E5%AD%A6%E5%BD%A2%E6%80%81%E5%AD%A6/2594174.

[2]CSDN博客:chaolei_9527. 数学形态学运算——腐蚀、膨胀、开运算、闭运算.https://blog.csdn.net/chaolei3/article/details/79618602.2018-03-20

[3]CSDN博客:青雲-吾道乐途. 膨胀与腐蚀的彻底击破.https://blog.csdn.net/qq_37059483/article/details/77878829.2017-09-07

[4]CSDN博客:HanShanBuLeng. 形态学应用——图像开运算与闭运算.https://blog.csdn.net/hanshanbuleng/article/details/80657148.2018-06-11

 

图像形态学

阅读数 2511

没有更多推荐了,返回首页