当前位置: 首页 > news >正文

计算机视觉 图片处理 在骨架化过程中,每次迭代都会从图像的边缘移除一层像素,直到只剩下单像素宽度的骨架

你说得对,if cv2.countNonZero(binary) == 0: break 这个条件确实表示图像中已经没有非零像素,即图像完全变为空白。这并不是骨架化完成的标志,而是表示图像已经被腐蚀到没有任何内容了。

在骨架化过程中,我们需要一个更合适的停止条件,以确保图像被正确地简化为单像素宽度的骨架,而不是被完全腐蚀掉。

骨架化的正确停止条件

在骨架化过程中,每次迭代都会从图像的边缘移除一层像素,直到只剩下单像素宽度的骨架。正确的停止条件是当图像中不再有可以被移除的像素时,即图像已经不能再被腐蚀,但仍然有非零像素存在。

改进的骨架化代码

以下是一个更完整的骨架化代码示例,其中包含了正确的停止条件:

import cv2
import numpy as npdef skeletonize(image):# 确保输入图像是二值化的_, binary = cv2.threshold(image, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)# 创建结构元素element = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))# 初始化骨架图像skeleton = np.zeros_like(binary)# 初始化前一次的骨架图像prev_skeleton = np.zeros_like(binary)# 迭代直到骨架图像不再变化while True:# 腐蚀操作eroded = cv2.erode(binary, element)# 膨胀操作temp = cv2.dilate(eroded, element)# 计算差值temp = cv2.subtract(binary, temp)# 更新骨架skeleton = cv2.bitwise_or(skeleton, temp)# 更新二值图像binary = eroded.copy()# 检查骨架图像是否发生变化if np.array_equal(skeleton, prev_skeleton):break# 更新前一次的骨架图像prev_skeleton = skeleton.copy()return skeleton# 读取图像
image = cv2.imread('your_image.png', cv2.IMREAD_GRAYSCALE)# 骨架化处理
skeleton = skeletonize(image)# 显示结果
cv2.imshow('Skeleton', skeleton)
cv2.waitKey(0)
cv2.destroyAllWindows()

代码解释

  1. 初始化骨架图像和前一次骨架图像

    • skeleton 用于存储当前的骨架图像。
    • prev_skeleton 用于存储上一次迭代的骨架图像,以便比较是否发生变化。
  2. 迭代处理

    • 每次迭代中,先对当前的二值图像进行腐蚀操作,然后进行膨胀操作。
    • 计算腐蚀和膨胀后的图像的差值,这个差值就是当前迭代的骨架部分。
    • 将差值与之前的骨架图像进行逻辑或操作,更新骨架图像。
    • 更新二值图像为腐蚀后的图像,继续下一次迭代。
  3. 停止条件

    • 使用 np.array_equal(skeleton, prev_skeleton) 检查当前的骨架图像是否与上一次的骨架图像相同。
    • 如果相同,说明骨架图像已经不再变化,此时已经完成了骨架化,退出循环。

为什么这样可以

在骨架化过程中,每次迭代都会从图像的边缘移除一层像素,直到只剩下单像素宽度的骨架。当骨架图像不再变化时,说明已经无法再从边缘移除像素,此时的骨架图像就是最终的骨架。

结果

使用上述代码,你可以得到一个单像素宽度的骨架图像,而不是一个完全空白的图像。这种骨架化方法在许多计算机视觉任务中非常有用,尤其是在需要提取形状特征的场景中。

http://www.xdnf.cn/news/1330471.html

相关文章:

  • 【图像算法 - 19】慧眼识苗:基于深度学习与OpenCV的大棚农作物生长情况智能识别检测系统
  • Kubernetes集群安装部署--flannel
  • InnoDB为什么使用B+树实现索引?
  • 从繁琐到优雅:Java Lambda 表达式全解析与实战指南
  • 【Spring Boot把日志记录到文件里面】
  • sfc_os!SfcQueueValidationRequest函数分析之sfc_os!IsFileInQueue
  • Android面试指南(三)
  • STM32学习笔记15-SPI通信软件控制
  • 《Java 多线程全面解析:从基础到生产者消费者模型》
  • InfoNES模拟器HarmonyOS移植指南
  • 从数据孤岛到实时互联:Canal 驱动的系统间数据同步实战指南
  • 排查Redis数据倾斜引发的性能瓶颈
  • python学习DAY46打卡
  • 迁移学习(Transfer Learning)
  • C语言:字符函数与字符串函数(1)
  • Go 进阶学习路线
  • 日语学习-日语知识点小记-构建基础-JLPT-N3阶段(17):文法+单词第5回3-复习
  • 《算法导论》第 32 章 - 字符串匹配
  • 云电脑 vs 传统PC:全面对比3A游戏与AI训练的成本与性能
  • 网络间的通用语言TCP/IP-网络中的通用规则3
  • 基于提示词工程和MCP构建垂直Agent应用
  • STM32 vscode 环境, 官方插件
  • 前端多环境变量配置全攻略:开发 / 测试 / 生产自动切换实战
  • 微信小程序连接到阿里云物联网平台
  • Java I/O 模型精讲:从传统BIO到高性能NIO与AIO
  • TDengine IDMP 运维指南(4. 使用 Docker 部署)
  • HarmonyOS 实战:学会在鸿蒙中使用第三方 JavaScript 库(附完整 Demo)
  • 实现自己的AI视频监控系统-第一章-视频拉流与解码1
  • Orange的运维学习日记--47.Ansible进阶之异步处理
  • [Git] 如何拉取 GitHub 仓库的特定子目录