news 2026/2/9 1:25:18

基于深度学习的甲状腺超声自动识别

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于深度学习的甲状腺超声自动识别

博主简介:擅长数据搜集与处理、建模仿真、程序设计、仿真代码、论文写作与指导,毕业论文、期刊论文经验交流。

✅成品或者定制,扫描文章底部微信二维码。


(1)基于级联区域卷积神经网络的甲状腺及周围组织自动检测与分割方法

甲状腺超声检查是临床评估甲状腺疾病的首选影像学方法,但超声图像中甲状腺与周围组织的精确识别和分割是一项复杂的任务,传统人工标注方式耗时且容易产生主观偏差。本研究提出了一种基于级联区域卷积神经网络的甲状腺及其周围组织自动检测与分割方法,旨在减轻超声医生的工作负担并提高检查的标准化程度。该方法以超声动态视频为输入,能够同时完成颈部多种解剖结构的检测和分割任务,包括甲状腺左叶、右叶、峡部、颈前肌肉、气管、颈动脉、颈内静脉、食管、环状软骨以及甲状腺内血管等。级联区域卷积神经网络采用多阶段检测策略,第一阶段生成大量候选区域提议,后续阶段逐步对这些提议进行精细化筛选和边界回归,这种级联结构能够有效提高检测的准确率和定位精度。网络的骨干特征提取部分采用特征金字塔结构,能够融合不同尺度的特征信息,对于超声图像中大小差异悬殊的解剖结构均能保持良好的检测性能。在分割任务中,网络在每个检测到的目标区域内预测像素级的分割掩膜,采用全卷积结构生成与输入尺寸对应的分割图。为训练该深度学习模型,本研究收集了大量甲状腺超声检查视频,每例患者包含五个标准扫描切面的视频数据,由经验丰富的超声科医生对视频帧中的各解剖结构进行精细标注。实验评估采用通用目标检测和实例分割指标,结果表明该方法对甲状腺各叶、颈前肌肉、气管、颈动脉、颈内静脉等主要结构的检测和分割均达到了较高的精度水平,平均精度值超过百分之八十五。与当前先进的实例分割方法相比,级联区域卷积神经网络在检测和分割任务上的综合性能更优,差异具有统计学显著性,证明了该方法在甲状腺超声自动分析中的有效性和优越性。

(2)面向超声视频的甲状腺结节实时检测与跟踪框架设计

甲状腺结节的准确检出是超声诊断的关键步骤,但由于结节形态多样、边界模糊且与正常组织对比度较低,传统静态图像检测方法往往难以达到理想的检测效果。本研究提出了一种面向超声动态视频的甲状腺结节实时检测与跟踪框架,充分利用视频序列的时序信息来提高结节检测的准确性和稳定性。该框架由目标检测模块和目标跟踪模块两部分组成,检测模块负责在每一帧图像中识别和定位甲状腺结节及其周围组织,跟踪模块则负责在连续帧之间建立检测目标的对应关系,实现结节在整个视频序列中的持续追踪。检测模块的设计综合考虑了检测精度和实时性的要求,采用了单阶段目标检测网络架构,该架构直接在特征图上进行目标分类和边界框回归,避免了两阶段方法中区域提议生成的计算开销,能够满足实时处理的需求。跟踪模块采用了基于外观特征和运动预测相结合的关联策略,通过卡尔曼滤波预测目标在下一帧的可能位置,同时利用深度特征计算检测框之间的外观相似度,综合两方面信息进行帧间目标匹配。这种检测与跟踪相结合的框架不仅能够检测到单帧图像中的结节,还能够对同一结节在不同帧中的表现进行关联分析,有效过滤因噪声或伪影导致的误检测,提高整体检测的鲁棒性。本研究收集了大规模甲状腺超声视频数据集用于模型训练和测试,视频来源于近八百例患者,包含超过一千七百个视频片段和两千多个经病理或细针穿刺确认的甲状腺结节。实验结果表明,所提出的检测跟踪框架对甲状腺结节及周围组织的综合检测精度与当前最先进的目标检测方法相当,在外部测试集上也展现出良好的泛化能力,证明了该框架在临床应用中的可行性。

(3)基于时序特征建模的甲状腺结节良恶性分类方法

甲状腺结节的良恶性鉴别是临床诊断的核心问题,准确的分类结果直接影响后续治疗方案的制定。本研究提出了一种基于时序特征建模的甲状腺结节良恶性分类方法,模拟超声医生在动态扫查过程中综合多帧图像信息进行诊断的思维方式。该方法的创新之处在于将结节分类任务建模为视频理解问题,而非传统的单帧图像分类问题,充分利用超声视频中蕴含的丰富时序信息。具体实现上,本研究首先利用检测跟踪框架定位视频中的结节区域,然后对每一帧结节图像提取特征表示。特征提取过程借鉴了甲状腺影像报告与数据系统的临床标准,设计专门的特征编码模块来学习与恶性相关的超声特征,包括回声水平、纵横比、边缘规则性、钙化类型等。每帧图像的特征向量构成一个时序序列,本研究采用时序卷积网络和循环神经网络相结合的架构对这一特征序列进行建模,捕获结节在不同扫描角度和深度下的表现变化规律。

import numpy as np import torch import torch.nn as nn import torch.nn.functional as F from torchvision.models import resnet50 from torchvision.ops import roi_align, nms class FeaturePyramidNetwork(nn.Module): def __init__(self, in_channels_list, out_channels=256): super(FeaturePyramidNetwork, self).__init__() self.lateral_convs = nn.ModuleList([ nn.Conv2d(in_ch, out_channels, 1) for in_ch in in_channels_list]) self.output_convs = nn.ModuleList([ nn.Conv2d(out_channels, out_channels, 3, padding=1) for _ in in_channels_list]) def forward(self, features): laterals = [conv(f) for conv, f in zip(self.lateral_convs, features)] for i in range(len(laterals) - 2, -1, -1): laterals[i] += F.interpolate(laterals[i+1], size=laterals[i].shape[-2:], mode='nearest') return [conv(lat) for conv, lat in zip(self.output_convs, laterals)] class CascadeRCNNHead(nn.Module): def __init__(self, in_channels=256, num_classes=12, num_stages=3): super(CascadeRCNNHead, self).__init__() self.num_stages = num_stages self.bbox_heads = nn.ModuleList() self.mask_heads = nn.ModuleList() for stage in range(num_stages): self.bbox_heads.append(nn.Sequential( nn.Linear(in_channels * 7 * 7, 1024), nn.ReLU(), nn.Linear(1024, 1024), nn.ReLU())) self.mask_heads.append(nn.Sequential( nn.Conv2d(in_channels, 256, 3, padding=1), nn.ReLU(), nn.Conv2d(256, 256, 3, padding=1), nn.ReLU(), nn.ConvTranspose2d(256, 256, 2, stride=2), nn.ReLU(), nn.Conv2d(256, num_classes, 1))) self.cls_score = nn.Linear(1024, num_classes) self.bbox_pred = nn.Linear(1024, num_classes * 4) def forward(self, roi_features, proposals): for stage in range(self.num_stages): pooled = roi_align(roi_features, proposals, output_size=(7, 7)) flat = pooled.view(pooled.size(0), -1) fc_out = self.bbox_heads[stage](flat) cls_scores = self.cls_score(fc_out) bbox_deltas = self.bbox_pred(fc_out) mask_features = roi_align(roi_features, proposals, output_size=(14, 14)) masks = self.mask_heads[-1](mask_features) return cls_scores, bbox_deltas, masks class NoduleDetector(nn.Module): def __init__(self, num_classes=3): super(NoduleDetector, self).__init__() self.backbone = resnet50(pretrained=True) self.conv1 = nn.Conv2d(2048, 512, 1) self.conv2 = nn.Conv2d(512, 256, 3, padding=1) self.conv3 = nn.Conv2d(256, 128, 3, padding=1) self.cls_head = nn.Conv2d(128, num_classes, 1) self.box_head = nn.Conv2d(128, 4, 1) def forward(self, x): features = self.backbone.conv1(x) features = self.backbone.bn1(features) features = self.backbone.relu(features) features = self.backbone.maxpool(features) features = self.backbone.layer1(features) features = self.backbone.layer2(features) features = self.backbone.layer3(features) features = self.backbone.layer4(features) features = F.relu(self.conv1(features)) features = F.relu(self.conv2(features)) features = F.relu(self.conv3(features)) cls_output = self.cls_head(features) box_output = self.box_head(features) return cls_output, box_output class KalmanTracker: def __init__(self): self.kf = np.eye(7) self.state = np.zeros(7) self.P = np.eye(7) * 10 self.Q = np.eye(7) * 0.01 self.R = np.eye(4) * 1 self.H = np.eye(4, 7) def predict(self): self.state = self.kf @ self.state self.P = self.kf @ self.P @ self.kf.T + self.Q return self.state[:4] def update(self, measurement): y = measurement - self.H @ self.state S = self.H @ self.P @ self.H.T + self.R K = self.P @ self.H.T @ np.linalg.inv(S) self.state = self.state + K @ y self.P = (np.eye(7) - K @ self.H) @ self.P class TIRADSFeatureExtractor(nn.Module): def __init__(self, feature_dim=128): super(TIRADSFeatureExtractor, self).__init__() self.backbone = resnet50(pretrained=True) self.backbone.fc = nn.Identity() self.echo_head = nn.Linear(2048, 4) self.margin_head = nn.Linear(2048, 3) self.shape_head = nn.Linear(2048, 2) self.calcification_head = nn.Linear(2048, 4) self.feature_fc = nn.Linear(2048, feature_dim) def forward(self, x): features = self.backbone(x) echo = self.echo_head(features) margin = self.margin_head(features) shape = self.shape_head(features) calc = self.calcification_head(features) feat_vec = self.feature_fc(features) return feat_vec, echo, margin, shape, calc class TemporalClassifier(nn.Module): def __init__(self, input_dim=128, hidden_dim=256, num_layers=2): super(TemporalClassifier, self).__init__() self.tcn = nn.Sequential( nn.Conv1d(input_dim, hidden_dim, 3, padding=1), nn.ReLU(), nn.Conv1d(hidden_dim, hidden_dim, 3, padding=2, dilation=2), nn.ReLU(), nn.Conv1d(hidden_dim, hidden_dim, 3, padding=4, dilation=4), nn.ReLU()) self.lstm = nn.LSTM(hidden_dim, hidden_dim, num_layers, batch_first=True, bidirectional=True) self.classifier = nn.Sequential( nn.Linear(hidden_dim * 2, 128), nn.ReLU(), nn.Dropout(0.5), nn.Linear(128, 2)) def forward(self, x): x = x.permute(0, 2, 1) tcn_out = self.tcn(x) tcn_out = tcn_out.permute(0, 2, 1) lstm_out, _ = self.lstm(tcn_out) pooled = torch.mean(lstm_out, dim=1) return self.classifier(pooled) class ThyroidDiagnosisSystem: def __init__(self): self.detector = NoduleDetector() self.feature_extractor = TIRADSFeatureExtractor() self.classifier = TemporalClassifier() self.trackers = {} def process_video(self, video_frames): all_detections = [] for frame in video_frames: frame_tensor = torch.FloatTensor(frame).unsqueeze(0).permute(0, 3, 1, 2) cls_out, box_out = self.detector(frame_tensor) detections = self._decode_detections(cls_out, box_out) all_detections.append(detections) tracked_nodules = self._track_nodules(all_detections) return tracked_nodules def _decode_detections(self, cls_out, box_out, conf_thresh=0.5): scores = torch.sigmoid(cls_out).squeeze() boxes = box_out.squeeze().permute(1, 2, 0) mask = scores.max(dim=0)[0] > conf_thresh return {'scores': scores[:, mask], 'boxes': boxes[mask]} def _track_nodules(self, detections): return detections def classify_nodule(self, nodule_crops): features = [] for crop in nodule_crops: crop_tensor = torch.FloatTensor(crop).unsqueeze(0).permute(0, 3, 1, 2) feat, _, _, _, _ = self.feature_extractor(crop_tensor) features.append(feat) feature_seq = torch.stack(features, dim=1) output = self.classifier(feature_seq) prob = torch.softmax(output, dim=1) return prob[0, 1].item()


如有问题,可以直接沟通

👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/3 20:58:11

基于Qwen3-Embedding-0.6B的代码相似度检测系统设计全解析

基于Qwen3-Embedding-0.6B的代码相似度检测系统设计全解析 在软件开发与代码治理实践中,识别重复、抄袭或高度相似的代码片段,是保障代码质量、防范安全风险、提升研发效率的关键环节。传统基于语法树或哈希比对的方法,往往难以捕捉语义等价…

作者头像 李华
网站建设 2026/2/7 8:12:50

大模型教程丨浙大出品必属精品!大模型入门书籍 《大模型基础》开源了!

一、书籍介绍 由浙江大学DAILY实验室毛玉仁研究员、高云君教授领衔撰写的《大模型基础》教材第一版。这本教材为对大语言模型感兴趣的读者系统地讲解相关基础知识、介绍前沿技术。 本书包括传统语言模型、大语言模型架构、提示工程、高效参数、模型编辑、搜索增强增加生成等六…

作者头像 李华
网站建设 2026/2/7 9:25:13

从0到1:基于YOLO的手势识别智能控制系统完整实现(数据集+训练+部署+控制逻辑)

文章目录 毕设助力!从0到1构建基于YOLO的手势识别智能控制系统,让你的毕设技惊四座 一、项目背景:手势识别为啥火? 二、核心技术:YOLO三兄弟怎么选? 1. YOLOv5 2. YOLOv8 3. YOLOv10 三、项目目标:我们要做啥? 四、数据准备:让模型“看懂”手势 1. 数据集来源 2. 数据…

作者头像 李华
网站建设 2026/2/2 20:20:03

机场登机口排队人数监测系统:基于YOLOv5/v8/v10的完整实现与性能对比(附代码+数据集

文章目录 机场登机口排队人数监测毕设全流程:从YOLOv5到YOLOv10的深度学习实战指南 一、课题背景与意义:为什么选这个题目? 二、技术选型:YOLOv5、YOLOv8、YOLOv10怎么选? 三、数据准备与标注:让模型“看懂”登机口场景 3.1 数据集选择 3.2 数据标注 3.3 数据增强 四、模…

作者头像 李华
网站建设 2026/2/6 8:09:59

Paraformer-large实时录音识别:麦克风流式输入实现方法

Paraformer-large实时录音识别:麦克风流式输入实现方法 1. 为什么需要流式识别?离线版的局限在哪里 你可能已经用过那个带Gradio界面的Paraformer-large离线识别镜像——上传一个MP3,点一下“开始转写”,几秒后就看到整段文字出…

作者头像 李华