形态变换

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. Spring中常用的23中设计模式

    1.spring 中常用的设计模式有23中  分类  设计模式  创建型 工厂方法模式(FactoryMethod).抽象工厂模式(AbstractFactory).建造者模式(Builder).原型 ...

  2. open的正确使用

    open一个对象的时候,不确定他是图片还是文本啊 #----------------------- import io   with open('photo.jpg', 'rb') as inf:   ...

  3. Ubuntu系统添加用户权限

    一.首先创建一个新用户: sudo adduser hadoop 其次设置密码: sudo passwd hadoop 如果无法使用root密码,请输入如下命令: sudo passwd root 二 ...

  4. Springboot中RedisTemplate的操作

    Springboot中RedisTemplate的操作 @Autowired private RedisTemplate redisTemplate; @Autowired private Strin ...

  5. 【第十一篇】这一篇来说说MVC+EF+easyui datagrid的查询功能

    老规矩 直接上代码 <form class="form-horizontal"> <div class="box-body"> < ...

  6. 让你的AI模型尽可能的靠近数据源

    来源:Redislabs作者:Pieter Cailliau.LucaAntiga翻译:Kevin (公众号:中间件小哥) 简介 今天我们发布了一个 RedisAI 的预览版本,预集成了[tensor ...

  7. Windows 笔记 - 用到的设置

    博客地址:http://www.moonxy.com 在日常使用 Windows 的过程中,或多或少会遇到一些设置,记录下来,以备以后使用. 1. 不重启电脑使 hosts 生效的解决方法 有时候需要 ...

  8. 06 (OC)* iOS中UI类之间的继承关系

    iOS中UI类之间的继承关系 此图可以更好的让你去理解iOS中一些底层的关系.你能够了解以及理解UI类之间的继承关系,你会更加明白苹果有关于底层的东西,更有助于你的项目开发由它们的底层关系,就能更加容 ...

  9. C++基础之迭代器

    迭代器的分类 插入迭代器(insert iterator):绑定一个容器上后可以向容器中插入元素: 流迭代器(stream iterator):绑定在输入输出流中,可以遍历关联的流: 反向迭代器(re ...

  10. FPGA 开发详细流程你了解吗?

    FPGA 的详细开发流程就是利用 EDA 开发工具对 FPGA 芯片进行开发的过程. FPGA 的详细开发流程如下所示,主要包括电路设计.设计输入.综合(优化).布局布线(实现与优化).编程配置五大步 ...