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

Python骨架长度检测

🏆本文收录于《CSDN问答解惑-专业版》专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!

问题描述

  Python骨架长度检测。我用的skimage进行骨架提取然后测量长度,我建立了一个系统界面,将图片显示在QHBoxLayout里面然后进行阈值分割和骨架提取,这两部都很正常,然后等到我长度测量的时候就一直报错



我检查了一下数组他是[0,1]的但是就是报这个错误,原代码如下

import os
import cv2
import numpy as np
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtWidgets import QMessageBox, QFileDialog, QLabel, QHBoxLayout, QTableWidgetItem
from lun_kuo_ce_liang import calculate_contour_properties
from skimage import measure
from skimage.morphology import skeletonize
from skimage import util
 
class LingJianPanDing:
    def __init__(self, ui, main_window):
        self.ui = ui
        self.main_window = main_window
        self.ui.pushButton_3.clicked.connect(self.open_defect_images)
        self.ui.yuzhifenge.valueChanged.connect(self.update_threshold_from_slider)
        self.ui.yuzhifengeshu.valueChanged.connect(self.update_slider_from_spinbox)
        self.ui.kspd_2.clicked.connect(self.start_length_measurement)
        self.ui.kspd_3.clicked.connect(self.generate_skeleton)
 
        self.folder_path = ""
        self.images = []
        self.binarized_images = []
        self.skeleton_images = []
 
    def open_defect_images(self):
        self.folder_path = QFileDialog.getExistingDirectory(self.main_window, "选择图片文件夹",
                                                            "C:/Users/fenghuajuedie/PycharmProjects/pythonProject1/heji/quexian")
        if not self.folder_path:
            QMessageBox.information(self.main_window, "提示", "没有选择任何文件夹")
            return
        try:
            self.images = [f for f in os.listdir(self.folder_path) if f.endswith(('.png', '.jpg', '.jpeg', '.bmp'))]
            if not self.images:
                QMessageBox.information(self.main_window, "提示", "选中的文件夹没有图片")
                return
 
            layout = self.ui.label_image_4.layout()
            if not layout:
                layout = QHBoxLayout(self.ui.label_image_4)
                self.ui.label_image_4.setLayout(layout)
 
            while layout.count():
                child = layout.takeAt(0)
                if child.widget():
                    child.widget().deleteLater()
 
            for image_name in self.images:
                pixmap = QPixmap(os.path.join(self.folder_path, image_name))
                label = QLabel()
                label.setPixmap(pixmap)
                layout.addWidget(label)
 
            self.update_threshold_images()
 
        except Exception as e:
            QMessageBox.critical(self.main_window, "错误", f"无法加载图片: {str(e)}")
 
    def update_threshold_from_slider(self):
        self.ui.yuzhifengeshu.setValue(self.ui.yuzhifenge.value())
        self.update_threshold_images()
 
    def update_slider_from_spinbox(self):
        self.ui.yuzhifenge.setValue(self.ui.yuzhifengeshu.value())
        self.update_threshold_images()
 
    def calculate_contour_similarity(self, imageA, imageB):
        contoursA, _ = cv2.findContours(imageA, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        contoursB, _ = cv2.findContours(imageB, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
 
        if len(contoursA) > 0 and len(contoursB) > 0:
            match_score = abs(cv2.matchShapes(contoursA[0], contoursB[0], cv2.CONTOURS_MATCH_I1, 0.0))
            return 1 - match_score
        return 0.0
 
    def update_threshold_images(self):
        threshold = self.ui.yuzhifenge.value()
        layout_4 = self.ui.horizontalLayout_4
        layout_5 = self.ui.horizontalLayout_5
 
        for layout in [layout_4, layout_5]:
            while layout.count():
                child = layout.takeAt(0)
                if child.widget():
                    child.widget().deleteLater()
 
        self.binarized_images = []
 
        for image_index, image_name in enumerate(self.images):
            image_path = os.path.join(self.folder_path, image_name)
            image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
 
            if image is not None:
                _, binary_image = cv2.threshold(image, threshold, 255, cv2.THRESH_BINARY)
                binary_image[binary_image == 255] = 1
                self.binarized_images.append(binary_image)
 
                pixmap_original = QPixmap(image_path)
                label_original = QLabel()
                label_original.setPixmap(pixmap_original)
                layout_4.addWidget(label_original)
 
                contour_image = np.zeros_like(binary_image, dtype=np.uint8)
                contours = measure.find_contours(binary_image, 0.5)
 
                for contour in contours:
                    for i in range(len(contour) - 1):
                        pt1 = (int(round(contour[i, 1])), int(round(contour[i, 0])))
                        pt2 = (int(round(contour[i + 1, 1])), int(round(contour[i + 1, 0])))
                        if 0 <= pt1[0] < image.shape[1] and 0 <= pt1[1] < image.shape[0]:
                            cv2.line(contour_image, pt1, pt2, (255,), 1)
 
                height, width = contour_image.shape
                qimage_contour = QImage(contour_image.data, width, height, width, QImage.Format_Grayscale8)
                pixmap_contour = QPixmap.fromImage(qimage_contour).scaled(width, height, Qt.KeepAspectRatio)
                label_contour = QLabel()
                label_contour.setPixmap(pixmap_contour)
                layout_5.addWidget(label_contour)
 
                similarity = self.calculate_contour_similarity(image, contour_image)
                current_row_count = self.ui.tableWidget_2.rowCount()
                if image_index >= current_row_count:
                    self.ui.tableWidget_2.insertRow(current_row_count)
 
                self.ui.tableWidget_2.setItem(image_index, 0, QTableWidgetItem(f"目标 {image_index + 1}"))
                self.ui.tableWidget_2.setItem(image_index, 1, QTableWidgetItem("划痕"))
                self.ui.tableWidget_2.setItem(image_index, 2, QTableWidgetItem(f"{similarity:.4f}"))
 
    def generate_skeleton(self):
        layout = self.ui.horizontalLayout_5
        while layout.count():
            child = layout.takeAt(0)
            if child.widget():
                child.widget().deleteLater()
 
        self.skeleton_images = []
 
        for index, binary_image in enumerate(self.binarized_images):
            if binary_image.size == 0:
                print(f"警告:第 {index + 1} 张骨架图像为空,跳过。")
                continue
 
            binary_image[binary_image == 255] = 1
            skeleton = skeletonize(binary_image)
            self.skeleton_images.append(skeleton)
 
            display_image = np.zeros((*skeleton.shape, 3), dtype=np.uint8)
            display_image[skeleton > 0] = [0, 0, 255]
            height, width, _ = display_image.shape
            qimage = QImage(display_image.data, width, height, 3 * width, QImage.Format_RGB888)
            pixmap = QPixmap.fromImage(qimage).scaled(width, height, Qt.KeepAspectRatio)
            label = QLabel()
            label.setPixmap(pixmap)
            layout.addWidget(label)
 
    def start_length_measurement(self):
        for index, skeleton_image in enumerate(self.skeleton_images):
            if skeleton_image.size == 0:
                print(f"警告:第 {index + 1} 张骨架图像为空,跳过。")
                continue
 
            binary_skeleton = (skeleton_image > 0).astype(np.uint8)
            contours, _ = cv2.findContours(binary_skeleton, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
            if not contours:
                print(f"第 {index + 1} 张图像未检测到轮廓")
                continue
 
            try:
                calculate_contour_properties(binary_skeleton, self.ui.tableWidget_2)
            except ValueError as ve:
                print(f"处理第 {index + 1} 张骨架图时解包错误: {ve}")
                continue
            except Exception as e:
                print(f"处理第 {index + 1} 张骨架图时出错: {e}")
                continue

下面是calculate_contour_propertie的具体代码,请参考代码再帮帮我,另外我把图片以数组的形式打印出来了进行全局的监测,图像全都是数字的形式,但是在进行长度测量的时候还是在报错

def calculate_contour_properties(image, table_widget):
    """
    计算轮廓的长宽信息并显示在表格中
    """
    # Step 1: 提取轮廓
    contours = measure.find_contours(image, 0.8)
 
    if not contours:
        print("未找到任何轮廓")
        return
 
    # Step 2: 获取中心线点
    skeleton = skeletonize(image)
    x, y = np.where(skeleton > 0)
    centers = np.hstack((x.reshape(-1, 1), y.reshape(-1, 1)))
 
    # Step 3: 法向量估计
    normals = estimate_normals(centers, n=3)  # n为近邻点数量,默认为3
 
    # Step 4: 获取轮廓点
    bl = contours[0]  # 左轮廓点
    br = contours[1]  # 右轮廓点
    bpoints = np.vstack((bl, br))
 
    # Step 5: 计算宽度
    _, widths = get_crack_ctrlpts(centers, normals, bpoints, hband=5, vband=2)
 
    # Step 6: 将计算结果填入表格
    for i, (length, width) in enumerate(widths):
        table_widget.setItem(i, 0, QTableWidgetItem(str(i + 1)))  # 目标编号
        table_widget.setItem(i, 1, QTableWidgetItem("划痕"))  # 目标类别
        table_widget.setItem(i, 2, QTableWidgetItem(f"{length:.2f}"))  # 长度
        table_widget.setItem(i, 3, QTableWidgetItem(f"{width:.2f}"))  # 宽度
 
    print("轮廓计算完成并已更新到表格中。")

下面是带有图片数组信息的报错图片

解决方案

  如下是上述问题的解决方案,仅供参考:

  从你的描述和代码来看,骨架提取和轮廓检测步骤都是正常的,但在进行骨架长度测量时发生了错误。这个问题可能有几个原因,我会帮你分析可能的原因,并提供解决方案。

可能的原因分析:

  1. 数组格式问题
    你的代码中有些地方将二值图像数组中的255值替换为1。虽然这对二值图像处理是常见的做法,但有时可能会导致意外行为。特别是在使用skimage.measure.find_contourscv2.findContours时,这些函数可能期待的输入数据类型和范围与实际不符。

  2. 轮廓检测到的点太少
    如果骨架图像中的轮廓点数过少,可能会导致后续处理出错,特别是在计算轮廓长宽时。

  3. 骨架图像可能存在问题
    骨架图像可能不连续或存在断裂,这可能会导致在calculate_contour_properties函数中计算轮廓属性时产生异常。

解决方案:

  1. 确保输入数据的类型和格式
    在进行骨架提取和轮廓检测之前,确保图像的数组格式正确。例如,使用cv2.findContours时,确保输入的数组是uint8格式,并且值范围为0或1。你可以在骨架提取后打印出骨架图像的数组信息来验证格式。

  2. 处理骨架图像为空的情况
    generate_skeleton方法中,骨架图像可能为空或近似为空。你可以在处理前加入判断,确保图像中有足够多的骨架点。

  3. 添加异常处理
    calculate_contour_properties函数中添加更详细的异常处理,以捕获并记录导致出错的具体原因。例如,检查轮廓点的数量、检查骨架图像的有效性等。

更新后的代码

我为你修改了代码,增加了一些调试信息和防御性编程以避免潜在的问题。

def calculate_contour_properties(image, table_widget):
    """
    计算轮廓的长宽信息并显示在表格中
    """
    # Step 1: 提取轮廓
    contours = measure.find_contours(image, 0.8)
 
    if not contours or len(contours) < 2:
        print("未找到任何轮廓或轮廓数量不足")
        return

    # Step 2: 获取中心线点
    skeleton = skeletonize(image)
    x, y = np.where(skeleton > 0)
    if len(x) == 0 or len(y) == 0:
        print("未检测到有效骨架点")
        return

    centers = np.hstack((x.reshape(-1, 1), y.reshape(-1, 1)))
 
    # Step 3: 法向量估计
    normals = estimate_normals(centers, n=3)  # n为近邻点数量,默认为3
 
    # Step 4: 获取轮廓点
    try:
        bl = contours[0]  # 左轮廓点
        br = contours[1]  # 右轮廓点
        bpoints = np.vstack((bl, br))
    except IndexError as ie:
        print(f"轮廓点处理异常: {ie}")
        return

    # Step 5: 计算宽度
    try:
        _, widths = get_crack_ctrlpts(centers, normals, bpoints, hband=5, vband=2)
    except Exception as e:
        print(f"宽度计算时出错: {e}")
        return
 
    # Step 6: 将计算结果填入表格
    for i, (length, width) in enumerate(widths):
        table_widget.setItem(i, 0, QTableWidgetItem(str(i + 1)))  # 目标编号
        table_widget.setItem(i, 1, QTableWidgetItem("划痕"))  # 目标类别
        table_widget.setItem(i, 2, QTableWidgetItem(f"{length:.2f}"))  # 长度
        table_widget.setItem(i, 3, QTableWidgetItem(f"{width:.2f}"))  # 宽度
 
    print("轮廓计算完成并已更新到表格中。")

调试建议:

  1. 打印调试信息
    在骨架图像生成后打印它的基本信息(如数组形状、数据类型、值范围等)来确认图像是否生成正确。

  2. 逐步调试
    可以使用小数据集或单一图像逐步调试整个流程,这样能更容易定位出错位置。

希望这些改进能够帮助你解决问题!如果问题依然存在,请提供具体的错误信息,我会继续协助你。

  希望如上措施及解决方案能够帮到有需要的你。

  PS:如若遇到采纳如下方案还是未解决的同学,希望不要抱怨&&急躁,毕竟影响因素众多,我写出来也是希望能够尽最大努力帮助到同类似问题的小伙伴,即把你未解决或者产生新Bug黏贴在评论区,我们大家一起来努力,一起帮你看看,可以不咯。

  若有对当前Bug有与如下提供的方法不一致,有个不情之请,希望你能把你的新思路或新方法分享到评论区,一起学习,目的就是帮助更多所需要的同学,正所谓「赠人玫瑰,手留余香」。

☀️写在最后

  如上问题有的来自我自身项目开发,有的收集网站,有的来自读者…如有侵权,立马删除。再者,针对此专栏中部分问题及其问题的解答思路或步骤等,存在少部分搜集于全网社区及人工智能问答等渠道,若最后实在是没能帮助到你,还望见谅!并非所有的解答都能解决每个人的问题,在此希望屏幕前的你能够给予宝贵的理解,而不是立刻指责或者抱怨!如果你有更优解,那建议你出教程写方案,一同学习!共同进步。

  ok,以上就是我这期的Bug修复内容啦,如果还想查找更多解决方案,你可以看看我专门收集Bug及提供解决方案的专栏《CSDN问答解惑-专业版》,都是实战中碰到的Bug,希望对你有所帮助。到此,咱们下期拜拜。

码字不易,如果这篇文章对你有所帮助,帮忙给 bug菌 来个一键三连(关注、点赞、收藏) ,您的支持就是我坚持写作分享知识点传播技术的最大动力。

同时也推荐大家关注我的硬核公众号:「猿圈奇妙屋」 ;以第一手学习bug菌的首发干货,不仅能学习更多技术硬货,还可白嫖最新BAT大厂面试真题、4000G Pdf技术书籍、万份简历/PPT模板、技术文章Markdown文档等海量资料,你想要的我都有!

📣关于我

我是bug菌,CSDN | 掘金 | InfoQ | 51CTO | 华为云 | 阿里云 | 腾讯云 等社区博客专家,C站博客之星Top30,华为云2023年度十佳博主,掘金多年度人气作者Top40,掘金等各大社区平台签约作者,51CTO年度博主Top12,掘金/InfoQ/51CTO等社区优质创作者;全网粉丝合计 30w+;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试真题、4000G PDF电子书籍、简历模板等海量资料,你想要的我都有,关键是你不来拿哇。



http://www.kler.cn/news/309016.html

相关文章:

  • leetcode-4. 寻找两个正序数组的中位数
  • 使用QT编写有图形界面的TCP局域网聊天室(app)
  • QT使用事件事件和绘制事件实现简易时钟
  • java自定义注解
  • 数据结构——二叉搜索树
  • linux-网络管理-防火墙配置
  • 面试真题-TCP的三次握手
  • STM32外设-0.96寸OLED显示屏
  • [数据集][目标检测]男女性别检测数据集VOC+YOLO格式9769张2类别
  • AI重塑视觉体验:将图像与视频转化为逼真可编辑的3D虚拟场景
  • 使用jackson将xml和对象、List相互转换
  • 碰撞检测 | 图解线段几何与线段相交检测原理(附ROS C++可视化)
  • pandas读取Excel保留空格与数字前置0
  • UNI-APP 富文本编辑器,可以对图片、文字格式进行编辑和混排。
  • python之openpyxl模块——实现Excel表格的处理(万字教学,全网最全,超详细!)
  • 深度解析代理IP地址与端口:定义及高效获取方法
  • 硬件工程师笔试面试——保险丝
  • 运维面试题1
  • 微信小程序中实现类似于 ECharts 的图表渲染及优化
  • Java项目实战II基于Java+Spring Boot+MySQL的作业管理系统设计与实现(源码+数据库+文档)
  • OpenAI o1——人工智能推理能力的飞跃,助力高级问题解决
  • Spark处理结构化数据:DataFrame、DataSet、SparkSQL
  • Linux5-echo,>,tail
  • 关于前后端跨域访问的问题
  • C语言16--宏定义和关键字
  • 【FFmpeg应用场景概述】
  • Nginx实用篇:实现负载均衡、限流与动静分离
  • source ~/.bash_profile有什么用
  • MySQL record 05 part
  • mysql5.7常用操作命令手册