目的:

结合自定义核,应用两个非常常见的形态学算子(例如,扩张和侵蚀),提取水平和垂直方向的线条。将会用到以下OpenCV函数:

  • cv::erode
  • cv::dilate
  • cv::getStructuringElement

接下里的例子是从乐谱中提取音符(五线谱中音符和乐谱线的分离)

理论

Morphology Operations

形态学是一组图像处理操作, 基于预定义的structuring elements(也被称为核)。输出图像中的每个像素的值是基于中心像素与输入图像中相邻像素的值的比较确定。通过选择核的大小和形状,可以构造一个对输入图像的特定形状敏感的形态学运算。
两个最基本的形态操作是膨胀和腐蚀。膨胀会将像素添加到图像中的对象的边界,而腐蚀正好相反。添加或删除的像素的量,分别取决于用于处理图像的结构元素的大小和形状。一般来说,这两个操作的规则如下:

  • 膨胀(Dilation):输出像素的值是在结构元素的大小和形状上的所有像素的最大值。例如,在一个二值图像中,如果在核的范围内的输入图像的任何一个像素都被设置为值1,则输出图像的相应的像素将被设置为1。后者适用于任何类型的图像(如灰度,BGR,等)。

  • 二值图像的膨胀(Dilation on a Binary Image)

    灰度图像的膨胀(Dilation on a Grayscale Image)

  • Erosion: 反之腐蚀操作的应用亦然,输出像素的值是在结构元素的大小和形状上的所有像素的最小值。下图是例子:

二值图像的腐蚀(Erosion on a Binary Image)

灰度图像的腐蚀(Erosion on a Grayscale Image)

Structuring Elements(结构元素)

正如前面所述,在任何形态操作中,用于探测输入图像的结构元素是最重要的一部分。

一个结构元素是一个只包含0和1的矩阵,可以有任何任意形状和大小。通常情况下,远小于被处理的图像,值为1的元素定义的是相邻的邻域。结构元素的中心像素,称为原点,标识感兴趣的像素(正在处理的像素)。例如,下面说是一个菱形结构元素,有7x7个元素。

一个菱形结构元素和结构的原点(A Diamond-Shaped Structuring Element and its Origin)

一个结构元素可以有许多常见的形状(如线,菱形,圆盘状,周期线,和圆圈)和大小。你通常会选择一个大小形状和待处理/提取的对象一致的结构元素。例如,在图像中查找线条,创建一个线性结构元素,稍后将看到。

代码

/*** @file Morphology_3(Extract_Lines).cpp* @brief Use morphology transformations for extracting horizontal and vertical lines sample code* @author OpenCV team*/#include <iostream>
#include <opencv2/opencv.hpp>using namespace std;
using namespace cv;int main(int, char** argv)
{
//! [load_image]// Load the imageMat src = imread(argv[1]);// Check if image is loaded fineif(!src.data)cerr << "Problem loading image!!!" << endl;// Show source imageimshow("src", src);
//! [load_image]//! [gray]// Transform source image to gray if it is notMat gray;if (src.channels() == 3){cvtColor(src, gray, CV_BGR2GRAY);}else{gray = src;}// Show gray imageimshow("gray", gray);
//! [gray]//! [bin]// Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbolMat bw;adaptiveThreshold(~gray, bw, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);// Show binary imageimshow("binary", bw);
//! [bin]//! [init]// Create the images that will use to extract the horizontal and vertical linesMat horizontal = bw.clone();Mat vertical = bw.clone();
//! [init]//! [horiz]// Specify size on horizontal axisint horizontalsize = horizontal.cols / 30;// Create structure element for extracting horizontal lines through morphology operationsMat horizontalStructure = getStructuringElement(MORPH_RECT, Size(horizontalsize,1));// Apply morphology operationserode(horizontal, horizontal, horizontalStructure, Point(-1, -1));dilate(horizontal, horizontal, horizontalStructure, Point(-1, -1));// Show extracted horizontal linesimshow("horizontal", horizontal);
//! [horiz]//! [vert]// Specify size on vertical axisint verticalsize = vertical.rows / 30;// Create structure element for extracting vertical lines through morphology operationsMat verticalStructure = getStructuringElement(MORPH_RECT, Size( 1,verticalsize));// Apply morphology operationserode(vertical, vertical, verticalStructure, Point(-1, -1));dilate(vertical, vertical, verticalStructure, Point(-1, -1));// Show extracted vertical linesimshow("vertical", vertical);
//! [vert]//! [smooth]// Inverse vertical imagebitwise_not(vertical, vertical);imshow("vertical_bit", vertical);// Extract edges and smooth image according to the logic// 1. extract edges// 2. dilate(edges)// 3. src.copyTo(smooth)// 4. blur smooth img// 5. smooth.copyTo(src, edges)// Step 1Mat edges;adaptiveThreshold(vertical, edges, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 3, -2);imshow("edges", edges);// Step 2Mat kernel = Mat::ones(2, 2, CV_8UC1);dilate(edges, edges, kernel);imshow("dilate", edges);// Step 3Mat smooth;vertical.copyTo(smooth);// Step 4blur(smooth, smooth, Size(2, 2));// Step 5smooth.copyTo(vertical, edges);// Show final resultimshow("smooth", vertical);
//! [smooth]waitKey(0);return 0;
}

解释和结果

1. 加载源图像,并检查它是否加载成功,然后显示它:

 // Load the image
Mat src = imread(argv[1]);
// Check if image is loaded fine
if(!src.data)cerr << "Problem loading image!!!" << endl;
// Show source image
imshow("src", src);

2. 如果图像不是灰度图像转换为灰度图像:

 // Transform source image to gray if it is not
Mat gray;
if (src.channels() == 3)
{cvtColor(src, gray, CV_BGR2GRAY);
}
else
{gray = src;
}
// Show gray image
imshow("gray", gray);

3. 然后将灰度图像转换为二值化。注意~符号表明我们使用逆操作后版本(即bitwise_not):

 // Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbol
Mat bw;
adaptiveThreshold(~gray, bw, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);
// Show binary image
imshow("binary", bw);

   

4. 在我们已经准备好应用形态学操作,以提取水平和垂直线,作为从乐谱中分离音符的结果,但首先让我们初始化的输出图像:

   

// Create the images that will use to extract the horizontal and vertical linesMat horizontal = bw.clone();
Mat vertical = bw.clone();

5. 正如我们在理论中所说的,为了提取我们所希望的对象,我们需要创建相应的结构元素。由于这里我们要提取水平线,一个相应的结构元素有以下形状: 

在代码的实现方式如下:

 // Specify size on horizontal axis
int horizontalsize = horizontal.cols / 30;
// Create structure element for extracting horizontal lines through morphology operations
Mat horizontalStructure = getStructuringElement(MORPH_RECT, Size(horizontalsize,1));
// Apply morphology operations
erode(horizontal, horizontal, horizontalStructure, Point(-1, -1));
dilate(horizontal, horizontal, horizontalStructure, Point(-1, -1));
// Show extracted horizontal lines
imshow("horizontal", horizontal);

6. 垂直线条的的用法也是这样,相应的结构元素如下:


代码如下

 // Specify size on vertical axis
int verticalsize = vertical.rows / 30;
// Create structure element for extracting vertical lines through morphology operations
Mat verticalStructure = getStructuringElement(MORPH_RECT, Size( 1,verticalsize));
// Apply morphology operations
erode(vertical, vertical, verticalStructure, Point(-1, -1));
dilate(vertical, vertical, verticalStructure, Point(-1, -1));
// Show extracted vertical lines
imshow("vertical", vertical);

7. 正如你所看到的,你会注意到,音符的边缘是有点粗糙的。由于这个原因,我们需要光顺处理边缘,以获得更平滑的结果

 // Inverse vertical image
bitwise_not(vertical, vertical);
imshow("vertical_bit", vertical);
// Extract edges and smooth image according to the logic
// 1. extract edges
// 2. dilate(edges)
// 3. src.copyTo(smooth)
// 4. blur smooth img
// 5. smooth.copyTo(src, edges)
// Step 1
Mat edges;
adaptiveThreshold(vertical, edges, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 3, -2);
imshow("edges", edges);
// Step 2
Mat kernel = Mat::ones(2, 2, CV_8UC1);
dilate(edges, edges, kernel);
imshow("dilate", edges);
// Step 3
Mat smooth;
vertical.copyTo(smooth);
// Step 4
blur(smooth, smooth, Size(2, 2));
// Step 5
smooth.copyTo(vertical, edges);
// Show final result
imshow("smooth", vertical);

基于形态学操作提取水平和垂直线条(五线谱中音符和乐谱线的分离)相关推荐

  1. opencv入门_【OpenCV入门之十八】通过形态学操作提取水平与垂直线

    小白导读 学习计算机视觉最重要的能力应该就是编程了,为了帮助小伙伴尽快入门计算机视觉,小白准备了[OpenCV入门]系列.新的一年文章的内容进行了很大的完善,主要是借鉴了更多大神的文章,希望让小伙伴更 ...

  2. Opencv---通过形态学操作提取水平和垂直线

    OpenCV通过使用形态学操作来提取水平和垂直线 目标 在本教程中,您将学习如何: 应用两个非常常见的形态运算符(即扩张和侵蚀),创建自定义内核,以便在水平轴和垂直轴上提取直线.为此,您将使用以下Op ...

  3. OpenCV使用形态学转换提取水平和垂直线的实例(附完整代码)

    OpenCV使用形态学转换提取水平和垂直线的实例 OpenCV使用形态学转换提取水平和垂直线的实例 OpenCV使用形态学转换提取水平和垂直线的实例 #include <opencv2/core ...

  4. OpenCV通过形态学操作提取水平线和垂直线

    OpenCV通过形态学操作提取水平线和垂直线 通过形态学操作提取水平线和垂直线 目标 理论 形态学运算 结构元素 代码 说明/结果 载入图片 灰阶 灰度到二进制图像 输出图像 结构要素 优化边缘/结果 ...

  5. 基于形态学眼底血管提取

    1.形态学分析 形态学是一种比较新的图像处理方法,它可以以一种特定的形态和结构元素作为样本提取出对应的形状,从而得到我们所需要的结构,且它包含有四种基本操作:膨胀.腐蚀.以及开闭操作.基于这些操作,我 ...

  6. 形态学操作之提取水平与垂直直线

    形态学操作之提取水平与垂直直线 检测原理 图像形态学操作,可以通过自定义的结构元素实现结构元素对输入图像一些对象敏感.另外一些对象不敏感,这样就会让敏感的对象改变而不敏感的对象保留输出.通过使用两个最 ...

  7. OpenCV 形态学操作应用——提取水平与垂直线

    文章目录 原理方法 提取步骤 输入图像彩色图像 `imread` 转换为灰度图像 – `cvtColor` 转换为二值图像 – `adaptiveThreshold` 定义结构元素: 开操作 (腐蚀+ ...

  8. 【项目实战二】基于模板匹配和形态学操作的信用卡卡号识别(OpenCV+Python)

    前言:信用卡卡号识别技术的发展有利于提高银行系统的业务水平和办事效率.相信此次通过学习使用OpenCV中的图像处理方法来实现信用卡卡号识别的项目,能让大家清楚地了解图像处理技术的一般方法与步骤以及如何 ...

  9. 基于形态学处理的车牌定位和车牌提取matlab仿真

    UP目录 一.理论基础 二.核心程序 三.测试结果 一.理论基础 车牌识别技术是利用计算机等辅助设备进行的自动汽车牌照自动识别就是在装备了数字摄像设备和计算机信息管理系统等软硬件平台的基础之上,通过对 ...

最新文章

  1. nfs服务端服务停掉,导致的客户端访问挂载目录卡死的情况
  2. 11. Leetcode 713. 乘积小于K的子数组 (数组-同向双指针-滑动窗口)
  3. ICS大作业——程序人生 Hello‘s P2P
  4. Effective C++:unio
  5. python numpy loadtxt
  6. 计算机集群用什么网络,什么是计算机系统集群?
  7. 【论文笔记】Deep Learning Face Representation from Predicting 10,000 Classes
  8. xshell以及xftp免费版
  9. Hands-on C++ Game Animation Programming阅读笔记(八)
  10. Linux网络编程1之什么是什么是网路通信?
  11. 随手写了一段C++访问LDAP, 并且获取sid的代码
  12. 三、青龙面板 添加企业微信应用推送消息
  13. 天津商业大学计算机科学学院,天津商业大学信息工程学院
  14. 信息系统项目管理师2019年上半年上午试题解析(四)
  15. 海尔全球十一连冠背后: 生态时代的无限游戏
  16. 怎样查询自己正在上网的路由器的IP地址?网络节点跟踪命令tracert的用法
  17. 记一次服务器被入侵,没想到我轻松搞定了它~
  18. Matter协议与Zigbee
  19. Java线程中,Blocked,Wait,以及TIMED_WAIT的区别
  20. 智能阅读模型的构建(第六届泰迪杯C题)

热门文章

  1. 部署vs 2008 Report View
  2. linux内核网络协议栈--数据包的网卡驱动收发包过程(二十五)
  3. leetcode算法题--最后一块石头的重量 II★
  4. centos nginx不是命令_Nginx 在CentOS 6/7 上的安装与使用
  5. mysqldump 忽略视图_mysqldump的使用--解决不能导出视图的问题
  6. 对面向对象设计原则的总结
  7. Apache Hook机制解析(上)——钩子机制的实现
  8. 15.1-15.3 FTP,使用vsftpd搭建FTP服务
  9. CCF NOI1113 括号匹配
  10. 使用Windows Server2012或R2 DHCP故障转移群集需要注意的几点