OpenCV4.10使用形态运算提取水平线和垂直线

目标

在本教程中,您将学习如何:

  • 应用两个非常常见的形态运算符(即膨胀和侵蚀),并创建自定义内核,以便在水平轴和垂直轴上提取直线。为此,您将使用以下 OpenCV 函数:

    • erode()
    • dilate()
    • getStructuringElement()

    在一个示例中,您的目标是从乐谱中提取音符。

理论

形态操作

形态学是一组图像处理操作,这些操作基于预定义的结构元素(也称为内核)处理图像。输出图像中每个像素的值基于输入图像中相应像素与其相邻像素的比较。通过选择内核的大小和形状,可以构造对输入图像的特定形状敏感的形态操作。

两种最基本的形态操作是扩张和侵蚀。扩张会将像素添加到图像中物体的边界上,而侵蚀则恰恰相反。添加或删除的像素量分别取决于用于处理图像的结构元素的大小和形状。通常,这两个操作遵循的规则如下:

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

二进制图像上的扩张

灰度图像上的扩张

  • 侵蚀:反之亦然。输出像素的值是结构化元素大小和形状范围内的所有像素的最小值。请看下面的示例图:

二进制映像上的侵蚀

灰度图像上的侵蚀

结构元素

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

结构元素是仅由 0 和 1 组成的矩阵,可以具有任意形状和大小。通常比正在处理的图像小得多,而值为 1 的像素定义邻域。结构元素的中心像素(称为原点)标识感兴趣的像素 - 正在处理的像素。

例如,下面演示了 7x7 大小的菱形结构单元。

一种菱形结构元件及其起源

结构元素可以具有许多常见形状,例如线条、菱形、圆盘、周期线以及圆形和大小。通常,选择的结构化元素的大小和形状与要在输入图像中处理/提取的对象相同。例如,要在图像中查找线条,请创建一个线性结构元素,稍后将看到。

示例代码:

C++

本教程代码如下所示。

您也可以从这里下载。

#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>void show_wait_destroy(const char* winname, cv::Mat img);using namespace std;
using namespace cv;int main(int argc, char** argv)
{CommandLineParser parser(argc, argv, "{@input | notes.png | input image}");Mat src = imread( samples::findFile( parser.get<String>("@input") ), IMREAD_COLOR);if (src.empty()){cout << "Could not open or find the image!\n" << endl;cout << "Usage: " << argv[0] << " <Input image>" << endl;return -1;}// Show source imageimshow("src", src);// Transform source image to gray if it is not alreadyMat gray;if (src.channels() == 3){cvtColor(src, gray, COLOR_BGR2GRAY);}else{gray = src;}// Show gray imageshow_wait_destroy("gray", gray);// Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbolMat bw;adaptiveThreshold(~gray, bw, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);// Show binary imageshow_wait_destroy("binary", bw);// Create the images that will use to extract the horizontal and vertical linesMat horizontal = bw.clone();Mat vertical = bw.clone();// Specify size on horizontal axisint horizontal_size = horizontal.cols / 30;// Create structure element for extracting horizontal lines through morphology operationsMat horizontalStructure = getStructuringElement(MORPH_RECT, Size(horizontal_size, 1));// Apply morphology operationserode(horizontal, horizontal, horizontalStructure, Point(-1, -1));dilate(horizontal, horizontal, horizontalStructure, Point(-1, -1));// Show extracted horizontal linesshow_wait_destroy("horizontal", horizontal);// Specify size on vertical axisint vertical_size = vertical.rows / 30;// Create structure element for extracting vertical lines through morphology operationsMat verticalStructure = getStructuringElement(MORPH_RECT, Size(1, vertical_size));// Apply morphology operationserode(vertical, vertical, verticalStructure, Point(-1, -1));dilate(vertical, vertical, verticalStructure, Point(-1, -1));// Show extracted vertical linesshow_wait_destroy("vertical", vertical);// Inverse vertical imagebitwise_not(vertical, vertical);show_wait_destroy("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, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 3, -2);show_wait_destroy("edges", edges);// Step 2Mat kernel = Mat::ones(2, 2, CV_8UC1);dilate(edges, edges, kernel);show_wait_destroy("dilate", edges);// Step 3Mat smooth;vertical.copyTo(smooth);// Step 4blur(smooth, smooth, Size(2, 2));// Step 5smooth.copyTo(vertical, edges);// Show final resultshow_wait_destroy("smooth - final", vertical);return 0;
}void show_wait_destroy(const char* winname, cv::Mat img) {imshow(winname, img);moveWindow(winname, 500, 0);waitKey(0);destroyWindow(winname);
}

要点/结果

C++

从这里获取演示图像。

加载图像

 CommandLineParser parser(argc, argv, "{@input | notes.png | input image}");Mat src = imread( samples::findFile( parser.get<String>("@input") ), IMREAD_COLOR);if (src.empty()){cout << "Could not open or find the image!\n" << endl;cout << "Usage: " << argv[0] << " <Input image>" << endl;return -1;}// Show source imageimshow("src", src);

灰度

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

灰度转二进制图像

 // Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbolMat bw;adaptiveThreshold(~gray, bw, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);// Show binary imageshow_wait_destroy("binary", bw);

输出图像

现在我们准备应用形态运算来提取水平线和垂直线,从而将音符与乐谱分开,但首先让我们初始化我们将用于此原因的输出图像:

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

结构元素

正如我们在理论中指出的那样,为了提取我们想要的对象,我们需要创建相应的结构元素。由于我们要提取水平线,因此用于该目的的相应结构元素将具有以下形状:

在源代码中,这由以下代码片段表示:

 // Specify size on horizontal axisint horizontal_size = horizontal.cols / 30;// Create structure element for extracting horizontal lines through morphology operationsMat horizontalStructure = getStructuringElement(MORPH_RECT, Size(horizontal_size, 1));// Apply morphology operationserode(horizontal, horizontal, horizontalStructure, Point(-1, -1));dilate(horizontal, horizontal, horizontalStructure, Point(-1, -1));// Show extracted horizontal linesshow_wait_destroy("horizontal", horizontal);

这同样适用于具有相应结构元素的垂直线:

同样,这表示如下:

 // Specify size on vertical axisint vertical_size = vertical.rows / 30;// Create structure element for extracting vertical lines through morphology operationsMat verticalStructure = getStructuringElement(MORPH_RECT, Size(1, vertical_size));// Apply morphology operationserode(vertical, vertical, verticalStructure, Point(-1, -1));dilate(vertical, vertical, verticalStructure, Point(-1, -1));// Show extracted vertical linesshow_wait_destroy("vertical", vertical);

优化边缘/结果

正如你所看到的,我们快到了。但是,在这一点上,您会注意到音符的边缘有点粗糙。出于这个原因,我们需要优化边缘以获得更平滑的结果:

 // Inverse vertical imagebitwise_not(vertical, vertical);show_wait_destroy("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, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 3, -2);show_wait_destroy("edges", edges);// Step 2Mat kernel = Mat::ones(2, 2, CV_8UC1);dilate(edges, edges, kernel);show_wait_destroy("dilate", edges);// Step 3Mat smooth;vertical.copyTo(smooth);// Step 4blur(smooth, smooth, Size(2, 2));// Step 5smooth.copyTo(vertical, edges);// Show final resultshow_wait_destroy("smooth - final", vertical);

参考文献:

1、《Extract horizontal and vertical lines by using morphological operations》---Theodore Tsesmelis


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/617405.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C 408—《数据结构》易错考点200题(含解析)

目录 Δ前言 一、绪论 1.1 数据结构的基本概念 : 1.2 算法和算法评价 : 二、线性表 2.2 线性表的顺序表示 : 2.3 线性表的链式表示 : 三、栈、队列和数组 3.1 栈 3.2 队列 3.3 栈和队列的应用 3.4 数组和特殊矩阵 四、串 4.2 串的模式匹配 五、树与二叉树 5.1 树的基…

mars3d.MaterialType.Image2修改配置面状:图片2的speed数值实现动画效果说明

摘要&#xff1a; mars3d.MaterialType.Image2修改配置面状&#xff1a;图片2的speed数值实现动画效果说明 前提&#xff1a; 1.在示例中&#xff0c;尝试给mars3d.MaterialType.Image2材质的图片加上speed参数&#xff0c;实现动画效果&#xff0c;但是没有看到流动效果说明…

CCS在线调试时实时修改变量值

在使用CCS调试dsp芯片时&#xff0c;发现CCS软件有一个非常好的功能&#xff0c;在仿真调试的时候可以实时修改代码中变量的值。这个功能在调试switch语句的时候非常好用&#xff0c;比如想要执行哪个case语句&#xff0c;直接在仿真界面里面修改switch语句入口参数就行。   …

全球历年GDP增长率_探数API数据统计

以下是数据的详细说明&#xff1a; 全球GDP增长最快的年份是1964年&#xff0c;全球GDP增速达到6.65%。2021年的GDP增长率也相当高&#xff0c;主要受2020年衰退后的恢复性增长推动。 全球GDP增长最慢的年份包括&#xff1a;1974年、1975年&#xff08;第一次石油危机引发&…

【亲测】国内如何支付Overleaf?Overleaf如何升级标准版专业版?Overleaf升级保姆级教程

0. 【必看】开通步骤简述 升级Overleaf的步骤简要总结如下&#xff1a; 使用虚拟信用卡平台WildCard开通虚拟信用卡&#xff08;从链接进入可以优惠15元人民币哦&#xff09;。开卡后&#xff0c;进入WIldcard找到卡片信息进入Overleaf绑定卡片并支付&#xff0c;完成支付后就…

【arduino】控制N位数码管

以下以四位共阳极数码管为例&#xff1b; 本文所有说明均以注释的方式进行。 使用方法&#xff1a; #include "DigitalTube.h" //每位共阳极对应的引脚int digital[4] {8, 11, 12, 7};//参数分别为a f b g e c d dp digital(共阳极引脚数组) length(digital长度)D…

Golang教程一(环境搭建,变量,数据类型,数组切片map)

目录 一、环境搭建 1.windows安装 2.linux安装 3.开发工具 二、变量定义与输入输出 1.变量定义 2.全局变量与局部变量 3.定义多个变量 4.常量定义 5.命名规范 6.输出 7.输入 三、基本数据类型 1.整数型 2.浮点型 3.字符型 4.字符串类型 转义字符 多行字符…

数字人生成 Wav2Lip面部动画 神经辐射场 NeRF场景结构 3DMM人脸模型 深度学习 生成对抗网 GAN 语音交互、虚拟现实(VR)和增强现实(AR)

数字人生成 喝奶茶的甄嬛 数字人,从广义上来说,是数字技术在人体解剖、物理、生理及智能各个层次、各个阶段的渗透。它是信息科学与生命科学融合的产物,利用信息科学的方法对人体在不同水平的形态和功能进行虚拟仿真。数字人可以是虚拟人物,也可以是真实人物的数字再现,它…

Typora导入功能使用详细

一、 pandoc安装&#xff08;导入需要的插件&#xff09; 1. 首次安装完typora&#xff0c;是没法导入的&#xff0c;需要安装pandoc&#xff0c;首先我们先在文件夹里面新建一个Typora文件&#xff0c;然后再找到导入功能点击就可以弹出安装的地址了 2. 点击文件可以找到导入…

常用算法——双指针算法

双指针算法介绍&#xff1a; 所谓的双指针算法看似十分的神秘&#xff0c;但是实质上就是两个标志查找元素的变量。双指针既可以是我们平常最常说的指针&#xff08;类似int *类型的数据&#xff09;&#xff0c;也可以是数组的下标。因为对于一个数组数据的查找&#xff0c;通…

halcon-轴断面检测定位

前言 通常情况下轴检测时&#xff0c;通常会检测轴的各个阶段的长度。但是由于各种原因&#xff0c;在轴断面的区域现实不明显&#xff0c;无法正确提取&#xff0c;这时候需要根据轴断面的突出部分进行检测&#xff0c;但是由于部分轴的粗轴和细轴区域的宽度差距相当接近&…

安卓数据怎么恢复?十大顶级Android数据恢复软件

Android 是移动设备的顶级操作系统。由于许多不确定的情况&#xff0c;会发生数据丢失。数据恢复软件有助于挽救丢失的数据。在这里&#xff0c;让我们讨论一下 前 10 名最佳 android 数据恢复软件。 十大顶级Android数据恢复软件 1.奇客数据恢复 奇客数据恢复是由奇客软件软件…