【python】OpenCV—Extract Horizontal and Vertical Lines—Morphology

文章目录
- 1、功能描述
- 2、代码实现
- 3、效果展示
- 4、完整代码
- 5、参考
更多有趣的代码示例,可参考【Programming】
1、功能描述
基于 opencv-python 库,利用形态学的腐蚀和膨胀,提取图片中的水平或者竖直线条
2、代码实现
导入基本的库函数
import numpy as np
import cv2 as cv
读入图片(https://raw.githubusercontent.com/opencv/opencv/5.x/doc/tutorials/imgproc/morph_lines_detection/images/src.png),增加读错图片的判断机制
1.jpg

def main(save=False):# Load the imagesrc = cv.imread("./1.jpg", cv.IMREAD_COLOR)# Check if image is loaded fineif src is None:print('Error opening image')return -1
可视化图片,并将其转化为灰度图
# Show source imagecv.imshow("src", src)# [load_image]# [gray]# Transform source image to gray if it is not alreadyif len(src.shape) != 2:gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)else:gray = srcif save:cv.imwrite("gray.jpg", gray)# Show gray imageshow_wait_destroy("gray", gray)# [gray]
gray.jpg

show_wait_destroy 实现如下 ,关闭图片后才运行后续代码
def show_wait_destroy(winname, img):cv.imshow(winname, img)cv.moveWindow(winname, 500, 0)cv.waitKey(0)cv.destroyWindow(winname)
二进制求反灰度图, 并自适应阈值二值化
# [bin]# Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbolgray = cv.bitwise_not(gray)if save:cv.imwrite("bitwise_not_gray.jpg", gray)bw = cv.adaptiveThreshold(gray, 255, cv.ADAPTIVE_THRESH_MEAN_C, \cv.THRESH_BINARY, 15, -2)if save:cv.imwrite("adaptiveThreshold.jpg", bw)# Show binary imageshow_wait_destroy("binary", bw)# [bin]
bitwise_not_gray.jpg

adaptiveThreshold.jpg

复制图片 adaptiveThreshold.jpg ,准备提取水平线和竖直线
# [init]# Create the images that will use to extract the horizontal and vertical lineshorizontal = np.copy(bw)vertical = np.copy(bw)# [init]
提取水平线
# [horiz]# Specify size on horizontal axiscols = horizontal.shape[1] # 1024 colshorizontal_size = cols // 30 # 34# Create structure element for extracting horizontal lines through morphology operationshorizontalStructure = cv.getStructuringElement(cv.MORPH_RECT, (horizontal_size, 1))# Apply morphology operationshorizontal = cv.erode(horizontal, horizontalStructure)if save:cv.imwrite("erode-horizontal.jpg", horizontal)horizontal = cv.dilate(horizontal, horizontalStructure)if save:cv.imwrite("dilate-horizontal.jpg", horizontal)# Show extracted horizontal linesshow_wait_destroy("horizontal", horizontal)# [horiz]
首先会构建结构元素 horizontalStructure(定义了形态学操作的邻域形状和大小)
图片列数 // 30 得到全为 1 的数组
array([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]], dtype=uint8)
接着腐蚀操作 erode-horizontal.jpg

最后膨胀操作 dilate-horizontal.jpg

至此我们就提取到了图片中的水平方向的线条
接下来我们提取竖直方向的线条
# [vert]# Specify size on vertical axisrows = vertical.shape[0] # 134verticalsize = rows // 30 # 4# Create structure element for extracting vertical lines through morphology operationsverticalStructure = cv.getStructuringElement(cv.MORPH_RECT, (1, verticalsize))# Apply morphology operationsvertical = cv.erode(vertical, verticalStructure)if save:cv.imwrite("erode-vertical.jpg", vertical)vertical = cv.dilate(vertical, verticalStructure)if save:cv.imwrite("dilate-vertical.jpg", vertical)# Show extracted vertical linesshow_wait_destroy("vertical", vertical)# [vert]
同理,也是先构建一个结构元素 verticalStructure
array([[1],[1],[1],[1]], dtype=uint8)
腐蚀 erode-vertical.jpg

膨胀 dilate-vertical.jpg

至此我们提取出了竖直方向的线条
可以拓展一下,
As you can see we are almost there. However, at that point you will notice that the edges of the notes are a bit rough. For that reason we need to refine the edges in order to obtain a smoother result
'''Extract edges and smooth image according to the logic1. extract edges2. dilate(edges)3. src.copyTo(smooth)4. blur smooth img5. smooth.copyTo(src, edges)'''
dilate-vertical.jpg 二进制求反,
# [smooth]# Inverse vertical imagevertical = cv.bitwise_not(vertical)if save:cv.imwrite("bitwise_not_vertical.jpg", vertical)show_wait_destroy("vertical_bit", vertical)
bitwise_not_vertical.jpg

cv2.adaptiveThreshold 适应性阈值二值化
# Step 1edges = cv.adaptiveThreshold(vertical, 255, cv.ADAPTIVE_THRESH_MEAN_C, \cv.THRESH_BINARY, 3, -2)if save:cv.imwrite("step1_edges.jpg", edges)show_wait_destroy("edges", edges)
得到 step1_edges.jpg,实现了边缘检测

看看 cv2.adaptiveThreshold 的介绍仔细分析下实现过程
dst = cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C)

形参 C 从邻域像素的平均值或加权平均值中减去的常数,配置的为负数,附近颜色相近的变黑(eg 纯白区域,像素 255,阈值 255-(-2)=257,都变黑,再 eg,纯黑区域,像素 0,阈值 0-(-2)=2,也是黑),附近颜色变动的变白(黑白交替,白色的部分保留,黑色的部分变黑),可以实现边缘提取,妙
膨胀强化边缘
# Step 2kernel = np.ones((2, 2), np.uint8)edges = cv.dilate(edges, kernel)if save:cv.imwrite("step2_edges.jpg", edges)show_wait_destroy("dilate", edges)
kernel 为
array([[1, 1],[1, 1]], dtype=uint8)
step2_edges.jpg

复制 bitwise_not_vertical.jpg
# Step 3smooth = np.copy(vertical)
模糊处理 step4_smooth.jpg
# Step 4smooth = cv.blur(smooth, (2, 2))if save:cv.imwrite("step4_smooth.jpg", smooth)

记录下 step2_edges.jpg 中像素不为零的部分的坐标,也即边缘部分坐标
边缘部分用平滑后的像素替换原来的像素
# Step 5(rows, cols) = np.where(edges != 0)vertical[rows, cols] = smooth[rows, cols]# Show final resultshow_wait_destroy("smooth - final", vertical)if save:cv.imwrite("smooth_final.jpg", vertical)# [smooth]

3、效果展示
输入

水平线条

竖直线条

平滑竖直线条后的结果

输入图片

水平线

竖直线

平滑竖直线条后的结果

4、完整代码
"""
@brief Use morphology transformations for extracting horizontal and vertical lines sample code
"""
import numpy as np
import cv2 as cvdef show_wait_destroy(winname, img):cv.imshow(winname, img)cv.moveWindow(winname, 500, 0)cv.waitKey(0)cv.destroyWindow(winname)def main(save=False):# Load the imagesrc = cv.imread("./1.jpg", cv.IMREAD_COLOR)# Check if image is loaded fineif src is None:print('Error opening image')return -1# Show source imagecv.imshow("src", src)# [load_image]# [gray]# Transform source image to gray if it is not alreadyif len(src.shape) != 2:gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)else:gray = srcif save:cv.imwrite("gray.jpg", gray)# Show gray imageshow_wait_destroy("gray", gray)# [gray]# [bin]# Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbolgray = cv.bitwise_not(gray) # (134, 1024)if save:cv.imwrite("bitwise_not_gray.jpg", gray)bw = cv.adaptiveThreshold(gray, 255, cv.ADAPTIVE_THRESH_MEAN_C, \cv.THRESH_BINARY, 15, -2)if save:cv.imwrite("adaptiveThreshold.jpg", bw)# Show binary imageshow_wait_destroy("binary", bw)# [bin]# [init]# Create the images that will use to extract the horizontal and vertical lineshorizontal = np.copy(bw)vertical = np.copy(bw)# [init]# [horiz]# Specify size on horizontal axiscols = horizontal.shape[1] # 1024 colshorizontal_size = cols // 30 # 34# Create structure element for extracting horizontal lines through morphology operationshorizontalStructure = cv.getStructuringElement(cv.MORPH_RECT, (horizontal_size, 1))# Apply morphology operationshorizontal = cv.erode(horizontal, horizontalStructure)if save:cv.imwrite("erode-horizontal.jpg", horizontal)horizontal = cv.dilate(horizontal, horizontalStructure)if save:cv.imwrite("dilate-horizontal.jpg", horizontal)# Show extracted horizontal linesshow_wait_destroy("horizontal", horizontal)# [horiz]# [vert]# Specify size on vertical axisrows = vertical.shape[0] # 134verticalsize = rows // 30 # 4# Create structure element for extracting vertical lines through morphology operationsverticalStructure = cv.getStructuringElement(cv.MORPH_RECT, (1, verticalsize))# Apply morphology operationsvertical = cv.erode(vertical, verticalStructure)if save:cv.imwrite("erode-vertical.jpg", vertical)vertical = cv.dilate(vertical, verticalStructure)if save:cv.imwrite("dilate-vertical.jpg", vertical)# Show extracted vertical linesshow_wait_destroy("vertical", vertical)# [vert]# [smooth]# Inverse vertical imagevertical = cv.bitwise_not(vertical)if save:cv.imwrite("bitwise_not_vertical.jpg", vertical)show_wait_destroy("vertical_bit", vertical)'''Extract edges and smooth image according to the logic1. extract edges2. dilate(edges)3. src.copyTo(smooth)4. blur smooth img5. smooth.copyTo(src, edges)'''# Step 1edges = cv.adaptiveThreshold(vertical, 255, cv.ADAPTIVE_THRESH_MEAN_C, \cv.THRESH_BINARY, 3, -2)if save:cv.imwrite("step1_edges.jpg", edges)show_wait_destroy("edges", edges)# Step 2kernel = np.ones((2, 2), np.uint8)edges = cv.dilate(edges, kernel)if save:cv.imwrite("step2_edges.jpg", edges)show_wait_destroy("dilate", edges)# Step 3smooth = np.copy(vertical)# Step 4smooth = cv.blur(smooth, (2, 2))if save:cv.imwrite("step4_smooth.jpg", smooth)# Step 5(rows, cols) = np.where(edges != 0)vertical[rows, cols] = smooth[rows, cols]# Show final resultshow_wait_destroy("smooth - final", vertical)if save:cv.imwrite("smooth_final.jpg", vertical)# [smooth]return 0if __name__ == "__main__":main(save=True)
5、参考
- Extract horizontal and vertical lines by using morphological operations
更多有趣的代码示例,可参考【Programming】
相关文章:
【python】OpenCV—Extract Horizontal and Vertical Lines—Morphology
文章目录 1、功能描述2、代码实现3、效果展示4、完整代码5、参考 更多有趣的代码示例,可参考【Programming】 1、功能描述 基于 opencv-python 库,利用形态学的腐蚀和膨胀,提取图片中的水平或者竖直线条 2、代码实现 导入基本的库函数 im…...
Redis十大数据类型详解
Redis(一) 十大数据类型 redis字符串(String) string是redis最基本的类型,一个key对应一个value string类型是二进制安全的,意思是redis的string可以包含任何数据。例如说是jpg图片或者序列化对象 一个re…...
Open FPV VTX开源之betaflight配置
Open FPV VTX开源之betaflight配置 1. 源由2. 配置3. 总结4. 参考资料5. 补充 - 飞控固件版本 1. 源由 飞控嵌入式OSD - ardupilot配置使用betaflight配套OSD图片。 Choose correct font depending on Flight Controller SW. ──> /usr/share/fonts/├──> font_btfl…...
AT32 bootloader程序与上位机程序
从8051到stm32, 从串口下载到JLINK调试,从keil到arm-none-eabi-gcc,从"Hello wrold"到通信协议,一路起来已学会很多,是时候写一下bootloader了。 基本原理 单片机代码编译完后可以生成".hex"和".bin"文件&…...
数据结构与算法之栈: LeetCode 151. 反转字符串中的单词 (Ts版)
反转字符串中的单词 https://leetcode.cn/problems/reverse-words-in-a-string/ 描述 给你一个字符串 s ,请你反转字符串中 单词 的顺序单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开 返回 单词 顺序颠倒且 单词 之间用单个空…...
使用 configparser 读取 INI 配置文件
使用 configparser 读取 INI 配置文件 适合于读取 .ini 格式的配置文件。 配置文件示例 (config.ini): [DEFAULT] host localhost port 3306 [database] user admin password secret import configparser# 创建配置解析器 config configparser.ConfigParser()# 读取配…...
idea 自动导包,并且禁止自动导 *(java.io.*)
自动导包配置 进入 idea 设置,可以按下图所示寻找位置,也可以直接输入 auto import 快速定位到配置。 Add unambiguous imports on the fly:自动帮我们优化导入的包Optimize imports on the fly:自动去掉一些没有用到的包 禁止导…...
RK3588-NPU pytorch-image-models 模型编译测试
RK3588-NPU pytorch-image-models 模型编译测试 一.背景二.操作步骤1.下载依赖2.创建容器3.安装依赖4.创建脚本A.生成模型名列表B.生成ONNX模型C.生成RKNN模型D.批量测试脚本 一.背景 测试RK3588-NPU对https://github.com/huggingface/pytorch-image-models.git中模型的支持程…...
低代码从“产品驱动”向“场景驱动”转型,助力数字化平台构建
一、前言 在数字化时代的大潮中,从宏观层面来看,新技术的落地速度不断加快,各行各业的数字化进程呈现出如火如荼的态势。而从微观层面剖析,企业面临着行业格局快速变化、市场竞争日益激烈以及成本压力显著增强等诸多挑战。 据专…...
相加交互效应函数发布—适用于逻辑回归、cox回归、glmm模型、gee模型
在统计分析中交互作用是指某因素的作用随其他因素水平变化而变化,两因素共同作用不等于两因素单独作用之和(相加交互作用)或之积(相乘交互作用)。相互作用的评估是尺度相关的:乘法或加法。乘法尺度上的相互作用意味着两次暴露的综合效应大于(…...
用gpg和sha256验证ubuntu.iso
链接 https://ubuntu.com/tutorials/how-to-verify-ubuntuhttps://releases.ubuntu.com/jammy/ 本文是2的简明版 sha256sum介绍 sha256sum -c SHA256SUMS 2>&1这段脚本的作用是验证文件的 SHA-256 校验和。具体来说,命令的各个部分含义如下: …...
深入解析 ZooKeeper:分布式协调服务的原理与应用
1.说说 Zookeeper 是什么? ZooKeeper 是一个开源的分布式协调服务,由 Apache Software Foundation 开发维护。它为构建分布式应用程序提供了一套简单且高效的协调接口。ZooKeeper 的设计目的是为了简化分布式系统中常见的任务,例如命名、配置…...
【Rust自学】11.10. 集成测试
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 11.10.1. 什么是集成测试 在Rust里,集成测试完全位于被测试库的外部。集成测试调用库的方式和其他代码一样,这也…...
对当前日期进行按年、按月、按日的取值
对当前日期进行按年、按月、按日的取值。 其规则为: 按年 为当前日期到来年同一日期的前一天(2024-12-01到2025-11-30)。 按月 为当前日期到下个月的同一日期的前一天 (2024-12-01 到 2024-12-31)。 按日 为当前日…...
【Rust自学】12.2. 读取文件
12.2.0. 写在正文之前 第12章要做一个实例的项目——一个命令行程序。这个程序是一个grep(Global Regular Expression Print),是一个全局正则搜索和输出的工具。它的功能是在指定的文件中搜索出指定的文字。 这个项目分为这么几步: 接收命令行参数读…...
C++内存泄露排查
内存泄漏是指程序动态分配的内存未能及时释放,导致系统内存逐渐耗尽,最终可能造成程序崩溃或性能下降。在C中,内存泄漏通常发生在使用new或malloc等分配内存的操作时,但没有正确地使用delete或free来释放这块内存。 在日常开发过程…...
Http 响应状态码 前后端联调
http 响应状态码 :是服务器在处理HTTP请求时返回的状态信息,用于表示请求的处理结果 1xx : 信息性状态码 100 Continue: 服务器已收到请求头部,客户端应继续发送请求体。 101 Switching Protocols : 切换协议。服务器已理解客户端的请求&a…...
48_Lua错误处理
在编写Lua应用时,都可能会遇到不可预见的错误,而错误处理是确保程序稳定性和健壮性的关键环节。有效的错误处理不仅能防止程序崩溃,还能提供有用的反馈信息给开发者或最终用户,从而提高应用程序的质量。本文将详细介绍Lua中的错误处理机制。 1.错误类型 Lua中的错误类型主…...
shell脚本回顾1
1、shell 脚本写出检测 /tmp/size.log 文件如果存在显示它的内容,不存在则创建一个文件将创建时间写入。 一、 ll /tmp/size.log &>/dev/null if [ $? -eq 0 ];then cat /tmp/size.log else touch /tmp/size.log echo date > /tmp/size.log fi二、 if …...
【3】管理无线控制器
1.概述 本文主要介绍AireOS WLC的管理。WLC的管理可以通过CLI和GUI两种方式,而CLI主要分为console接入、telnet以及SSH的登录管理;GUI的管理分为HTTP和HTTPS。 2.CLI的管理 通过console实现的CLI管理这里就单独进行说明了,只要能找到设备的console接口,通过一般的RJ45接…...
【无人机定位】无人机跳频信号 TDOA 定位仿真系统,信号生成(跳频、时延、衰减、噪声)、接收信号合成、时频分析、多算法定位【含Matlab源码 15278期】
💥💥💥💥💥💥💥💥💞💞💞💞💞💞💞💞💞Matlab武动乾坤博客之家💞…...
VLA学习笔记——持续更新中
5 VLA - Vision-Language-Action 大模型 Vision-Language-Action(视觉 - 语言 - 动作) 大模型是之后 多模态 AI 以及机器人发展的一个非常重要的方向,有了 VLA 这位大神的加持,机器人可以完成由环境感知到动作应对的智能任务。 欢迎大家star! Paper: O…...
Chocolatey 安装 Python 3 时那些你可能不知道的隐藏依赖(附详细日志分析)
Chocolatey 安装 Python 3 时那些你可能不知道的隐藏依赖(附详细日志分析) 当你在 Windows 系统上使用 Chocolatey 安装 Python 3 时,表面上看只是一条简单的命令,但背后却隐藏着一系列复杂的依赖处理过程。这些自动安装的组件往往…...
Wan2.2-I2V-A14B保姆级教程:从云服务器选购(CPU/内存/磁盘)到镜像运行全链路
Wan2.2-I2V-A14B保姆级教程:从云服务器选购到镜像运行全链路 1. 前言:为什么选择私有部署 在当今视频内容需求爆炸式增长的时代,能够快速生成高质量视频内容的能力变得尤为重要。Wan2.2-I2V-A14B作为一款先进的文生视频模型,可以…...
人脸分析系统快速上手教程:一键部署智能人脸检测工具
人脸分析系统快速上手教程:一键部署智能人脸检测工具 1. 系统介绍与核心功能 1.1 什么是人脸分析系统 人脸分析系统(Face Analysis WebUI)是一个基于InsightFace框架的智能人脸检测与分析工具。它能够自动识别图片中的人脸,并提…...
Llama-3.2V-11B-cot实战教程:集成Whisper实现音视频+图像联合推理
Llama-3.2V-11B-cot实战教程:集成Whisper实现音视频图像联合推理 1. 项目概述与核心能力 Llama-3.2V-11B-cot是一个强大的视觉语言模型,它不仅能理解图像内容,还能进行系统性推理。这个模型基于LLaVA-CoT论文实现,特别适合需要结…...
分布式系统CAP理论之如何取舍
在分布式系统中,CAP 理论 是一个基石性、指导性的理论,它告诉我们:在设计分布式系统时,无法同时满足三个核心特性,只能在三者之间做权衡。🌐 一、CAP 理论的三个字母代表什么?字母含义说明CCons…...
深入探索Java JPA中的CriteriaQuery
在Java持久化API(JPA)中,CriteriaQuery 提供了强大的查询功能,允许我们以面向对象的方式构建动态查询。今天我们将通过一个实际的例子,深入探讨如何使用CriteriaQuery来获取特定书籍的最新更新ID。 什么是CriteriaQuery? CriteriaQuery是JPA的一部分,它提供了一种类型…...
golang.org/x/net WebSocket开发完全手册:实现实时双向通信
golang.org/x/net WebSocket开发完全手册:实现实时双向通信 【免费下载链接】net [mirror] Go supplementary network libraries 项目地址: https://gitcode.com/gh_mirrors/ne/net 在现代Web应用开发中,实时双向通信已成为提升用户体验的关键技术…...
【数据结构与算法】二叉树从建立开始
为什么你学了二叉树却还是不会做题?从“建树”到“解题”的完整思维体系在学习数据结构的过程中,二叉树几乎是每个人都会接触的内容。但一个很现实的问题是:很多人会写遍历,却不会做题。表面上看是代码能力的问题,实际…...
