前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >超声图像手臂动脉检测及弹性分析

超声图像手臂动脉检测及弹性分析

作者头像
Srlua
发布2024-11-26 09:57:28
发布2024-11-26 09:57:28
9100
代码可运行
举报
文章被收录于专栏:CSDN社区搬运
运行总次数:0
代码可运行

动脉血管检测

YOLOv7-tiny的训练与测试

全自动的超声图像动脉血管弹性分析首先应该将动脉血管检测出来,检测出来之后,我们才能对其进行定位,并分析其弹性。在这里我们提供了一些手臂动脉血管的超声图像以及视频,经过图像增强后并打上了标签,检测模型采用的时YOLOv7-tiny,经过训练之后可以获得一个比较精准的动脉血管实时检测模型。

图像数据位于项目datasets文件夹中,至于YOLOv7-tiny如何进行训练,网上大量的教程,再次不做赘述。

检测效果如图苏所示。

血管弹性分析

首先我们来说清楚什么是血管弹性分析。血管弹性就是血管收缩后能否再次扩大的能力,一般老年人的血管弹性较差,在病人接受检查时,超声探测头放在病人手臂上,看到的是病人的手臂横切面,所以动脉是呈现出一个类似于圆形的状态,也就是上面图片中检测到的东西。动脉在不停的收缩,所以这个圆的半径是不停的放大缩小放大缩小的。如何判断其弹性呢?一般在接受检查时,首先计算其动脉的半径变化,接着会在病人的胳膊上绑上一个橡胶带,让血管收缩,这时这个圆会逐渐缩小,经过一分钟,然后将橡胶带松开,这时血管会猛然扩大,如果该患者动脉血管弹性好,扩大后的半径要比绑橡胶带之前要大一些,如果患者血管弹性较差,则和绑橡胶带之前的半径相似,也就是说,我们通过对比帮橡胶带前后血管的大小变化来判断其血管弹性,从而分析疾病。

血管定位跟踪

我们检测到动脉血管后,要实时分析其半径,这是一个持续的过程,我们这时采用了KCF跟踪算法来跟踪此动脉血管,以便在此区域中进行分析。其中我们做了一些优化,来提高跟踪的鲁棒性,代码如下。

代码语言:javascript
代码运行次数:0
复制
        if len(det):#是否有目标
            # Rescale boxes from img_size to im0 size
            det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()
            # 置信度排序
            det = det[det[:, 4].argsort(descending=True)]
            # 最大置信度目标
            max_conf_det = det[0]
            #for *xyxy, conf, cls in reversed(max_conf_det):#循环这张图片里的所有目标
            conf = max_conf_det[4]
            xyxy = max_conf_det[:4]
            cls = max_conf_det[5]
            if conf > 0.5:
                if not tracking_started:
                    # 停止检测,开始跟踪
                    tracking_started = True
                    # 初始化跟踪器
                    tracker = cv2.TrackerKCF_create()
                    bbox = (int(xyxy[0]), int(xyxy[1]), int(xyxy[2] - xyxy[0]), int(xyxy[3] - xyxy[1]))
                    #在bbox的上下左右均扩大1.1倍
                    bbox = (
                        int(bbox[0] - bbox[2] * 0.05),
                        int(bbox[1] - bbox[3] * 0.05),
                        int(bbox[2] * 1.1),
                        int(bbox[3] * 1.1)
                    )
                    tracker.init(im0, bbox)

血管弹性分析

当跟踪到动脉血管后,我们对该目标区域进行分析,目标区域如下图所示。

像素点统计

弹性分析,在超声图像中其实就是看这块黑色圆圈区域像素点的多少,具体的通过阈值分割方法对目标区域进行一个分割,再通过连通区域分析以及像素点的统计,来进行判断,代码如下:

代码语言:javascript
代码运行次数:0
复制
def segmentation(box_img):
    # Convert to grayscale
    gray_img = cv2.cvtColor(box_img, cv2.COLOR_BGR2GRAY)
    # 大津阈值
    # _, thresh_img = cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
    # 自定义阈值(根据您的需求进行调整)
    custom_threshold = 80
    # 使用自定义阈值进行二值化
    _, thresh_img = cv2.threshold(gray_img, custom_threshold, 255, cv2.THRESH_BINARY)
    # 反转二值图像的颜色
    thresh_img = cv2.bitwise_not(thresh_img)
    # 连通组件标记
    _, labels, stats, centroids = cv2.connectedComponentsWithStats(thresh_img)
    # 找到最大连通区域的标签
    largest_label = np.argmax(stats[1:, cv2.CC_STAT_AREA]) + 1
    # 创建一个与原始图像大小相同的掩码图像
    mask = np.zeros_like(thresh_img)
    # 将最大连通区域对应的像素设置为白色(255)
    mask[labels == largest_label] = 255
    # 删除其他连通区域
    thresh_img = cv2.bitwise_and(thresh_img, mask)
    # 闭运算来填充小的黑色区域
    kernel = np.ones((10, 10), np.uint8)
    thresh_img = cv2.morphologyEx(thresh_img, cv2.MORPH_CLOSE, kernel)
    # 统计白色像素
    pixel = np.count_nonzero(thresh_img == 255)  # Count non-zero pixels as vessel area
    # Print vessel area
    #print(f"面积: {pixel} pixels")
    return thresh_img,pixel

可视化效果如下图所示,第一列为绑橡胶带前,第二列为绑橡胶带中,第三列为松橡胶带后。可以看出松绑之后明显要比帮之前要大,说明这个人血管弹性还是比较良好的。

但是这种算法有一个弊端,可以看出由于传统图像算法在进行分割时,往往会将红框中的非动脉区域错误分割,这也是不可避免的,所以我们提出了一种新的弹性分析算法。

动脉半径实时预测

该算法的核心思想为通过确定目标圆心,接着通过圆心计算半径。 圆心决定策略:由最大连通区域通过图像矩得到白色区域重心,并通过与检测框求平均从而决定圆心。 半径计算策略:以圆心为中心向四周以平均角度放出若干条射线,当到达背景区域则记录此射线长度。通过Z-score法过滤异常值,将过滤过的半径集合求平均得到半径。 该算法稍微复杂一些,具体代码如下,代码中的注释也解释了该算法具体如何实施的:

代码语言:javascript
代码运行次数:0
复制
def detect_blood_vessel_radius(thresh_img_normal):

    # 在二值化图像中找到轮廓
    contours, _ = cv2.findContours(thresh_img_normal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    if len(contours) == 0:
        print("未找到血管轮廓。")
        return None

    # 获取最大轮廓(假设它是血管)
    largest_contour = max(contours, key=cv2.contourArea)
    largest_contour = largest_contour.reshape(len(largest_contour), 2)
    # 绘制血管横截面的轮廓
    # cv2.drawContours(image, [largest_contour], -1, (0, 255, 0), 2)  # 这里用绿色绘制轮廓
    # cv2.imshow("Blood Vessel Contour", image)
    # cv2.waitKey(0)
    # cv2.destroyAllWindows()

    # 找到最大轮廓(血管)的中心点,这段代码是计算最大轮廓(血管)的中心点坐标的方法,基于图像矩(Moments)。图像矩是对图像像素值的统计特征,其中 m00 表示图像的零阶矩(图像的总和),m10 表示图像的一阶矩(关于 x 轴的总和),m01 表示图像的一阶矩(关于 y 轴的总和)。
    M = cv2.moments(largest_contour)
    center_x = int(M["m10"] / M["m00"])
    center_y = int(M["m01"] / M["m00"])

    img_center_x = thresh_img_normal.shape[1] // 2
    img_center_y = thresh_img_normal.shape[0] // 2

    center_x_last = (center_x + img_center_x) // 2
    center_y_last = (center_y + img_center_y) // 2

    # 从中心发射线段与血管轮廓相交
    num_rays = 8  # 可根据需要调整线段数量
    angles = np.linspace(0, 2 * np.pi, num_rays, endpoint=False)  # 平分角度
    boundary_points = []
    rs = []

    # 外点线段长度
    n = int(math.sqrt((thresh_img_normal.shape[0] // 2) ** 2 + (thresh_img_normal.shape[1] // 2) ** 2) + 50)

    for angle in angles:
        # 1000是为了射线足够长
        a = 1000 * np.cos(angle)
        ray_x = int(center_x_last + n * np.cos(angle))  # 计算以中心点为起点、长度为 1000 的线段在 x 轴方向的偏移量
        ray_y = int(center_y_last + n * np.sin(angle))  # 计算以中心点为起点、长度为 1000 的线段在 y 轴方向的偏移量

        # 获取直线上的所有点
        line_points = np.linspace((center_x_last, center_y_last), (ray_x, ray_y), num=100).astype(int)

        # 遍历直线上的所有点,查询像素值,找到边界点

        for point in line_points:
            x, y = point[0], point[1]
            if x>=thresh_img_normal.shape[1]:
                x= thresh_img_normal.shape[1]-1
            if y>=thresh_img_normal.shape[0]:
                y= thresh_img_normal.shape[0]-1
            pixel_value = thresh_img_normal[y, x]  # OpenCV的像素访问是 (y, x)
            if pixel_value == 0:
                boundary_points.append((x, y))
                r = int(np.sqrt((center_x_last - x) ** 2 + (center_y_last - y) ** 2))
                rs.append(r)
                break
    if all(i==0 for i in rs):
        print("无目标!")
        return 0,(center_x_last,center_y_last)
    else:
        # 过滤掉异常值的半径(可根据需要调整阈值)
        # 计算平均值和标准差
        mean_radius = np.mean(rs)
        std_radius = np.std(rs)

        # 设置Z-score的阈值(例如Z-score绝对值超过2则认为是离群值)
        z_score_threshold = 2

        # 根据Z-score过滤离群值
        acceptable_radii = [r for r in rs if abs((r - mean_radius) / std_radius) <= z_score_threshold]

        # 计算平均半径
        average_radius = int(np.mean(acceptable_radii))
        print(average_radius)
        # # 在图像中标记边界点
        # for point in boundary_points:
        #     x, y = point
        #     cv2.circle(thresh_img_normal, (x, y), 3, (0, 255, 0), -1)  # 绘制半径为3的绿色实心圆
        # cv2.imwrite(r"E:\maibang\yolo\yolov7-main\image\huizhi.jpg",thresh_img_normal)

        # cv2.circle(image, center, radius, color, thickness)
        # color = (0, 255, 0)  # 绘制圆的颜色,格式为 (B, G, R);这里使用绿色
        # thickness = 2  # 绘制圆的线条宽度,为负值表示实心圆
        # cv2.circle(image, (center_x_last, center_y_last), average_radius, color, thickness)
        # cv2.imwrite(r"E:\maibang\yolo\yolov7-main\image\result.jpg", image)
        return average_radius,(center_x_last,center_y_last)

该算法可以实时计算血管的半径以及收缩状况,我们并将圆心和半径组成的圆实时可视化显示,效果图如所示:

具体的演示效果可以参考演示视频。

使用说明

我们在附件中放入了整个项目文件,包括所有的数据(图像数据和视频数据)以及代码。代码结构为YOLOv7的代码结构,环境参考YOLOv7环境,我们在文件中detect.py中进行了修改,运行detect.py文件进行展示。

​​

希望对你有帮助!加油!

若您认为本文内容有益,请不吝赐予赞同并订阅,以便持续接收有价值的信息。衷心感谢您的关注和支持!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-11-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 动脉血管检测
    • YOLOv7-tiny的训练与测试
  • 血管弹性分析
    • 血管定位跟踪
    • 血管弹性分析
      • 像素点统计
      • 动脉半径实时预测
  • 使用说明
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档