CBCT的分类结果是以nii.gz格式存储的,为此要对分类结果进行展示就必须对nii.gz格式文件进行解析,然后以合适的方式进行可视化。
同样的,前端js方面依旧缺乏解析的库,为此想了一个代替方案,通过后端python的SimpleITK 库对nii.gz格式进行解析,并由此生成不同视图的图片,在前端展示dicom文件时将对应的分类图片叠加上去,以此来展示分类的结果。
而在这个过程中遇到了一些困难,nii.gz文件是用0-33的数字来标记不同牙齿或部位的,而前端显示应该用不同颜色来标记不同牙齿或部位,为此需要生成一定数量的具有区分度的颜色,为此编写相应程序,生成了33种不同的具有区分度的颜色,并且手动调整了牙槽骨对应的颜色,使得更有区分性和辨识度。
同时在展示的时候为了不对后面的dicom文件进行遮挡,图片需要具有透明度,而刚开始采用的jpg文件,并不支持透明度,为此修改为转化为具有透明度的png格式。
import SimpleITK as sitk
import numpy as np
import os
from PIL import Image
def read_nii(file_path):
ds = sitk.ReadImage(file_path) # 读取nii数据的第一个函数sitk.ReadImage
# print('ds: ', ds)
data = sitk.GetArrayFromImage(ds) # 把itk.image转为array
# print('data: ', data)
print('shape_of_data', data.shape)
spacing = ds.GetSpacing() # 三维数据的间隔
# print('spacing_of_data', spacing)
return data
# 从十六进制的颜色得到RGB颜色
def color(value):
digit = list(map(str, range(10))) + list("ABCDEF")
if isinstance(value, tuple):
string = '#'
for vi in value:
a1 = vi // 16
a2 = vi % 16
string += digit[a1] + digit[a2]
return string
elif isinstance(value, str):
a1 = digit.index(value[1]) * 16 + digit.index(value[2])
a2 = digit.index(value[3]) * 16 + digit.index(value[4])
a3 = digit.index(value[5]) * 16 + digit.index(value[6])
return [a1, a2, a3]
def getRGBColor(colorArray):
colorMapRGB = []
for i in range(len(colorArray)):
colorMapRGB.append(color(colorArray[i]))
return colorMapRGB
# niiDataArray(读取nii文件获得的三维数组)
# type(需要得到的图片的类型有1:横断面上下切、2矢状面左右切、3冠状面前后切)
# imgId(获得的图片在该面的位置)
def getImgFromNiiDataArray(niiDataArray, type, imgId, colorMap):
shape = niiDataArray.shape
if type == 1:
imgData = np.array(niiDataArray[imgId, :, :])
elif type == 2:
imgData = np.array(niiDataArray[:, imgId, :])
elif type == 3:
imgData = np.array(niiDataArray[:, :, imgId])
imgR = np.zeros(imgData.shape)
imgG = np.zeros(imgData.shape)
imgB = np.zeros(imgData.shape)
imgA = np.zeros(imgData.shape)
for i in range(1, 33):
imgR[imgData == i] = colorMap[i - 1][0]
imgG[imgData == i] = colorMap[i - 1][1]
imgB[imgData == i] = colorMap[i - 1][2]
imgA[imgData == i] = 255
r = Image.fromarray(imgR).convert('L')
g = Image.fromarray(imgG).convert('L')
b = Image.fromarray(imgB).convert('L')
a = Image.fromarray(imgA).convert('L')
image = Image.merge('RGBA', (r, g, b, a))
return image
# 以png格式输出nii文件中三种视图的所有图片
# 会在target目录下生成三个文件夹(横断面,矢状面,冠状面)
# 里面装了对应得一系列png图片
def exportAllImgByPNG(niiDataArray, targetPath):
if os.path.exists(os.path.join(targetPath, '横断面')) == False:
os.mkdir(os.path.join(targetPath, '横断面'))
if os.path.exists(os.path.join(targetPath, '矢状面')) == False:
os.mkdir(os.path.join(targetPath, '矢状面'))
if os.path.exists(os.path.join(targetPath, '冠状面')) == False:
os.mkdir(os.path.join(targetPath, '冠状面'))
dataShape = niiDataArray.shape
#
for i in range(dataShape[0]):
imge = getImgFromNiiDataArray(niiDataArray, 1, i, rgbColorMap)
imge.save(os.path.join(targetPath, '横断面', '_' + str(i) + '.png'))
for i in range(dataShape[1]):
imge = getImgFromNiiDataArray(niiDataArray, 2, i, rgbColorMap)
imge.save(os.path.join(targetPath, '矢状面', '_' + str(i) + '.png'))
for i in range(dataShape[2]):
imge = getImgFromNiiDataArray(niiDataArray, 3, i, rgbColorMap)
imge.save(os.path.join(targetPath, '冠状面', '_' + str(i) + '.png'))
# 33个颜色,分别对应牙槽骨和32颗牙齿的颜色,0为牙槽骨的颜色,1~32为牙齿的颜色
colorMap = ['#00AA00', '#F93408', '#F57A34', '#F7951E', '#F6C238', '#FBE92F', '#E5F827', '#B6F313', '#97F922',
'#75F72A',
'#35F80A', '#23FD23', '#1DF645', '#37F57C', '#03FE8C', '#29FEC4', '#36FBE9', '#2CEBFE', '#29C1FA',
'#048BFB',
'#1B6CFA', '#153FFE', '#2F2FFD', '#3A10F4', '#610DF4', '#8F0AFD', '#B714F4', '#DE11F3', '#FE30EB',
'#FD19BF',
'#FB279B', '#FD1C6E', '#FD0532'
]
if __name__ =='__main__':
rgbColorMap = getRGBColor(colorMap) # 得到各部位颜色
# print(rgbColorMap)
niifile_path = "./lvsili_label_tooth.nii.gz"
# file_path = "./lvsiling_label_alveolar.nii.gz"
niiDataArray = read_nii(niifile_path)
exportAllImgByPNG(niiDataArray, './teethImg')
# imge = getImgFromNiiDataArray(niiDataArray, 1, 100, rgbColorMap)
# imge.show()
# imge.save('./dog.png')