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.mzph.cn/news/814852.shtml

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

相关文章

认识异常(2)

❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&a…

python创建word文档并向word中写数据

一、docx库的安装方法 python创建word文档需要用到docx库&#xff0c;安装命令如下&#xff1a; pip install python-docx 注意&#xff0c;安装的是python-docx。 二、使用方法 使用方法有很多&#xff0c;这里只介绍创建文档并向文档中写入数据。 import docxmydocdocx.Do…

基于生成对抗网络在服装领域的发展脉络和应用趋势

文章目录 1、概述2、深度学习图像生成模型2.1、深度信念网络(Deep belief network&#xff0c;DBN)2.2、变分自编码器(Variational auto-encoder&#xff0c;VAE)2.3、生成对抗网络(Generative adversarial networks&#xff0c;GAN) 3、 模型对比分析4、基于多模态转换的服装图…

nexus搭建maven与docker镜像的私有仓库

引言 通过nexus搭建maven与docker镜像的私有仓库,实现jar包与镜像动态更新、共享、存储。 一、nexus部署 通过docker-compose部署nexus name: java services:#############################环境#############################env-nexus:restart: always## 3.58.1image: so…

代码随想录算法训练营第三十七天| LeetCode 738.单调递增的数字、总结

一、LeetCode 738.单调递增的数字 题目链接/文章讲解/视频讲解&#xff1a;https://programmercarl.com/0738.%E5%8D%95%E8%B0%83%E9%80%92%E5%A2%9E%E7%9A%84%E6%95%B0%E5%AD%97.html 状态&#xff1a;已解决 1.思路 如何求得小于等于N的最大单调递增的整数&#xff1f;98&am…

【C语言基础】:编译和链接(计算机中的翻译官)

文章目录 一、翻译环境和运行环境1. 翻译环境1.1 编译1.1.1 预处理1.1.2 编译1.1.3 汇编 1.2 链接 2. 运行环境 一、翻译环境和运行环境 我们在Visual Studio上写的C语言代码其实都是一些文本信息&#xff0c;计算机是不能够直接执行他们的&#xff0c;计算机只能够执行二进制…

第6章 6.4.1 案例一:爬取亚洲各地区的实时时间(MATLAB入门课程)

讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 本案例将引导大家爬取亚洲各地区的实时时间。我们将从下面这个网…

第 128 场 LeetCode 双周赛题解

A 字符串的分数 模拟 class Solution {public:int scoreOfString(string s) {int res 0;for (int i 1; i < s.size(); i) res abs(s[i] - s[i - 1]);return res;} };B 覆盖所有点的最少矩形数目 排序&#xff1a;先按照 x i x_i xi​ 排序&#xff0c;然后顺序遍…

【C++庖丁解牛】底层为红黑树结构的关联式容器--哈希容器(unordered_map和unordered_set)

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 1. unordered系列关联式容…

系统架构设计图

首先明确应用架构的定义&#xff0c;从百度百科上即可了解到何为应用架构&#xff1a; 应用架构&#xff08;Application Architecture&#xff09;是描述了IT系统功能和技术实现的内容。应用架构分为以下两个不同的层次&#xff1a; 企业级的应用架构&#xff1a;企业层面的应…

arm工作模式、arm9通用寄存器、异常向量表中irq的异常向量、cpsr中的哪几位是用来设置工作模式以及r13,r14,15别名是什么?有什么作用?

ARM 首先先介绍一下ARM公司。 ARM成立于1990年11月&#xff0c;前身为Acorn计算机公司 主要设计ARM系列RISC处理器内核 授权ARM内核给生产和销售半导体的合作伙伴ARM公司不生产芯片 提供基于ARM架构的开发设计技术软件工具评估版调试工具应用软件总线架构外围设备单元等等CPU中…

【MySQL】MySQL在Centos 7环境安装

目录 准备工作 第一步&#xff1a;卸载不要的环境 第二步&#xff1a;下载官方的mysql 第三步 上传到Linux中 第四步 安装 正式安装 启动 ​编辑 登录 准备工作 第一步&#xff1a;卸载不要的环境 使用root进行安装 如果是普通用户&#xff0c;使用 su - 命令&#…

langchain RunableBranch 分类判断选择不同链

import os from dotenv import load_dotenv from langchain_community.llms import Tongyi load_dotenv(key.env) # 指定加载 env 文件 key os.getenv(DASHSCOPE_API_KEY) # 获得指定环境变量 DASHSCOPE_API_KEY os.environ["DASHSCOPE_API_KEY"] # 获得指定环境…

字符串转换为List<String>时候抛出异常:com.alibaba.fastjson2.JSONException: TODO : s

前言&#xff1a; 一个字符串想要能够转换为 List&#xff0c;其本身必须是具备 List 属性的字符串。 问题现象&#xff1a; 项目中需要对第三方接口返回的字符串转换为 List&#xff0c;就想到了使用 fastjson 来处理。 代码如下&#xff1a; Object obj data.get(SignC…

格式化D盘后C盘内的文件会受影响吗?深度解析

在计算机的日常使用中&#xff0c;磁盘格式化是一个常见的操作&#xff0c;它能帮助我们清除磁盘上的数据&#xff0c;为新的数据腾出空间。然而&#xff0c;当涉及到系统盘和其他存储盘时&#xff0c;许多用户会担心一个问题&#xff1a;如果我格式化了非系统盘&#xff0c;比…

蓝桥杯真题演练:2023B组c/c++

日期统计 小蓝现在有一个长度为 100 的数组&#xff0c;数组中的每个元素的值都在 0 到 9 的范围之内。 数组中的元素从左至右如下所示&#xff1a; 5 6 8 6 9 1 6 1 2 4 9 1 9 8 2 3 6 4 7 7 5 9 5 0 3 8 7 5 8 1 5 8 6 1 8 3 0 3 7 9 2 7 0 5 8 8 5 7 0 9 9 1 9 4 4 6 8 6 3 …

连锁品牌企业为何不能使用通用收银系统?

在连锁服装品牌企业中&#xff0c;加盟连锁店往往选择使用特定的收银管理系统&#xff0c;而不是市面上通用的收银系统。这一选择背后有着几个主要原因&#xff1a; 首先&#xff0c;加盟连锁店的核心在于品牌的一致性和管理。采用统一的收银管理系统可以确保所有门店在运营和管…

2024连锁收银系统哪个好 有什么特点

在服装连锁店的经营中&#xff0c;选择一款优秀的收银系统至关重要。收银系统不仅仅是简单的结账工具&#xff0c;更是管理销售、库存和客户信息的关键平台。以下将介绍几款优秀的服装连锁店收银系统&#xff0c;以便您更好地了解各款系统的特点和优势。 1. 商淘云连锁店收银系…

【CSS】一篇文章讲清楚screen、window和html元素的位置:top、left、width、height

一个Web网页从内到外的顺序是&#xff1a; 元素div,ul,table... → 页面body → 浏览器window → 屏幕screen 分类详情屏幕screen srceen.width - 屏幕的宽度 screen.height - 屏幕的高度&#xff08;屏幕未缩放时&#xff0c;表示屏幕分辨率&#xff09; screen.availLeft …

中国绿色技术助力全球能源转型(国际论坛)

中国的清洁能源发展战略和实践对全球能源结构转型产生了深远影响。作为全球最大的可再生能源生产和消费国&#xff0c;中国在推动国内可再生能源产业发展的同时&#xff0c;也积极与世界各国分享技术和经验&#xff0c;促进全球范围内清洁能源技术的普及和应用成本的降低。例如…