【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接…...
中国政务数据安全建设细化及市场需求分析
(基于新《政务数据共享条例》及相关法规) 一、引言 近年来,中国政府高度重视数字政府建设和数据要素市场化配置改革。《政务数据共享条例》(以下简称“《共享条例》”)的发布,与《中华人民共和国数据安全法》(以下简称“《数据安全法》”)、《中华人民共和国个人信息…...
十二、【ESP32全栈开发指南: IDF开发环境下cJSON使用】
一、JSON简介 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,具有以下核心特性: 完全独立于编程语言的文本格式易于人阅读和编写易于机器解析和生成基于ECMAScript标准子集 1.1 JSON语法规则 {"name"…...
python数据结构和算法(1)
数据结构和算法简介 数据结构:存储和组织数据的方式,决定了数据的存储方式和访问方式。 算法:解决问题的思维、步骤和方法。 程序 数据结构 算法 算法 算法的独立性 算法是独立存在的一种解决问题的方法和思想,对于算法而言&a…...

React与原生事件:核心差异与性能对比解析
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...
Three.js + Vue3 加载GLB模型项目代码详解
本说明结合 src/App.vue 代码,详细解释如何在 Vue3 项目中用 three.js 加载并显示 glb 模型。 1. 依赖与插件导入 import {onMounted, onUnmounted } from vue import * as THREE from three import Stats from stats.js import {OrbitControls } from three/examples/jsm/co…...

搭建nginx的负载均衡
1、编写一个configMap的配置文件 events {worker_connections 1024; # 定义每个worker进程的最大连接数 }http {# 定义通用代理参数(替代proxy_params文件)proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-F…...
基于深度强化学习的智能机器人导航系统
前言 随着人工智能技术的飞速发展,机器人在日常生活和工业生产中的应用越来越广泛。其中,机器人导航技术是实现机器人自主移动的关键。传统的导航方法依赖于预设的地图和路径规划算法,但在复杂的动态环境中,这些方法往往难以适应。…...

Framework开发之IMS逻辑浅析1--关键线程及作用
关键线程:EventHub,InputReader,InputDispatcher EventHub: 由于Android继承Linux,Linux的思想是一切皆文件,而输入的类型不止一种(触碰,写字笔,键盘等),每种类型都对应一种驱动设备,而每个硬件驱动设备又对应Linux的一个目录文件…...

因泰立科技H1X激光雷达:因泰立科技为智慧工业注入新动力
在当今工业领域,精准测量与高效作业是推动产业升级的关键因素。因泰立科技推出的H1X三维轮廓扫描激光雷达,凭借其卓越的性能和广泛的应用场景,正成为智慧工业中不可或缺的高科技装备。 产品简介 H1X三维轮廓扫描激光雷达是因泰立科技基于二维…...
【Redis】笔记|第9节|Redis Stack扩展功能
Redis Stack 扩展功能笔记(基于 Redis 7) 一、Redis Stack 概述 定位:Redis OSS 扩展模块(JSON、搜索、布隆过滤器等),提供高级数据处理能力。核心模块: RedisJSON:原生 JSON 支持…...