心得经验总结:数字图像处理

简介: 心得经验总结:数字图像处理

形态学变化本质是数学上一个分支,是基于形状的一系列图像处理操作。

一、基础理论

形态学的主要用途是获取物体拓扑和结果信息,通过物体结构一系列运算,得到物体更本质的形态。在图像处理的主要应用有:

1、利用形态学处理图像,用于改善图像质量目的,比如通过原图像减去顶冒图像用于提高对比度

2、描述和定义图像的各种几何参数和特征,如面积、连通、颗粒度、骨架和方向性。

1.1 腐蚀和膨胀

腐蚀和膨胀是对二维图片的进行操作的形态学运算,简单来讲形态学操作就是基于形状的一系列图像处理操作,通过将结构元素作用于输入图像来产生输出图像。腐蚀(Erosion)和膨胀(Dilation)是最基本的形态学操作,他们运用广泛主要有:

1、消除噪声

2、 分割(ioslate)独立的图像元素以及连接(join)相邻的元素

3、 寻找图像中的明显的极大值区域或极小值区域

腐蚀是将构建的内核B对原图像SRC进行卷积,将内核B覆盖区域内的最小值提取,代替锚点位置(默认为内核中心点,因为是选取的最小值,所以是白色腐蚀,即白色区域减小。

膨胀是腐蚀的孪生兄弟,操作过程一模一样,区别在于选取覆盖区域的最大值提到锚点位置的像素,所以是白色区域膨胀,扩展白色区域。

其实腐蚀和膨胀是相对于值的选取,腐蚀则为选取最小值,膨胀选取最大值,对于数字图像则是相对于白色区域,腐蚀就是腐蚀白色区域的,膨胀则是膨胀白色区域的。谨记白色代表背景还是前景。

(src) (膨胀-dilate) (腐蚀-erode)

void dilate( InputArray src,

OutputArray dst,

InputArray kernel,

Point anchor = Point(-1,-1),

int iterations = 1,

int borderType = BORDER_CONSTANT,

const Scalar borderValue = morphologyDefaultBorderValue() )

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: 输出图像,与输入图像尺寸类型一致。

. Input Array kernel: 用于腐蚀操作的kernel,当参数=Mat()即NULL时,kernel是一个锚点位于中心的3x3模板。可以通过getStructuringElement函数来制定kernel的形状和尺寸,具体用法请参考对膨胀的表述

. Point anchor = Point(-1, -1): 锚点位置

. int iterations = 1: 迭代腐蚀操作次数,有默认值1

. int borderType = BORDER_CONSTANT: 用于推断图像外部像素的某种边界模式,其有默认值BORDER_CONSTANT,可以通过cv::BorderTypes查询其他的方法。

. const Scalar borderValue = morphologyDefaultBorderValue(): 边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般不用进行设置,如果有特殊需要,可以查看createMorphologyFilter()函数得到详细信息。

应用讲解:腐蚀和膨胀经常使用,基本是对二值化图像进行操作,但两者皆是需要组合使用,以去除噪点和连通区域,但后续出现了形态学运算,此函数应用较少了。

1.2 开运算和闭运算

开运算是通过先对图像腐蚀再膨胀实现。

1、开运算(Opening Operation) = 先腐蚀,后膨胀

dst=open(src,element)=dilate(erode(src,element))

腐蚀和膨胀的原理前期讲解过,假设背景是白色区域(较明亮),开运算则是先腐蚀白色,意味前景区域连通了些,后又膨胀,意味白色膨胀,可能将一些噪点去除。结果是删除了不属于结构元素的对象区域,平滑了轮廓,断开了狭窄的连接,去掉细小的突出部分。

左图是原图像,右图是采用开运算转换之后的结果图,可以发现字母拐弯处的白色空间消失。

从上面两幅图像可以看出,开运算以腐蚀为主,即第一步为主,连通了区域,将不想关区域最后去掉。

2、闭运算(Closing Operation) = 先膨胀,后腐蚀

dst=close(src,element)=erode(dilate(src,element))

闭运算当然是开运算相反操作,即先膨胀,后腐蚀。能够排除小型黑洞(黑色区域),能够平滑对象的轮廓,但是与开运算不同的是闭运算一般会将狭窄的缺口连接起来形成细长的弯口,并填充比结构元素小的洞。

能够排除小型黑洞(黑色区域)。

综上所述,对于开运算和闭运算,比较侧重第一步,开和闭相对于白色来讲,开就是是白色区域断开点(侧重腐蚀,即开运算第一步),闭则相反。

在应用中首先确定目标为白色还是黑色,然后根据是连通还是去燥,最后确定开运算还是闭运算。

1.3 形态学梯度

形态梯度是膨胀图与腐蚀图之差,其操作原理表达式如下:

形态学梯度(Morphological Gradient) = 膨胀图 - 腐蚀图

dst=morph_grad(src,element)=dilate(src,element)?erode(src,element)

形态学梯度很容易理解,公式表述为膨胀图-腐蚀图,就是利用白色膨胀,另一幅图像腐蚀,本质就是目标边缘一个左移,一个右移,相减生成里差值图像,即为边缘图像。

因为opencv中有很多获取边缘的函数,则这个功能很少应用。

1.4 顶帽和黑帽

顶帽操作是原图像与开运算结果图之差。

顶帽(Top Hat) = 原图 - 开运算图

dst=tophat(src,element)=src?open(src,element)

开运算的结果是白色区域断开,因此从原图中减去开运算后的图得到的效果图能够突出比原图轮廓周围的区域更明亮的区域,且这一操作与选择的核的大小有关。

顶帽操作往往用来分离比邻近点亮一些的板块,在一幅图像具有大幅背景而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。

在图像轮廓有较为明亮区域,如果加上原图,可以增加边缘区域的对比度。

黑帽(Black Hat) = 闭运算图 - 原图

dst=blackhat(src,element)=close(src,element)?src

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

二、open函数介绍

opencv中提供了形态学操作函数morphologyEx()来实现开运算、闭运算、形态学梯度、顶帽、黑帽等五种相对高级的操作,也可以实现膨胀核腐蚀两种基本的形态学操作。

2.1 函数详解

void cv::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()

)

. InputArray src: 输入图像,可以是Mat类型,对于图像通道数无要求,但图像深度必须是CV_8U、CV_16U、CV_16S、CV_32F或CV_64F

. OutPutArray dst: 目标图像,与原图像尺寸核类型相同

. int op: 形态学运算的类型,可以通过MorphTypes查看,如下所示:

标识符 | 运算类型

MORPH_OPEN: 开运算

MORPH_CLOSE :闭运算

MORPH_GRADIENT: 形态学梯度

MORPH_TOPHAT:顶帽运算

MORPH_BLACKHAT: 黑帽运算

MORPH_ERODE :腐蚀运算

MORPH_DILATE :膨胀运算

MORPH_HITMISS: 击中击不中运算(只支持CV_8UC1类型的二值图像)

. InputArray kernel: 形态学运算的内核,如果是Mat()则表示的是参考点位于内核中心3x3的核,前面也提到一般使用前需要定义一个Mat变量结合getStructuringElement()函数使用,getStructuringElement会返回指定形状和尺寸的结构元素,这里再重申一下getStructuringElement的参数,其函数原型如下:

Mat cv::getStructuringElement ( int shape,

Size ksize,

Point anchor = Point(-1,-1)

)

Point anchor=Point(-1, -1): 锚点位置

. int iterations=1: 迭代使用函数的次数,默认值为1

. int borderType=BORDER_CONSTANT: 用于推断图像外部像素的某种边界模式,有默认值BORDER_CONSTANT

. const Scalar borderValue=morphologyDefaultBorderValue(): 当边界为常数时的边界值,可以通过createMorphologyFilter() 查看更多细节。

2.2 源码分析

//

void cv::morphologyEx( InputArray _src,OutputArray _dst, int op,

InputArray kernel, Pointanchor, int iterations,

int borderType, constScalar borderValue )

{

Mat src = _src.getMat(), temp;

_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: //代码效果参考:http://www.zidongmutanji.com/bxxx/191434.html

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" );

}

}

这个函数使用了一个大的switch实现了多种形态学滤波的调用

2.3 示例程序

#coding=utf-8

import cv2

import numpy as np

img = cv2.imread('D:/binary.bmp',0)

#定义结构元素

kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5, 5))

#闭运算

closed = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)

#显示腐蚀后的图像

cv2.imshow("Close",closed);

#开运算

opened = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)

#显示腐蚀后的图像

cv2.imshow("Open", opened);

cv2.waitKey(0) //代码效果参考:http://www.zidongmutanji.com/bxxx/303794.html

cv2.destroyAllWindows()

其实就是改一下morphologyEx里面的第三个标识符参数而已。核都是选的MORPH_RECT,矩形元素结构。另外,通过看源码我们发现,最基本的腐蚀和膨胀操作也可以用morphologyEx函数来实现。

2.4 知识补充

形态学处理的核心就是定义结构元素,在OpenCV-Python中,可以使用其自带的getStructuringElement函数,也可以直接使用NumPy的ndarray来定义一个结构元素。首先来看用getStructuringElement函数定义一个结构元素:

element = cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))

这就定义了一个5×5的十字形结构元素,如下:

也可以用NumPy来定义结构元素,如下:

NpKernel = np.uint8(np.zeros((5,5)))

for i in range(5):

NpKernel【2, i】 = 1 #感谢chenpingjun1990的提醒,现在是正确的

NpKernel【i, 2】 = 1

【【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】】

cv2.getStructuringElement(cv2.MORPH_RECT, (24, 3)) ,输出为:

【【1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1】

【1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1】

【1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1】】

四、参考文献

1、opencv学习(二十四)之腐蚀与膨胀:对腐蚀、膨胀进行了详细的介绍

2、腐蚀与膨胀(Eroding and Dilating):opencv中文教程,详细说明函数细节

3、opencv学习(二十五)之开运算、闭运算、形态梯度、顶帽、黑帽:文中对形态学应用有较清晰的介绍,并详细介绍了各个算法的原理及应用

4、【OpenCV入门教程之十一】 形态学图像处理(二):开运算、闭运算、形态学梯度、顶帽、黑帽合辑:浅墨写的博客,很详细,很全面。

相关文章
|
7月前
|
算法 数据可视化 vr&ar
【图形学】探秘图形学奥秘:区域填充的解密与实战
【图形学】探秘图形学奥秘:区域填充的解密与实战
63 0
|
1月前
|
设计模式 前端开发 算法
探索代码之美:我的编程之旅与实践感悟
【10月更文挑战第23天】 在数字世界的海洋中,编程是构建梦想之船的技艺。本文将带你领略编程的魅力,从我踏入这个奇妙世界的第一步开始,到逐渐掌握各种编程语言和工具的过程。我们将一起探讨编程思维的重要性、解决问题的策略,以及如何通过不断学习和实践来提升自己的技术水平。文章不仅分享了我个人的经验和技巧,还提供了实用的代码示例,旨在帮助初学者更好地理解编程概念,并为资深开发者提供新的视角和灵感。
49 2
|
6月前
|
算法 开发者
代码之美:技术感悟与编程艺术
【6月更文挑战第28天】在数字世界的构建中,代码不仅仅是冷冰冰的指令集合,更是开发者智慧与情感的结晶。本文将深入探讨编程背后的艺术性,揭示如何通过技术感悟提升代码质量,以及在日复一日的编码实践中如何保持创新与热情。
|
6月前
|
算法 程序员
探索代码之美:技术感悟与实践
【6月更文挑战第10天】在编程的海洋中,我们都是探险者。本文将分享我在编程旅程中的一些技术感悟,包括如何理解代码之美、如何提高编程效率以及如何保持对技术的热爱。通过这些感悟,我们可以更好地理解编程的本质,提高我们的技术水平,并享受编程带来的乐趣。
32 3
|
6月前
|
算法 计算机视觉 Python
心得经验总结:数字图像处理
心得经验总结:数字图像处理
30 0
|
7月前
|
算法
技术感悟:代码之美
在当今数字化时代,技术的发展日新月异,而程序设计作为其中的重要一环,更是呈现出无限的魅力。本文通过对代码之美的深入思考,探讨了程序设计背后的艺术和哲学,以及技术在人类生活中的重要性和影响。
36 0
|
7月前
|
设计模式 算法 开发者
代码之美:探索编程艺术与实践的交汇点
【4月更文挑战第2天】 在数字世界的构建中,代码不仅仅是一种工具,它亦是艺术家手中的画笔。本文旨在探讨编程作为一种技术和艺术相结合的领域,揭示那些隐藏在代码背后的美学原则和创造力。我们将从编程的基础出发,逐步深入到设计模式、算法优雅性以及代码的可读性和维护性,最终探讨如何通过技术实现创新并解决问题。文章的目的是为那些渴望在技术实践中寻找创造性和美感的开发者提供灵感和指导。
技术人修炼之道阅读笔记(九)揪头发思维
技术人修炼之道阅读笔记(九)揪头发思维
|
图形学
入行3D建模难不难学习?10年建模师:这3种人,一辈子都学不会
下面这些问题是不是也是你心中所惑?还是说经常听到有人这么说? 这个功能在哪里,我不会呀! 为什么我努力学习了这么久,还是没学会? **这个时代,最不缺的就是勤奋的年轻人。** 大家都希望用宝贵的青春奋力一搏,高山仰止。所以我们经常看到许多人,每天像吃饭一样大口吞咽知识,然而却消化不良。
233 0
入行3D建模难不难学习?10年建模师:这3种人,一辈子都学不会
|
计算机视觉
《数字图像处理》冈萨雷斯版 读书笔记(五)
博主之前保研差一名,去考了个研,许久不更。本次考研准备三个月,三跨(跨学校跨地区跨专业),自学了一波数字图像处理,考完自知不妙,修身养性一个月(实习+打游戏)。现在百无聊赖,正好来分享一下我考研专业课的学习笔记。
1363 0
下一篇
DataWorks