尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Delphi下用EZDICOM控件快速实现DICOM医学影像读取与显示

Delphi下用EZDICOM控件快速实现DICOM医学影像读取与显示 简介本资源是面向Delphi医疗软件开发者的DICOM图像处理控件集成包专为需要快速嵌入医学影像读取、显示与通信功能的中高级开发者设计解决PACS对接、CT/MRI图像解析及窗宽窗位调节等典型临床应用开发难题。压缩包共136个文件涵盖19个Delphi源码.pas、9个窗体定义.dfm、6个工程文件.dpr、27个位图资源.bmp及5个可执行演示程序.exe其中ezdicom.exe为即开即用的DICOM浏览器source目录提供完整控件源码供二次开发executables目录含编译成品配套Readme.txt与license.txt明确部署指引与授权条款。目前已有198人学习下载。读者可直接复用示例工程快速上手DICOM元数据提取、JPEG/RLE图像解码、灰度映射渲染及DICOM网络通信模块结合lut调色表文件与bmp工具图标高效构建符合临床阅片需求的本地化影像工作站。 做了这么多年 Delphi 医疗影像开发我对 EZDICOM 这个压缩包印象太深了。那时候项目组接到一个需求要把老设备导出的 DICOM 影像在 Windows 下快速预览还得做成一个小工具给放射科用。找遍各种方案最后在一个内部共享盘里翻到了 ezDicom 的压缩包里面有编译好的 ezDICOM.exe 演示程序还有一套可以直接放进 Delphi 使用的 DICOM 控件源码。就是这套东西让我在两天内把功能跑通。今天这篇就围绕 EZDICOM 展开讲讲怎么用 Delphi 重构 DICOM 文件的读取、显示和交互也会把我在日常开发中踩过的坑一并交代清楚。如果你是刚开始接触 DICOM 的 Delphi 开发者或者正在评估“要不要直接用第三方控件来做医学影像处理”这篇文章适合你如果你已经装过 EZDICOM 但在使用和调试时碰到各种疑难杂症后半部分的避坑记录更值得先看一遍。1. 先用起来EZDICOM 到底是什么、能帮你省多少事EZDICOM 是一套面向 Delphi 的 DICOM 文件解析与图像显示控件核心作用是把医学数字影像文件中那些复杂的 Tag、像素数据、灰度映射细节封装成我们熟悉的 Delphi 组件和对象。你不需要从零去解析 DICOM 文件结构不需要自己处理 16 位灰度转 8 位显示的问题也不用手动写 JPEG 压缩帧的解析逻辑这些基础能力控件已经帮你垫了一层。1.1 压缩包里的常见内容大多数版本解压后结构大致如下ezDICOM.exe官方提供的演示程序。可以直接打开 DICOM 文件看效果也可以拿来验证某个文件是否符合 DICOM 标准。ezDICOM.dpk / ezDICOM.dpk 对应各版本 Delphi 的包工程用来编译和安装控件。dcus 或 bpl 目录预编译好的单元文件或运行时包。Docs 目录部分版本带 PDF 或 HTML 帮助文档建议先翻一翻“Image Display”和“Pixel Data”章节。Demo 目录包含读取、列表、图像显示等示例工程。我第一次拿到压缩包时先没有急着安装控件而是直接打开 ezDICOM.exe拖了几个 CT 影像进去确认它确实能解析。这个习惯我一直保留到现在凡是拿到医疗影像相关工具先看演示程序对测试文件的兼容性再决定要不要集成进现有项目。1.2 为什么我选它而不是自己写解析器很多项目在最初规划时都会纠结是自行解析 DICOM还是用开源库还是买商业控件。我当时的判断维度有三点。第一是时间成本。DICOM 标准繁杂公共标签有上千个私有标签更是数不清。如果从零写光是把传输语法、像素数据、标签嵌套这三块理顺就够开发团队忙一个月。用 EZDICOM 这类现成库核心解析逻辑可以直接复用项目重心可以放到业务功能上。第二是调试便利性。EZDICOM 是 Delphi 原生实现不是通过 DLL 包裹一层 API所以在 Delphi 调试器里可以单步跟踪到具体解析代码。遇到画面异常或者标签值不对的情况我能直接看内部状态。这也是我不愿意在中小工具里引入重型商业 SDK 的主要原因。第三是授权和体积。完整的商业医学影像 SDK 功能很强但授权费用高部署文件大。做一个给科室内部用的看片工具没必要为了一个 DICOM 读取功能背上几百万像素处理库。EZDICOM 的轻量特性正好匹配这种“够用就好”的场景。当然EZDICOM 也不是万能的。它对压缩传输语法的支持取决于具体版本有些较老版本只处理未压缩数据碰到 JPEG2000 压缩的 DICOM 文件会直接报错。这个我会在后面的踩坑记录里重点说明。1.3 核心控件和对象常见打包版本中你会看到几个反复出现的类TDICOMFile负责打开、保存 DICOM 文件管理文件头与数据集合。TDICOMImage负责图像像素数据的读取、显示参数设置和图像绘制。TDICOMTag封装单个标签的组号、元素号、VR 类型和值。TDICOMDataSet对应一个完整的数据集通常通过 DICOMFile 的 DataSet 属性访问。从使用角度看你只要掌握 TDICOMFile 做文件层操作、TDICOMImage 做图像层显示绝大多数需求都能覆盖。更细的标签读取通常通过 DataSet.GetTag 或者类似的接口完成。2. Delphi 环境与控件安装全流程EZDICOM 属于老牌控件库不同 Delphi 版本安装方式略有差别但总体思路一致。下面这套流程我在 Delphi 7、XE2、10.4 上都验证过新版本只要注意路径和位数的差异基本不会翻车。2.1 版本兼容性怎么看如果压缩包里直接带了对应 Delphi 版本的 dpk优先使用。比如看到 ezDICOM_10.dpk 或者文件名里带 XE2 字样说明作者已经针对特定版本适配过。但实际下载到的压缩包往往版本比较旧常见情况是只支持 32 位编译目标。Delphi 7 到 XE8 用的都是传统 VCL 工程包安装方式一致到了 Delphi 10.3 之后的版本如果还在用 32 位 Windows 目标也问题不大。需要警惕的是 64 位目标编译不少老控件在 64 位下编译并不顺利会出现指针截断、汇编代码不兼容、隐式类型转换报错等问题。我建议接受一个现实如果你的项目目标是 Win64 大型程序最好直接评估新版商业库或者开源库而不是在 EZDICOM 上花太多时间强行适配。若只是做 32 位的小工具那放心用。2.2 安装步骤以 Delphi XE2 为例大致如下把压缩包解压到固定目录比如 D:\Components\EZDICOM不要在中文路径下安装老控件对中文路径支持不好。打开 Delphi点击 Component - Install Packages确认当前目标平台是 32 位 Windows。使用 File - Open打开对应的 dpk 文件。在工程管理器里右键 dpk 工程选择 Compile。编译成功后再选择 Install把控件注册到 IDE。添加源码路径Tools - Options - Library - Library Path加入 EZDICOM 的 Source 子目录。否则新建工程引用相关单元时会提示找不到文件。安装完成后控件面板一般会多出一个页签或把组件放在现有页签里比如 EZ 或 Images。2.3 最小的读取程序骨架安装成功后拖一个 TDICOMFile 到窗体上再加上一个 TImage 用来显示结果就可以写第一版读取逻辑procedure TForm1.ButtonLoadClick(Sender: TObject); var DICOMFile: TDICOMFile; DICOMImage: TDICOMImage; begin OpenDialog1.Filter : DICOM Files|*.dcm;*.dicom|All Files|*.*; if not OpenDialog1.Execute then Exit; DICOMFile : TDICOMFile.Create(nil); try if DICOMFile.LoadFromFile(OpenDialog1.FileName) then begin DICOMImage : TDICOMImage.Create(nil); try if DICOMImage.LoadFromFile(OpenDialog1.FileName) then begin DICOMImage.DisplayOn(Image1.Canvas, Image1.ClientRect); Caption : 文件读取成功; end; finally DICOMImage.Free; end; end else ShowMessage(无法打开 DICOM 文件); finally DICOMFile.Free; end; end;这段代码核心思路很简单TDICOMFile 负责判断文件头部合法性TDICOMImage 负责把像素数据解码后绘制到 Canvas 上。实际项目中我不会用 TDICOMFile 再创建 TDICOMImage会直接把 TDICOMImage 当作主控件使用因为它的 LoadFromFile 内部已经包含了文件解析和图像数据装载两步。2.4 常见安装后问题安装阶段最常遇到的问题依次是找不到 .dcu、包编译报错、控件没出现在面板上。找不到 .dcu 的解决办法是把 Source 目录加到全局 Library Path不是加在工程 Search Path 里。我不止一次看到有人只改工程设置结果新工程一编译照样报找不到文件最后发现是全局路径没配。编译报错通常分两类一类是单元名大小写不一致另一类是某个函数在新版 Delphi 中已改名或废弃。前者改 uses 列表即可后者一般要看编译日志定位到具体行手动替换 API。十年前的控件拿到 Delphi 10.4 里编译最常见的报错是 AnsiString/PChar 转换问题需要小范围修改源码。控件没出现在面板上的原因大多是安装时选错了包类型。如果编译的是运行时包Runtime Package它不会出现在设计期控件面板你需要找设计期包Design Time Package或者在运行时包编译完成后再到 Component - Install Packages 里手动添加对应的 .bpl 文件。3. DICOM 解析的核心细节Tag、VR 与像素数据用 EZDICOM 写了几个工具后你会发现自己必须补一补 DICOM 格式的基本概念否则遇到显示异常很难定位问题。这节我把最重要的底层细节捋一遍。3.1 DICOM 文件结构三件套一个典型的 DICOM 文件由三部分组成文件头Preamble固定的 128 字节一般全为 0。DICOM 前缀4 字节的字符串“DICM”。数据集Data Set由一系列数据元素Tag VR Length Value组成。EZDICOM 打开文件时会先检查 Preamble 后面那四个字节是不是“DICM”如果不是还会尝试按隐式 VRImplicit VR来解析。这也是为什么有些文件扩展名不是 .dcm 也能被正确识别——因为判断依据是文件头而不是扩展名。Data Set 里的每个元素就是一个 TagTag 由组号Group和元素号Element组成用两个 16 位无符号整数表示。比如患者姓名是 (0010,0010)图像宽度是 (0028,0011)。组号和元素号都是十六进制表示开发时最好记住几个常用 Tag 的十六进制数值。3.2 必背的关键 Tag 速查表我把平时最常用的一批标签整理成一个表。这表格不是让你背而是作为开发时的速查参考Tag名称说明(0008,0060)Modality影像设备类型CT、MR、CR、DX 等(0010,0010)PatientName患者姓名(0010,0020)PatientID患者 ID(0020,000D)StudyInstanceUID检查实例 UID(0020,000E)SeriesInstanceUID序列实例 UID(0028,0002)SamplesPerPixel采样像素数单色为 1彩色为 3(0028,0004)PhotometricInterpretation彩色空间MONOCHROME1、MONOCHROME2、RGB 等(0028,0010)Rows行数(0028,0011)Columns列数(0028,0100)BitsAllocated像素存储位数(0028,0101)BitsStored像素有效位数(0028,0102)HighBit最高有效位(0028,0103)PixelRepresentation0 表示无符号1 表示有符号(0028,1050)WindowCenter窗位(0028,1051)WindowWidth窗宽(0028,1052)RescaleIntercept灰度校正截距(0028,1053)RescaleSlope灰度校正斜率(7FE0,0010)PixelData像素数据本体这个表里窗口和像素位数相关 Tag 最容易被忽略。很多人打开图像后觉得一片黑或一片白十有八九是 BitsStored、HighBit 和 WindowCenter 处理不对。3.3 像素数据解包从 16 位灰度到屏幕显示DICOM 图像最常见的是 16 位无符号灰度数据也就是每个像素占用 2 个字节。等显示到屏幕上时普通显示器只有 8 位灰度能力也就是每像素 1 个字节。因此处理显示逻辑时必须进行位深度转换。EZDICOM 的 TDICOMImage 在内部读取 PixelData 时会根据 BitsAllocated 和 BitsStored 来正确解释每个像素的数值范围。比如 CT 图像常使用 12 位有效位BitsStored12HighBit11但 BitsAllocated16。这意味着读出来的数值虽然存放在 16 位整数里实际有效的只有低 12 位。如果未经转换直接按 16 位最大值做灰度映射图像会显得非常暗因为最大的有效值是 4095而不是 65535。所以在拿像素数据做进一步处理时一定要用 BitsStored 而不是 BitsAllocated 来归一化。EZDICOM 内部已处理这部分但如果你从控件里拿到了原始像素数组去做算法就要自己注意。3.4 Rescale Slope / Intercept 为什么重要CT 影像的像素值实际上是一个与物理密度相关的值但 DICOM 文件中存储的原始值往往带有偏移。医学上更关心的数值是真实 Hounsfield 单位HU计算公式很简单HU StoredPixelValue × RescaleSlope RescaleIntercept大多数 CT 图像的 RescaleIntercept 是 -1024RescaleSlope 是 1。这意味着存储值 0 对应的真实值是 -1024恰好是空气的 HU 值。测量图像中某一点的 CT 值时必须做这个转换否则肺部空气区域会显示成接近 0 的数值而不是 -1000 左右。我曾经见过一个同事把原始像素值直接当作 HU 值去统计病灶增强程度结果整组数据偏差了 1024 个单位最后发现就是漏了 RescaleIntercept 换算。这些参数在 DICOM 解析时不是可选项而是必读项。3.5 读取标签值的代码示例EZDICOM 里读取标签通常通过 DataSet 获取。不同版本 API 略有差异但基本都能支持按 Tag 取字符串或整数值procedure TForm1.DisplayTags(DicomFile: TDICOMFile); var DataSet: TDICOMDataSet; PatientName: string; Modality: string; Rows, Cols: Integer; begin DataSet : DicomFile.DataSet; PatientName : DataSet.GetTagString($0010, $0010); Modality : DataSet.GetTagString($0008, $0060); Rows : DataSet.GetTagInt($0028, $0010); Cols : DataSet.GetTagInt($0028, $0011); Memo1.Lines.Add(患者姓名: PatientName); Memo1.Lines.Add(设备类型: Modality); Memo1.Lines.Add(分辨率: IntToStr(Rows) x IntToStr(Cols)); end;如果某个 Tag 不存在GetTagString 通常会返回空字符串或引发异常。建议在标签读取时增加一个 TryGetTag 或先用 HasTag 判断的方法避免因文件缺失标准标签导致程序崩溃。实际临床 DICOM 文件里缺失标签的情况比想象中多得多。4. 图像显示与交互功能实现读取出像素数据只是第一步真正让医生觉得“好用”的关键在于显示交互。EZDICOM 的 TDICOMImage 已经内置了显示底层能力但窗宽窗位、缩放、平移这些交互逻辑是否要自己写取决于你的业务深度。4.1 把图像画到窗体上TDICOMImage 一般提供了 DisplayOn 之类的方法可以把图像绘制到指定 Canvas 区域上DICOMImage.DisplayOn(Image1.Canvas, Image1.ClientRect);这个方法会做几件事把 DICOM 图像按目标区域等比缩放、应用当前窗宽窗位、把像素数据转换为屏幕可显示的灰度位图然后绘制到 Canvas。也就是说控件内部已经帮你完成了从“医学灰度空间”到“显示器 RGB 空间”的映射。在重绘逻辑里我通常会在 FormResize 事件里重新调用一次 DisplayOn这样窗口拉伸时图像可以跟着自适应。需要注意每次 DisplayOn 都会重新生成位图如果图像特别大比如 3000x3000 的 DR 胸片重新绘制会比较消耗 CPU建议配合双缓冲绘制或者缓存位图来做性能优化。4.2 窗宽窗位的灰度映射窗宽窗位的本质是确定一个灰度映射范围。窗位Window Center是映射范围的中心值窗宽Window Width是映射范围的总宽度。在这个范围外的值被压缩成最黑或最白范围内的值按线性关系映射到 0 到 255 的灰度。默认情况下EZDICOM 会读取 DICOM 文件里的 (0028,1050) 和 (0028,1051) 作为初始窗宽窗位。如果文件里没有这两个标签控件会使用像素值的最小值和最大值来自动计算一个显示范围。实际开发时我建议把窗宽窗位调整做成鼠标拖拽交互。可以这样设计鼠标左键拖拽调整窗位。鼠标右键水平拖拽或配合 Shift 键调整窗宽。双击恢复到默认窗宽窗位。手工调整窗宽窗位时把鼠标移动量映射成窗位变化量用的系数很关键一般可以设成鼠标移动 1 像素对应 1 个 CT 值。但遇到乳腺钼靶这类高分辨率图像时系数要调大一些否则调半天没反应。4.3 缩放与平移缩放采用鼠标滚轮是用户最习惯的方式。实现思路是先记录当前缩放比例再根据滚轮 Delta 值调整比例最后以鼠标所在位置为中心重新绘制图像。我用过两种实现路径一种是在 TDICOMImage 上设置一个 Zoom 属性让控件内部负责重新绘制。优点是代码量少但要做到“以鼠标位置为缩放中心”比较麻烦因为控件内部默认以图像中心为基准。另一种是自己维护一个视图变换矩阵把 DICOM 图像先渲染到一个大位图上再通过 StretchBlt 把位图按当前缩放比例绘制到目标区域。这种方式灵活性高可以实现平滑的平移和缩放但代码量大一些。对于中小型工具建议先用第一种思路快速实现等有性能问题再升级到第二种。4.4 测量与标注医生在看片时经常需要测量两点距离或者角度。DICOM 文件里和实际物理尺寸有关的参数是 PixelSpacing (0028,0030)它表示一个像素在病人坐标系中的物理尺寸单位是毫米。两点距离的换算公式是实际距离(mm) 像素距离(像素) × PixelSpacing如果 PixelSpacing 缺失或为 0只能按图像坐标展示相对距离并明确提示用户该图像缺少物理标定信息。我遇到不少非标准设备导出的图像这个 Tag 经常为空所以界面提示一定要做。测量工具的实现思路是在图像上叠加一个透明画布鼠标拖动时动态绘制线段并实时计算像素距离和物理距离。EZDICOM 本身主要聚焦在 DICOM 解析与图像绘制这类交互工具通常要自己写在覆盖层上。5. 进阶玩法DICOMDIR、多帧与格式转换等基础的读取显示跑通后你很快就会遇到更复杂的场景一个检查包含几百张影像需要按序列浏览或者导出 DICOM 成普通图片方便写报告再或者设备导出的文件是多帧动态影像需要逐帧播放。这些进阶功能用 EZDICOM 也能实现只是需要一些额外设计。5.1 多帧图像的帧遍历多帧 DICOM 图像的 PixelData 里包含多帧图像数据。读取方式是把整个像素数据按帧数切分每一帧显示为一张图形。大多数版本 EZDICOM 提供 NumberOfFrames 相关属性可以通过指定帧索引来取出某一帧的图像数据。处理多帧时最常见的需求是类似视频播放器的逐帧浏览。我习惯用一个 TTimer 控制播放帧率每 100 毫秒切换一帧。需要注意多帧图像占用的内存是单帧图像的数倍一次性把所有帧数据都解到内存里可能占用很大。更好的办法是使用流式读取读取当前帧时才解析对应的像素偏移。不过部分老版本 EZDICOM 对多帧支持有限如果你发现无法按帧读取可以先检查压缩包自带的 Demo 里有没有多帧示例。如果没有再评估是否要引入额外的解码库。5.2 DICOMDIR 解析与检查目录DICOMDIR 是 DICOM 光盘或文件夹中的索引文件里面记录了患者、检查、序列、图像四层结构信息。EZDICOM 对 DICOMDIR 的支持程度因版本而异有的版本直接提供了 DICOMDIR 相关对象有的则需要自己解析。解析 DICOMDIR 的本质是读取文件中特定私有目录结构把患者信息和文件路径提取出来。如果你从控件的 API 层面找不到现成的 DICOMDIR 支持没关系你可以手动解析。我这里提供一个绕过思路先遍历 DICOMDIR 找到所有ReferencedFileID指向具体 DICOM 文件的相对路径然后用 TDICOMFile 逐文件读取提取患者的姓名和 ID 来构造检查列表。这样即使控件不支持 DICOMDIR你也能实现“选择患者 - 选择检查 - 选择序列 - 展示图像”的完整流程。5.3 导出 BMP / JPG 常见套路有时候医生需要把 DICOM 图像插入报告的 Word 文档里这时候就需要把图像导出成 JPG 或 BMP。最简单的方式是利用 TDICOMImage 把当前显示画面绘制到位图上procedure TForm1.ExportToBmp(FileName: string); var Bitmap: TBitmap; begin Bitmap : TBitmap.Create; try Bitmap.Width : DICOMImage.Width; Bitmap.Height : DICOMImage.Height; DICOMImage.DisplayOn(Bitmap.Canvas, Rect(0, 0, Bitmap.Width, Bitmap.Height)); Bitmap.SaveToFile(FileName); finally Bitmap.Free; end; end;但这样做导出的图像是“经过窗宽窗位映射后的显示图像”不是原始像素数据的完整保真导出。如果临床要求导出无损且未经过窗宽窗位调整的原始数据可以考虑写 BMP 头然后把原始像素数组作为灰度数据写入。这种方法要自己处理文件头信息和像素格式但可以保证导出的图像包含全部原始信息。5.4 灰度增强和简单处理利用 EZDICOM 读取的原始像素数据配合 Delphi 自己的数组处理可以做很多增强操作。比如对 DR 胸片做简单的直方图均衡化思路是统计灰度直方图然后构造累计分布函数再用映射表重新计算每个像素的灰度值。这套操作在 2000x2000 的图像上用 Delphi 的 PByte 指针遍历也就几十毫秒可以接受。重点提示做图像算法时永远不要直接在控件的内部像素缓冲区上修改一定要先复制一份数组再处理。否则控件内部状态被破坏后续窗口调节和保存操作都会出问题。我早期犯过这个错直接改了 PixelData 的部分字节结果是图像显示错误且保存文件也跟着损坏。6. 常见崩溃与踩坑实录最后这块是我最想分享的全是实际项目中碰到的问题。如果你也在用 EZDICOM下面的排查思路大概率能给你省下好几个小时。6.1 安装与编译过程报错速查错误现象可能原因解决办法找不到 Unit7源码路径没配置全局 Library Path 加入 Source 目录无法定位 .dcu编译目标位数不一致确认目标平台是 32 位包编译通过但控件面板无控件安装的是运行时包在设计期包中重新安装或手动加载 .bpl编译时提示字符集转换错误老代码使用 AnsiString改为 UnicodeString 或增加显式类型转换打开工程时提示 BPL 版本不匹配系统里存在多个运行时包把旧版本 BPL 从系统路径清理干净6.2 读取 JPEG2000 压缩的 DICOM 失败这是最让人头痛的问题之一。很多新设备导出的 DICOM 文件采用 JPEG2000 或 JPEG-LS 压缩老版本的 EZDICOM 默认只支持未压缩的原始像素数据。如果你打开文件后图像区域空白或者直接报告“不支持的传输语法”但用 RadiAnt 这类软件能正常显示基本就是这个问题。排查方法是先在程序里读取传输语法标签 (0002,0010)它的值不是显式 VR也不是隐式 VR而是带 JPEG 或 JPEG2000 相关字样的 UID。一旦确认是压缩格式就需要引入第三方解码库把压缩像素数据解码成原始 RGB 或灰度数据后再交给 EZDICOM 去构建显示图像。我当时的做法是使用开源的 DICOM 工具库作为解码后端解出原始像素数组后用 EZDICOM 的像素数据装载接口生成图像。这个方案虽然绕了一层但实际运行稳定。6.3 内存访问冲突与多线程使用EZDICOM 老版本在设计时未必考虑多线程安全。如果你在后台线程里加载 DICOM 文件同时在主线程里刷新界面很容易出现 Access Violation 或随机闪退。解决思路有三个第一尽量在主线程中调用控件的加载和显示方法。如果文件很大加载过程中界面会卡顿但对稳定性最有保障。第二后台线程只做文件读取与解析解析完成后把结果包装成简单数据结构通过 Synchronize 或 Queue 把数据发送给主线程再由主线程的 TDICOMImage 绘制。第三如果一定要在线程中操作控件为每一个线程创建独立的 TDICOMImage 实例不要共享控件对象。共享同一个控件对象是访问冲突的头号原因。6.4 中文患者姓名乱码DICOM 标准对字符串编码有明确规定默认使用 ASCII但国内很多设备的患者姓名采用 GB18030 或 GBK 编码而且部分设备在存储时没有正确标注 SpecificCharacterSet (0008,0005) 标签导致使用 EZDICOM 读出的姓名乱码。遇到乱码时首先要检查 SpecificCharacterSet 标签的值如果是空的或只写了 ISO_IR 6可以尝试用系统默认代码页转换。我写过一个兼容函数优先依赖 SpecificCharacterSet如果值不存在或转换后仍乱码就尝试用 GBK 解码。这里有个要非常小心的点不要为了“修复”显示乱码而修改原始文件里的字符编码否则会导致文件哈希变化从而无法通过 PACS 系统的一致性校验。乱码只处理显示层不要写回文件。6.5 64 位编译注意如果非要在 64 位下编译这里有几个容易出现的问题。一个是代码里不要把指针类型赋值给 NativeInt 前面的 Integer因为 64 位下指针是 8 字节Integer 是 4 字节编译时会报警或数据截断。另一个是汇编代码部分老控件为了性能在关键循环里使用了内联汇编这类代码在 64 位编译器下直接不支持。排查 64 位编译错误时最快的办法是把编译日志里所有Fatal级别的错误挑出来逐一判断是否与指针转换、记录大小、汇编代码相关。对于内联汇编段实在无法移植就考虑在整个单元层面做 32 位/64 位条件编译。6.6 图像显示偏黑、偏白、花屏显示问题大多数不是 EZDICOM 的 bug而是图像自身的参数域出了问题。偏黑或偏白优先看 WindowCenter 和 WindowWidth 标签对照 DICOM 文件中的实际值重新设置窗宽窗位试试。花屏则要重点检查 PhotometricInterpretation 标签。如果是 MONOCHROME1说明数据的最小值代表白色最大值代表黑色与常规 MONOCHROME2 相反。EZDICOM 通常会处理这种反转但如果控件版本较老或者你直接操作了原始像素数据容易把这样的图像显示反色。还有一个冷门问题部分设备会同时设置 BitsAllocated8 且 SamplesPerPixel3这类图像实际是 24 位彩色图。如果你用单色灰度渲染逻辑来处理画面大概率是花屏。检查这两个标签组合后及时切换到 RGB 渲染分支就可以了。最后分享一个小技巧有个经验是我在做了几个 DICOM 相关工具后才总结出来的不管是自己开发的工具还是借用 EZDICOM 的演示程序一定要在正式交付前用各种真实设备导出的文件做一轮“文件完整性测试”。不同厂商对 DICOM 标准的实现差别极大有的文件标签顺序打乱有的缺失必选标签有的把像素数据放在私有标签里。测试通过率高不高直接决定科室日常使用体验。我个人的做法是维护一个 DICOM 测试样本库按设备厂商、图像类型、压缩格式分类每次改动解析逻辑或图像显示代码后用这批样本跑一遍自动化冒烟测试。效果非常好能极大减少“我这边测试没问题但客户那边乱码”的情况。如果你刚开始用 EZDICOM我建议先把压缩包里的 ezDICOM.exe 跑通再动手搭 Delphi 工程。很多解析和显示的标准问题演示程序已经替你验证过了。碰到疑难杂症时多一份耐心去看它自带的 Demo 源码十次里有八次能找到答案。本文还有配套的精品资源点击获取
返回列表