本文由Markdown语法编辑器编辑完成.
1. 需求背景
已知已经获取到了一幅dicom医学图像的像素文件, 它的格式为.raw文件. 由于市面上通用的dicom viewer软件, 都只能识别dicom格式的文件. 因此, 如何将这个.raw文件, 还原为能够被大部分软件识别的医学dicom文件, 从而进行相应的显示和操作.
2. 需求调研
2.1 本地已有软件尝试
我本地的电脑中, 已经安装有三款dicom viewer的软件, 分别是: 3D Slicer, RadiAnt, Aliza.
用这三款软件, 载入本地的.raw文件时, 均无法正常的加载. 软件界面都会提示不同的错误信息, 大意都是文件不是正常的dicom文件, 无法加载. 可见这三个软件, 在加载文件时, 都会对文件的header进行校验, 如果不存在, 或无法被正确地识别, 都会导致图像无法正常被加载和显示.
2.2 google查找其他软件和方法
通过从google上, 按照: “raw data to dicom” 的关键词来搜索, 可以查询到一些关键信息.
根据: https://www.researchgate.net/post/Dear_all_can_anybody_introduce_me_a_software_to_can_convert_raw_formatted_images_to_dcm_format 链接, 可以看到, 目前市面上是有几款软件, 可以在无dicom的信息头的情况下, 直接显示.raw格式的纯数据文件的. 比较有代表性的两个软件是: ImageJ和XMedCon.
从网上下载了ImageJ的安装包, 并且安装后, 载入本地的.raw文件。出乎意料的是,看起来是一个比较简单轻量级的dicom viewer软件,居然就可以显示出.raw的文件。而RadiAnt, 3d slicer等都无法加载。而且,ImageJ软件,除了可以显示.raw文件,还可以导出该文件成为一个dicom文件。基本上也就实现了我的需求。但是,这个操作毕竟是手动进行的,因此就无法批量的,自动化的进行。
要知道批量和自动化操作,是软件工程师比较追求的。因为只要满足了这两条,那么这些繁琐的操作,便可以交给机器去完成。而自己就可以将更多地精力,放在别的更重要的工作上。这个听起来,也就是人工智能诞生的很重要的原因。
3. 解决方案
3.1 imageJ
https://imagej.nih.gov/ij/
3.2 SimpleITK
simpleITK的examples中提供了一个例子, 是关于读取raw文件的demo.
import argparse
import os
import tempfile
import SimpleITK as sitk
def read_raw(binary_file_name, image_size, sitk_pixel_type, image_spacing=None,
image_origin=None, big_endian=False):
"""
Read a raw binary scalar image.
Parameters
----------
binary_file_name (str): Raw, binary image file content.
image_size (tuple like): Size of image (e.g. [2048,2048])
sitk_pixel_type (SimpleITK pixel type: Pixel type of data (e.g.
sitk.sitkUInt16).
image_spacing (tuple like): Optional image spacing, if none given assumed
to be [1]*dim.
image_origin (tuple like): Optional image origin, if none given assumed to
be [0]*dim.
big_endian (bool): Optional byte order indicator, if True big endian, else
little endian.
Returns
-------
SimpleITK image or None if fails.
"""
pixel_dict = {sitk.sitkUInt8: 'MET_UCHAR',
sitk.sitkInt8: 'MET_CHAR',
sitk.sitkUInt16: 'MET_USHORT',
sitk.sitkInt16: 'MET_SHORT',
sitk.sitkUInt32: 'MET_UINT',
sitk.sitkInt32: 'MET_INT',
sitk.sitkUInt64: 'MET_ULONG_LONG',
sitk.sitkInt64: 'MET_LONG_LONG',
sitk.sitkFloat32: 'MET_FLOAT',
sitk.sitkFloat64: 'MET_DOUBLE'}
direction_cosine = ['1 0 0 1', '1 0 0 0 1 0 0 0 1',
'1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1']
dim = len(image_size)
header = ['ObjectType = Image\n'.encode(),
('NDims = {0}\n'.format(dim)).encode(),
('DimSize = ' + ' '.join([str(v) for v in image_size]) + '\n')
.encode(),
('ElementSpacing = ' + (' '.join([str(v) for v in image_spacing])
if image_spacing else ' '.join(
['1'] * dim)) + '\n').encode(),
('Offset = ' + (
' '.join([str(v) for v in image_origin]) if image_origin
else ' '.join(['0'] * dim) + '\n')).encode(),
('TransformMatrix = ' + direction_cosine[dim - 2] + '\n')
.encode(),
('ElementType = ' + pixel_dict[sitk_pixel_type] + '\n').encode(),
'BinaryData = True\n'.encode(),
('BinaryDataByteOrderMSB = ' + str(big_endian) + '\n').encode(),
# ElementDataFile must be the last entry in the header
('ElementDataFile = ' + os.path.abspath(
binary_file_name) + '\n').encode()]
fp = tempfile.NamedTemporaryFile(suffix='.mhd', delete=False)
print(header)
# Not using the tempfile with a context manager and auto-delete
# because on windows we can't open the file a second time for ReadImage.
fp.writelines(header)
fp.close()
img = sitk.ReadImage(fp.name)
os.remove(fp.name)
return img
parser = argparse.ArgumentParser()
parser.add_argument('raw_file_name', help='path to raw binary image file')
parser.add_argument('out_file_name',
help='output file name when image read as little endian')
parser.add_argument("big_endian", type=lambda v: v.lower() in {"1", "true"},
help="\'false\' for little ending or \'true\'for big "
"endian")
parser.add_argument('sitk_pixel_type',
help="SimpleITK pixel type (e.g. sitk.sitkUInt16)")
parser.add_argument('sz', nargs='+', help="image size, x,y,...",
type=int)
args = parser.parse_args()
string_to_pixelType = {"sitkUInt8": sitk.sitkUInt8,
"sitkInt8": sitk.sitkInt8,
"sitkUInt16": sitk.sitkUInt16,
"sitkInt16": sitk.sitkInt16,
"sitkUInt32": sitk.sitkUInt32,
"sitkInt32": sitk.sitkInt32,
"sitkUInt64": sitk.sitkUInt64,
"sitkInt64": sitk.sitkInt64,
"sitkFloat32": sitk.sitkFloat32,
"sitkFloat64": sitk.sitkFloat64}
# Read the image using both big and little endian
image = read_raw(binary_file_name=args.raw_file_name,
image_size=args.sz,
sitk_pixel_type=string_to_pixelType[args.sitk_pixel_type],
big_endian=args.big_endian)
sitk.WriteImage(image, args.out_file_name)
if "SITK_NOSHOW" not in os.environ:
sitk.Show(image, 'raw converted')
参考链接:
https://simpleitk.readthedocs.io/en/master/link_RawImageReading_docs.html