LIDC数据集共有1012例数据(CT数据文件类型为Dicom),这里不讲Dicom的数据读取,只介绍XML标注文件及处理方法。
每例数据下面有两个文件夹,其中一个文件夹是侧面的CT(里面大概只有1-2个文件),一般不采用这个文件夹中的数据参与训练;另一个文件是肺尖——肺底方向的横向切片,为全肺切片。
文件夹中包含一个标注信息文件,(一般命名为 XXX.xml),XML中包含大量的标注信息。接下来对这部分标注信息进行介绍及给出相关处理代码。
标注信息包含三类:<unblindedReadNodule>, <unblindedReadNodule(small_nodules)>, <nonNodule>,其中第一类第二类(似乎)没有明确的区分。
第一类,大结节:在XML标注中读取"unblindedReadNodule"的Tag;读取"roi"的Tag;读取"imageSOP_UID" 得到切片信息,对应Dicom的SOPInstanceUID,读取"edgeMap"的Tag并读取其中的"xCoord"和"yCoord"的Tag,得到大结节Mask的横纵坐标。
第二类,小结节:与第一类的读取几乎完全一致,唯一区别在于xCoord和yCoord仅包含一个坐标(即整个Mask只包含一个坐标点)。但从可视化来看尽管结节较小,但一个坐标肯定无法包含整个小肺结节的Mask,这对分割任务是非常不利的,不清楚这个地方如何处理。
第三类,非肺结节:"nonNodule" Tag下的?"imageSOP_UID"和"locus"下的"xCoord"和"yCoord"。其中,非肺结节这一类标注的含义网上没有相关的参考。医学上没有“非肺结节”这一名词,正常理解没有肺结节的区域就是非肺结节区域,为何还需要“非肺结节”这一标注?经过可视化,我认为这一标注是给出“与肺结节特征极其相似但并非肺结节的区域”,也就相当于分割任务中的“困难样本”。这一标注可以省去深度学习训练时Hard?Mining的难度。
拥有这些标注后就可以得到肺部区域的肺结节Mask,其中可以包含“非肺结节”信息。
处理代码:
大结节和小结节
import pydicom as dicom
import os
import cv2
import numpy as np
import lxml.etree as etree
def xml_reader(path):
xmlns = '{http://www.nih.gov}'
tree = etree.parse(path)
Res = tree.findall(xmlns + 'ResponseHeader')[0]
if tree.findall(xmlns + 'readingSession')==[]: return {}
i = tree.findall(xmlns + 'readingSession')[0]
Nodules = []
if i.findall(xmlns + 'unblindedReadNodule')==[]: return {}
for j in i.findall(xmlns + 'unblindedReadNodule'):
Nodule = []
for k in j.findall(xmlns + 'roi'):
roi = {}
roi['imageSOP_UID'] = k.findall(xmlns + 'imageSOP_UID')[0].text
roi['roi'] = []
for l in k.findall(xmlns + 'edgeMap'):
x = int(l.findall(xmlns + 'xCoord')[0].text)
y = int(l.findall(xmlns + 'yCoord')[0].text)
roi['roi'].append([x,y])
Nodule.append(roi)
Nodules.append(Nodule)
return Nodules
非肺结节:
from xml.dom.minidom import parse
def parseXML(xml_path):
non_nodules_coor = {}
domTree = parse(xml_path)
# print(domTree) # <xml.dom.minidom.Document object at 0x0000000003FD4E88>
# print(type(domTree)) # <class 'xml.dom.minidom.Document'>
# 文档根元素
rootNode = domTree.documentElement
print(rootNode.nodeName) # LidcReadMessage
# 所有医生标注
readingSessions = rootNode.getElementsByTagName("readingSession")
# print(readingSessions)
for rS in readingSessions:
non_nodules = rS.getElementsByTagName("nonNodule")
# 解析非结节
for non_nodule in non_nodules:
#this_dict = {}
Z_w = non_nodule.getElementsByTagName("imageSOP_UID")[0].childNodes[0].data
locus = non_nodule.getElementsByTagName("locus")[0]
X_v = locus.getElementsByTagName("xCoord")[0].childNodes[0].data
Y_v = locus.getElementsByTagName("yCoord")[0].childNodes[0].data
non_nodules_coor[Z_w] = [X_v,Y_v]
# coor = (int(X_v), int(Y_v), float(Z_w))
# if coor not in non_nodules_coor:
# non_nodules_coor.append(coor)
#non_nodules_coor.append(this_dict)
print(r"Finish ", xml_path)
return non_nodules_coor
参考博客:
LIDC数据集由xml提取小结节和非结节_dreamandgo的博客-CSDN博客
LIDC-IDRI数据集的dicom文件XML标注读取_qq_782808845的博客-CSDN博客
|