形态变换

opencv之膨胀与腐蚀中介绍了Dilation/Erosion的原理.建议先读这一篇,搞懂原理. 这样就可以很轻松地理解为什么本文的这些形态变换可以取得相应的效果.

基于此,我们可以组合出更多的形态变换以达到不同的目的.

有以下几种:

  • Opening
  • Closing
  • Morphological Gradient
  • Top Hat
  • Black Hat

Opening



先腐蚀再膨胀,可以把较小的目标去除.比如:

Closing



可以把物体内的小黑洞消除.比如:

Morphological Gradient



可以提取出物体的轮廓.

比如下图,腐蚀和膨胀对物体内部的像素影响不大,(内部的局部最大值和最小值差不多),所以做完插值以后,边缘的像素值差比较大,内部像素差值变为0,从而提取出物体轮廓.

Top Hat



Black Hat



from __future__ import print_function
import cv2 as cv
import numpy as np
import argparse
morph_size = 0
max_operator = 4
max_elem = 2
max_kernel_size = 21
title_trackbar_operator_type = 'Operator:\n 0: Opening - 1: Closing \n 2: Gradient - 3: Top Hat \n 4: Black Hat'
title_trackbar_element_type = 'Element:\n 0: Rect - 1: Cross - 2: Ellipse'
title_trackbar_kernel_size = 'Kernel size:\n 2n + 1'
title_window = 'Morphology Transformations Demo'
morph_op_dic = {0: cv.MORPH_OPEN, 1: cv.MORPH_CLOSE, 2: cv.MORPH_GRADIENT, 3: cv.MORPH_TOPHAT, 4: cv.MORPH_BLACKHAT}
def morphology_operations(val):
morph_operator = cv.getTrackbarPos(title_trackbar_operator_type, title_window)
morph_size = cv.getTrackbarPos(title_trackbar_kernel_size, title_window)
morph_elem = 0
val_type = cv.getTrackbarPos(title_trackbar_element_type, title_window)
if val_type == 0:
morph_elem = cv.MORPH_RECT
elif val_type == 1:
morph_elem = cv.MORPH_CROSS
elif val_type == 2:
morph_elem = cv.MORPH_ELLIPSE
element = cv.getStructuringElement(morph_elem, (2*morph_size + 1, 2*morph_size+1), (morph_size, morph_size))
operation = morph_op_dic[morph_operator]
dst = cv.morphologyEx(src, operation, element)
cv.imshow(title_window, dst) src = cv.imread("/home/sc/disk/keepgoing/opencv_test/j.png")
if src is None:
print('Could not open or find the image: ', args.input)
exit(0) cv.namedWindow(title_window)
cv.createTrackbar(title_trackbar_operator_type, title_window , 0, max_operator, morphology_operations)
cv.createTrackbar(title_trackbar_element_type, title_window , 0, max_elem, morphology_operations)
cv.createTrackbar(title_trackbar_kernel_size, title_window , 0, max_kernel_size, morphology_operations)
morphology_operations(0)
cv.waitKey()

可以用上述代码感受一下对不同图片,采用不同操作,不同参数,得到的结果是怎样的.


利用形态变换提取图像中的水平线

看一个具体的例子

我们想从下图中提取出水平线出来.

前面讲过,膨胀和腐蚀都是通过卷积核去定义一个要从什么样的区域去取局部极大值或局部极小值. 那为了完成水平线的提取,我们可以定义自己的特定形状的卷积核去完成这个功能.

# 形态变换实现水平线和音符提取
import cv2 as cv
import numpy as np def test():
src = cv.imread("/home/sc/disk/keepgoing/opencv_test/music.png",cv.IMREAD_COLOR)
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
gray = cv.bitwise_not(gray)
cv.imshow("gray",gray) horizontal = np.copy(gray)
vertical = np.copy(gray) ##设计特定形状卷积核
cols = horizontal.shape[1]
horizontal_size = cols // 30
horizontalStructure = cv.getStructuringElement(cv.MORPH_RECT, (horizontal_size, 1))
print(horizontalStructure) horizontal1 = cv.erode(horizontal, horizontalStructure)
cv.imshow("h1",horizontal1) horizontal2 = cv.dilate(horizontal1, horizontalStructure)
cv.imshow("h2",horizontal2) ##设计特定形状卷积核
rows = vertical.shape[0]
verticalsize = rows // 30
verticalStructure = cv.getStructuringElement(cv.MORPH_RECT, (1, verticalsize)) vertical = cv.erode(vertical, verticalStructure)
vertical = cv.dilate(vertical, verticalStructure)
cv.imshow("v",vertical) test()
if 27 == cv.waitKey(0):
cv.destroyAllWindows()

首先我们完成将图像的预处理.



这里用了cv.bitwise_not(gray)将我们关注的部分变为亮的.不关注的变为暗的. 因为膨胀和腐蚀都是针对亮的区域而言的.指亮的区域的扩张或收缩.

接下来就是如何定义我们的卷积核呢?

以音符的提取为例,我们希望把水平方向的白色横线去除,即我们更关注垂直方向的像素点. 水平方向的白色横线的上下位置基本是黑色的背景. 所以我们需要的卷积核是一个如下:



这样对白色横线上的像素点,通过取该像素点上下方若干个点的最小值,把该像素点灰度值变为0.从而达到去除白色横线的目的.

最终得到如下:

opencv之形态变换的更多相关文章

  1. OpenCV仿射变换+投射变换+单应性矩阵

    本来想用单应性求解小规模运动的物体的位移,但是后来发现即使是很微小的位移也会带来超级大的误差甚至错误求解,看起来这个方法各种行不通,还是要匹配知道深度了以后才能从三维仿射变换来入手了,纠结~ esti ...

  2. opencv::基于距离变换与分水岭的图像分割

    什么是图像分割 图像分割(Image Segmentation)是图像处理最重要的处理手段之一 图像分割的目标是将图像中像素根据一定的规则分为若干(N)个cluster集合,每个集合包含一类像素. 根 ...

  3. opencv 仿射变换 投射变换, 单应性矩阵

    仿射 estimateRigidTransform():计算多个二维点对或者图像之间的最优仿射变换矩阵 (2行x3列),H可以是部分自由度,比如各向一致的切变. getAffineTransform( ...

  4. opencv C++极坐标变换

    #include<opencv2/core/core.hpp> #include<opencv2/highgui/highgui.hpp> #include<opencv ...

  5. OpenCV 离散傅立叶变换

    #include "opencv2/core/core.hpp" #include "opencv2/imgproc/imgproc.hpp" #include ...

  6. OpenCV击中击不中HMTxingt变换最容易理解的解释

    OpenCV击中击不中变换是几个形态变换中相对比较拗口.不容易理解的,给初学者理解带来了很多困难,虽然网上也有许多的公开资料,原理和算法基本上介绍比较清晰,但是是要OpenCV进行形态变换大多还是说得 ...

  7. 计算机视觉2D几何基元及其变换介绍和OpenCV WarpPerspective源码分析

    2D图像几何基元 一般的,表示一个2d几何基元只用两个维度(比如x,y)就可以表示了,但是在计算机视觉研究中,为了统一对2d几何基元的操作(后面讲到的仿射,透射变换),一般会以增广矢量的方式表示几何基 ...

  8. OpenCV——Delaunay三角 [转载]

    从这个博客转载 http://blog.csdn.net/raby_gyl/article/details/17409717 请其它同学转载时注明原始文章的出处! Delaunay三角剖分是1934年 ...

  9. OpenCV+OpenGL 双目立体视觉三维重建

    0.绪论 这篇文章主要为了研究双目立体视觉的最终目标--三维重建,系统的介绍了三维重建的整体步骤.双目立体视觉的整体流程包括:图像获取,摄像机标定,特征提取(稠密匹配中这一步可以省略),立体匹配,三维 ...

随机推荐

  1. SpringBoot + JPA问题汇总

    实体类有继承父类,但父类没有单独标明注解 异常表现 Caused by: org.hibernate.AnnotationException: No identifier specified for ...

  2. 字符串的api (基础)

    一.基础 1.字符串.charAt(index) 根据下标获取字符串的某一个字符 应用: 判断字符串的首字母是否大写 任意给定的一串字母,统计字符串里面的大写字母和小写字母的个数 2.字符串.inde ...

  3. 做一个完整的Hadoop项目

     1. 完整的数据流图 由同ip访问的次数: SQL查询 select ip,count(ip) from tablename Group by ip; 基于Hadoop分析 使用Hadoop分析,需 ...

  4. 增强学习Q-learning分析与演示(入门)

    一些说明.参阅 https://github.com/MorvanZhou/Reinforcement-learning-with-tensorflow/blob/master/contents/1_ ...

  5. 让Jenkins执行GitHub上的pipeline脚本

    本文是<Jenkins流水线(pipeline)实战>系列的第二篇,上一篇搭建好了Jenkins环境并执行了一个简单的pipeline任务,当时我们直接在Jenkins网页上编写pipel ...

  6. Small Spring系列一:BeanFactory(一)

    人生如逆旅,我亦是行人. 前言 Spring是一个开放源代码的设计层面框架,它解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用. 准备 bean-v1.xml配置b ...

  7. Bytectf-几道web总结

    1.EZcms 这道题思路挺明确,给了源码,考点就是md5哈希扩展+phar反序列化 首先这道题会在上传的文件目录下生成无效的.htaccess,从而导致无法执行上传的webshell,所以就需要想办 ...

  8. Spring Boot 多站点利用 Redis 实现 Session 共享

    如何在不同站点(web服务进程)之间共享会话 Session 呢,原理很简单,就是把这个 Session 独立存储在一个地方,所有的站点都从这个地方读取 Session. 通常我们使用 Redis 来 ...

  9. el-table合并行并自定义某一列或几列

    在el-table的官方组件中并没有看到具体的合并行或者列及自定义表格内容,于是就自己写了一个效果如下所示. 这种对左侧内容要求比较高,要求行合并,并要自定义一些内容.下面说一下具体方法及代码写法. ...

  10. java-ztest测试报告的搭建,python-BeautifulReport

    今天用testng的时候感觉测试报告贼丑又慢,以下图片是对比.了解到ztest,搭建的时候发现网上没有教程,对java真是太不友好了,所以步骤记录下吧.有疑问的可进群:231733032 使用ztes ...