opencv--形态学自定义内核提取水平或者竖直直线

2024-05-27 10:52

本文主要是介绍opencv--形态学自定义内核提取水平或者竖直直线,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

理论

形态操作

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

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

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

 二进制上的膨胀

灰度图像上的膨胀

 可以发现他是核函数覆盖区域内取最大值

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

二进制映像上的腐蚀 

灰度图像上的腐蚀

可以发现核函数覆盖内取最小值

结构元素

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

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

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

morph12.gif

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

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

例子提取乐谱

输入图片如下:

二值化后:

需求是如何提取对应的水平直线和乐谱形状呢?

提取水平线

构建结构元素

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

linear_horiz.png

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

 // 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);

结果如下 

 提取乐谱形状

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

linear_vert.png

同样,这表示如下:

 // 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);

结果如下:

总结:

可以发现,只要我们合理的设置核函数就可以提取我们想要的形状,不仅仅是水平直线和竖直直线,其他形状也是可以的,例如倾斜45度的直线,圆弧等等,只要设置好核函数即可

源码如下:

#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);
}

这篇关于opencv--形态学自定义内核提取水平或者竖直直线的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1007272

相关文章

C#中通过Response.Headers设置自定义参数的代码示例

《C#中通过Response.Headers设置自定义参数的代码示例》:本文主要介绍C#中通过Response.Headers设置自定义响应头的方法,涵盖基础添加、安全校验、生产实践及调试技巧,强... 目录一、基础设置方法1. 直接添加自定义头2. 批量设置模式二、高级配置技巧1. 安全校验机制2. 类型

SpringBoot AspectJ切面配合自定义注解实现权限校验的示例详解

《SpringBootAspectJ切面配合自定义注解实现权限校验的示例详解》本文章介绍了如何通过创建自定义的权限校验注解,配合AspectJ切面拦截注解实现权限校验,本文结合实例代码给大家介绍的非... 目录1. 创建权限校验注解2. 创建ASPectJ切面拦截注解校验权限3. 用法示例A. 参考文章本文

Vite 打包目录结构自定义配置小结

《Vite打包目录结构自定义配置小结》在Vite工程开发中,默认打包后的dist目录资源常集中在asset目录下,不利于资源管理,本文基于Rollup配置原理,本文就来介绍一下通过Vite配置自定义... 目录一、实现原理二、具体配置步骤1. 基础配置文件2. 配置说明(1)js 资源分离(2)非 JS 资

Python从Word文档中提取图片并生成PPT的操作代码

《Python从Word文档中提取图片并生成PPT的操作代码》在日常办公场景中,我们经常需要从Word文档中提取图片,并将这些图片整理到PowerPoint幻灯片中,手动完成这一任务既耗时又容易出错,... 目录引言背景与需求解决方案概述代码解析代码核心逻辑说明总结引言在日常办公场景中,我们经常需要从 W

Java+AI驱动实现PDF文件数据提取与解析

《Java+AI驱动实现PDF文件数据提取与解析》本文将和大家分享一套基于AI的体检报告智能评估方案,详细介绍从PDF上传、内容提取到AI分析、数据存储的全流程自动化实现方法,感兴趣的可以了解下... 目录一、核心流程:从上传到评估的完整链路二、第一步:解析 PDF,提取体检报告内容1. 引入依赖2. 封装

Java使用正则提取字符串中的内容的详细步骤

《Java使用正则提取字符串中的内容的详细步骤》:本文主要介绍Java中使用正则表达式提取字符串内容的方法,通过Pattern和Matcher类实现,涵盖编译正则、查找匹配、分组捕获、数字与邮箱提... 目录1. 基础流程2. 关键方法说明3. 常见场景示例场景1:提取所有数字场景2:提取邮箱地址4. 高级

聊聊springboot中如何自定义消息转换器

《聊聊springboot中如何自定义消息转换器》SpringBoot通过HttpMessageConverter处理HTTP数据转换,支持多种媒体类型,接下来通过本文给大家介绍springboot中... 目录核心接口springboot默认提供的转换器如何自定义消息转换器Spring Boot 中的消息

Python 字符串裁切与提取全面且实用的解决方案

《Python字符串裁切与提取全面且实用的解决方案》本文梳理了Python字符串处理方法,涵盖基础切片、split/partition分割、正则匹配及结构化数据解析(如BeautifulSoup、j... 目录python 字符串裁切与提取的完整指南 基础切片方法1. 使用切片操作符[start:end]2

使用Python提取PDF大纲(书签)的完整指南

《使用Python提取PDF大纲(书签)的完整指南》PDF大纲(Outline)​​是PDF文档中的导航结构,通常显示在阅读器的侧边栏中,方便用户快速跳转到文档的不同部分,大纲通常以层级结构组织,包含... 目录一、PDF大纲简介二、准备工作所需工具常见安装问题三、代码实现完整代码核心功能解析四、使用效果控

Python自定义异常的全面指南(入门到实践)

《Python自定义异常的全面指南(入门到实践)》想象你正在开发一个银行系统,用户转账时余额不足,如果直接抛出ValueError,调用方很难区分是金额格式错误还是余额不足,这正是Python自定义异... 目录引言:为什么需要自定义异常一、异常基础:先搞懂python的异常体系1.1 异常是什么?1.2