opencv之形态变换
形态变换
在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之形态变换的更多相关文章
- OpenCV仿射变换+投射变换+单应性矩阵
本来想用单应性求解小规模运动的物体的位移,但是后来发现即使是很微小的位移也会带来超级大的误差甚至错误求解,看起来这个方法各种行不通,还是要匹配知道深度了以后才能从三维仿射变换来入手了,纠结~ esti ...
- opencv::基于距离变换与分水岭的图像分割
什么是图像分割 图像分割(Image Segmentation)是图像处理最重要的处理手段之一 图像分割的目标是将图像中像素根据一定的规则分为若干(N)个cluster集合,每个集合包含一类像素. 根 ...
- opencv 仿射变换 投射变换, 单应性矩阵
仿射 estimateRigidTransform():计算多个二维点对或者图像之间的最优仿射变换矩阵 (2行x3列),H可以是部分自由度,比如各向一致的切变. getAffineTransform( ...
- opencv C++极坐标变换
#include<opencv2/core/core.hpp> #include<opencv2/highgui/highgui.hpp> #include<opencv ...
- OpenCV 离散傅立叶变换
#include "opencv2/core/core.hpp" #include "opencv2/imgproc/imgproc.hpp" #include ...
- OpenCV击中击不中HMTxingt变换最容易理解的解释
OpenCV击中击不中变换是几个形态变换中相对比较拗口.不容易理解的,给初学者理解带来了很多困难,虽然网上也有许多的公开资料,原理和算法基本上介绍比较清晰,但是是要OpenCV进行形态变换大多还是说得 ...
- 计算机视觉2D几何基元及其变换介绍和OpenCV WarpPerspective源码分析
2D图像几何基元 一般的,表示一个2d几何基元只用两个维度(比如x,y)就可以表示了,但是在计算机视觉研究中,为了统一对2d几何基元的操作(后面讲到的仿射,透射变换),一般会以增广矢量的方式表示几何基 ...
- OpenCV——Delaunay三角 [转载]
从这个博客转载 http://blog.csdn.net/raby_gyl/article/details/17409717 请其它同学转载时注明原始文章的出处! Delaunay三角剖分是1934年 ...
- OpenCV+OpenGL 双目立体视觉三维重建
0.绪论 这篇文章主要为了研究双目立体视觉的最终目标--三维重建,系统的介绍了三维重建的整体步骤.双目立体视觉的整体流程包括:图像获取,摄像机标定,特征提取(稠密匹配中这一步可以省略),立体匹配,三维 ...
随机推荐
- 【Spring】事务
一.数据库事务概述 二.Spring中事务 1. Spring 事务管理: 2. Spring 事务管理的API: 2.1 API概述 2.2 PlatformTransactionManager 接 ...
- Redis真的那么好用吗
Redis是什么 Redis是一个开源的底层使用C语言编写的key-value存储数据库.可用于缓存.事件发布订阅.高速队列等场景.而且支持丰富的数据类型:string(字符串).hash(哈希).l ...
- 分库分表之后,id 主键如何处理?
其实这是分库分表之后你必然要面对的一个问题,就是 id 咋生成?因为要是分成多个表之后,每个表都是从 1 开始累加,那肯定不对啊,需要一个全局唯一的 id 来支持.所以这都是你实际生产环境中必须考虑的 ...
- mysql之explain详解
mysql之explain详解 mysql之explain各个字段的详细意思: 字段 含义 select_type 分为简单(simple)和复杂 type all : 即全表扫描 index : 按 ...
- .Net基础篇_学习笔记_第六天_For循环语法
For循环:专门处理已知循环次数的循环. 小技巧:连续敲击两下TAB键循环体自动搭建完成. For循环语法: for(表达式1;表达式2;表达式3){ 循环体;}表达式1一般为声明循环变量,记录循环 ...
- java对象排序(Comparable)详细实例
对象实现Comparable接口 public class Field implements Comparable<Field>{ private String name; private ...
- LeetCode第七题
Reverse digits of an integer. Example1: x = 123, return 321Example2: x = -123, return -321 Have you ...
- js 手机号码正则表达式
var chenkPhone=/^(0|86|17951)?(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$/var username=$ ...
- MySQL优化之索引原理(二)
一,前言 上一篇内容说到了MySQL存储引擎的相关内容,及数据类型的选择优化.下面再来说说索引的内容,包括对B-Tree和B+Tree两者的区别. 1.1,什么是索引 索引是存储引擎用于快速找 ...
- Scala 系列(十三)—— 隐式转换和隐式参数
一.隐式转换 1.1 使用隐式转换 隐式转换指的是以implicit关键字声明带有单个参数的转换函数,它将值从一种类型转换为另一种类型,以便使用之前类型所没有的功能.示例如下: // 普通人 clas ...