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')