DICOM:存储病人医疗影像和元数据的工业标准

  美国放射学会(ACR)和国家电气制造联合会(NEMA)在1993年通过协作将医学影像和通讯(DICOM)标准化了,为医学影像设备和相关应用程序的开发提供了可供参照执行的依据,后来成为存储和传输放射影像通用的标准,Oracle 10g R2完全支持DICOM 3.0标准,但是直到Oracle 11g才实现了对DICOM数据进行加密。

  Oracle 11g扩展了Oracle 10g R2 中ORDImage对象类型的能力,增加了一个新的ORDDicom对象类型,以便更有效地存储DICOM影像,由于ORDDicom对象可以存储在SecureFile LOB中,这样就可以实现压缩、重复数据删除和加密存储在DICOM文件中的影像和相关的元数据的能力,医生、大学和医院使用计算的病人信息,特别是诊断和研究。

  Oracle 10gR2提供了转储病人信息到XML文件的能力,但现在的ORDDicom对象扩展了这些能力,使得可以直接在Oracle 11g数据库查询病人的元数据,这就意味着可以使用新的高级索引特性如XMLIndex数据类型来检索特殊的病人信息,此外,Oracle 11g现在还可以创建、存储、展现和检索存储在ORDDicom对象内的数据影像的指纹特征,也就是说将会更容易选中正确的病人信息,ORDDicom数据模型也可以帮助保证病人信息的机密性,因为它提供了一个方法可以避免泄露来自DICOM文件病人元数据中的私密信息。

  1、创建DICOM对象

  为了说明如何使用这些强大的功能特性增强Oracle 11g SecureFile的加密能力,我创建了一个新的方案和表MIPS.PATIENT_IMAGES,我将使用这个表存储DICOM元数据,并在ORDDicom和ORDImage数据类型列中存储影像数据,DICOM_IMAGE列将会存储来自对应的源文件直接载入的DICOM信息,ANONYMOUS列将会存储病人的敏感数据,THUMBPRINT列将会存储来自每个DICOM文件中第一个影像的指纹图像,清单4显示了我创建这个表的DDL语句。

  清单4 创建加密的SecureFile LOB存储敏感信息 

创建一个用户(MIPS)

  DROP USER mips CASCADE;

  CREATE USER mips

  IDENTIFIED BY mips

  DEFAULT TABLESPACE patimages

  TEMPORARY TABLESPACE temp

  QUOTA UNLIMITED ON patimages;

  GRANT CONNECT, RESOURCE TO mips;

  GRANT EXECUTE ANY PROCEDURE to mips;

  GRANT CREATE ANY DIRECTORY TO mips;

  创建一个新的目录  

DROP DIRECTORY mips_imgs;

  CREATE OR REPLACE DIRECTORY mips_imgs

  AS '/home/oracle/dicom';

  GRANT READ ON DIRECTORY mips_imgs TO mips;

  创建一个新表MIPS.PATIENT_IMAGES使用SecureFile LOB存储DICOM文件。

 DROP TABLE mips.patient_images PURGE;

  CREATE TABLE mips.patient_images (

  patient_id NUMBER

  ,name VARCHAR2(30)

  ,ssn VARCHAR2(11)

  ,dob DATE

  ,dicom_image ORDSYS.ORDDICOM

  ,anonymous ORDSYS.ORDDICOM

  ,thumbprint ORDSYS.ORDIMAGE

  )

  TABLESPACE patimages

  LOB(dicom_image.source.localData)

  STORE AS SECUREFILE (

  TABLESPACE patimages

  DISABLE STORAGE IN ROW

  DEDUPLICATE

  COMPRESS HIGH

  CACHE READS

  )

  LOB(anonymous.source.localData)

  STORE AS SECUREFILE (

  TABLESPACE patimages

  DISABLE STORAGE IN ROW

  DEDUPLICATE

  COMPRESS HIGH

  CACHE READS

  )

  LOB(thumbprint.source.localData)

  STORE AS SECUREFILE (

  TABLESPACE patimages

  DISABLE STORAGE IN ROW

  DEDUPLICATE

  COMPRESS HIGH

  CACHE READS

  )

  ;

   注释

  COMMENT 
ON TABLE mips.patient_images

  IS 'Contains Patient metadata and DICOM images';

  COMMENT ON COLUMN mips.patient_images.patient_id

  IS 'Unique identifier for a Patient';

  COMMENT ON COLUMN mips.patient_images.name

  IS 'Patient Name';

  COMMENT ON COLUMN mips.patient_images.ssn

  IS 'Patient Social Security Number';

  COMMENT ON COLUMN mips.patient_images.dob

  IS 'Patient Date of Birth';

  COMMENT ON COLUMN mips.patient_images.dicom_image

  IS 'DICOM LOB';

  COMMENT ON COLUMN mips.patient_images.anonymous

  IS 'Anonymous DICOM LOB';

  COMMENT ON COLUMN mips.patient_images.thumbprint

  IS 'Thumbprint of DICOM image';

   创建索引和约束

  
CREATE UNIQUE INDEX mips.patient_images_pk_idx

  ON mips.patient_images(patient_id)

  TABLESPACE patimages;

  ALTER TABLE mips.patient_images

  ADD CONSTRAINT patient_images_pk

  PRIMARY KEY (patient_id);

  2、从DICOM源文件转移到ORDDicom对象

  现在我已经为DICOM信息创建好了适当的资料库,我将要使用SQL*Loader直接从DICOM源文件中提取并格式化病人元数据,对于我的源数据,我将使用若干个简单的DICOM文件,这些DICOM文件都是从http://www.barre.nom.fr/medical/下载得到的,病人的姓名和其它信息完全是虚构的,只是为了说明这些概念,清单5显示了SQL*Loader用于初始化、格式化和从这些源文件中载入DICOM数据到ORDDicom和ORDImage数据类型列的参数文件,以及调用SQL*Loader执行这个初始化数据载入的结果。

  清单5 使用SQL*Loader载入DICOM原始文件到Oracle 11g数据库中

  SQL*Loader参数文件: LoadDICOMFromFile.sqlparms

  
 目的: 这个参数文件将直接从DICOM文件载入DICOM数据到MIPS.PATIENT_IMAGES 表的DICOM_IMAGE 列,它也控制了ANONYMOUS列(它容纳匿名的DICOM数据)和THUMBPRINT列(它容纳DICOM图像本身的指纹图像)的初始化。

  
LOAD DATA

  INFILE *

  INTO TABLE mips.patient_images

  TRUNCATE

  FIELDS TERMINATED BY WHITESPACE

  OPTIONALLY ENCLOSED BY '"'

  (

  patient_id INTEGER EXTERNAL

  ,name CHAR

  ,ssn CHAR

  ,dob DATE "yyyymmdd"

  ,fn FILLER CHAR

   载入列对象MIPS.PATIENT_IMAGES.DICOM_IMAGE:

  
 1.) LOB属性source.localData和DICOM数据一起载入。

  
 2.) 属性srcType被设置为"local"。

  
 3.) 属性updateTime被初始化到当前日期。

  
 4.) LOB属性扩展使用空的LOB进行初始化。

  ,dicom_image 
COLUMN OBJECT (

  source COLUMN OBJECT (

  localData LOBFILE(fn) TERMINATED BY EOF

  ,srcType CONSTANT 'local'

  ,updateTime EXPRESSION "SYSDATE"

  )

  ,extension LOBFILE(fn) TERMINATED BY EOF

  DEFAULTIF dicom_image.source.srcType='local'

  )

   初始化(但不载入)列对象MIPS.PATIENT_IMAGES.ANONYMOUS:

  
 1.) LOB属性source.localData和扩展被初始化。

  
 2.) srcType属性被初始化为"local"。

  
 3.) localData LOB将容纳DICOM数据的匿名内容。

  
 4.) srcType属性被初始化为"local"。

  
 5.) LOB扩展是一个由ORDDICOM使用的内部字段。

  ,anonymous 
COLUMN OBJECT (

  source COLUMN OBJECT (

  localData LOBFILE(fn) TERMINATED BY EOF

  DEFAULTIF anonymous.source.srcType='local'

  ,srcType CONSTANT 'LOCAL'

  )

  ,extension LOBFILE(fn) TERMINATED BY EOF

  DEFAULTIF dicom_image.source.srcType='local'

  )

   初始化(但不载入)列对象MIPS.PATIENT_IMAGES.THUMBPRINT:

  
 1.) LOB属性扩展被一个空的LOB初始化。

  
 2.) LOB属性source.localData被一个空的LOB初始化。

  
 3.) 本地属性被初始化为1。

  ,thumbprint 
COLUMN OBJECT (

  source COLUMN OBJECT (

  localData LOBFILE(fn) TERMINATED BY EOF

  DEFAULTIF thumbprint.source.local=X'1'

  ,local CONSTANT 1

  )

  )

  )

  BEGINDATA

  101 "Ames, Aldritch" 322511111 19071201 CTMONO216ankle.dcm

  202 "Barry, Dave" 345212222 19681224 OTMONO28colon.dcm

  303 "Colson, Charles" 342433333 19311103 MRMONO2816xheart.dcm

  404 "Dean, John" 322094444 19420218 NMMONO21613xheart.dcm

  505 "Ehrlichman, John" 345095555 19140801 USPAL810xecho.dcm

  606 "Haldeman, Robert" 322186666 19181111 USRGB8esopecho.dcm

  >>> SQL*Loader results:

  SQL*Loader: Release 11.1.0.6.0  Production on Sat Mar 14 13:00:40 2009

  Copyright (c) 19822007, Oracle. All rights reserved.

  Control File: LoadDICOMFromFile.sqlparms

  Data File: LoadDICOMFromFile.sqlparms

  Bad File: LoadDICOMFromFile.bad

  Discard File: none specified

  (Allow all discards)

  Number to loadALL

  Number to skip: 0

  Errors allowed: 50

  Bind array: 64 rows, maximum of 256000 bytes

  Continuation: none specified

  Path used: Conventional

  Table MIPS.PATIENT_IMAGES, loaded from every logical record.

  Insert option in effect for this tableTRUNCATE

  Column Name Position Len Term Encl Datatype

  —————————- ———- —– —- —- ———————

  PATIENT_ID FIRST 
* WHT O(") CHARACTER

  NAME NEXT * WHT O(") CHARACTER

  SSN NEXT * WHT O(") CHARACTER

  DOB NEXT * WHT O(") DATE yyyymmdd

  FN NEXT * WHT O(") CHARACTER

  (FILLER FIELD)

  DICOM_IMAGE DERIVED * COLUMN OBJECT

  *** Fields in DICOM_IMAGE

  SOURCE DERIVED * COLUMN OBJECT

  *** Fields in DICOM_IMAGE.SOURCE

  LOCALDATA DERIVED * EOF CHARACTER

  Dynamic LOBFILE. Filename in field FN

  SRCTYPE CONSTANT

  Value is 'local'

  UPDATETIME EXPRESSION

  SQL string for column : "SYSDATE"

  *** End of fields in DICOM_IMAGE.SOURCE

  EXTENSION DERIVED * EOF CHARACTER

  Dynamic LOBFILE. Filename in field FN

  DEFAULT if DICOM_IMAGE.SOURCE.SRCTYPE = 0X6c6f63616c(character 'local')

  *** End of fields in DICOM_IMAGE

  ANONYMOUS DERIVED * COLUMN OBJECT

  *** Fields in ANONYMOUS

  SOURCE DERIVED * COLUMN OBJECT

  *** Fields in ANONYMOUS.SOURCE

  LOCALDATA DERIVED * EOF CHARACTER

  Dynamic LOBFILE. Filename in field FN

  DEFAULT if ANONYMOUS.SOURCE.SRCTYPE = 0X6c6f63616c(character 'local')

  SRCTYPE CONSTANT

  Value is 'LOCAL'

  *** End of fields in ANONYMOUS.SOURCE

  EXTENSION DERIVED * EOF CHARACTER

  Dynamic LOBFILE. Filename in field FN

  DEFAULT if DICOM_IMAGE.SOURCE.SRCTYPE = 0X6c6f63616c(character 'local')

  *** End of fields in ANONYMOUS

  THUMBPRINT DERIVED * COLUMN OBJECT

  *** Fields in THUMBPRINT

  SOURCE DERIVED * COLUMN OBJECT

  *** Fields in THUMBPRINT.SOURCE

  LOCALDATA DERIVED * EOF CHARACTER

  Dynamic LOBFILE. Filename in field FN

  DEFAULT if THUMBPRINT.SOURCE.LOCAL = 0X01(character '')

  LOCAL CONSTANT

  Value is '1'

  *** End of fields in THUMBPRINT.SOURCE

  *** End of fields in THUMBPRINT

  Table MIPS.PATIENT_IMAGES:

  6 Rows successfully loaded.

  0 Rows not loaded due to data errors.

  0 Rows not loaded because all WHEN clauses were failed.

  0 Rows not loaded because all fields were null.

  Space allocated for bind array: 83456 bytes(64 rows)

  Read buffer bytes: 1048576

  Total logical records skipped: 0

  Total logical records read6

  Total logical records rejected: 0

  Total logical records discarded: 0

  Run began on Sat Mar 14 13:00:40 2009

  Run ended on Sat Mar 14 13:00:49 2009

  Elapsed time was: 00:00:09.65

  CPU time was: 00:00:00.61

  3、生成指纹图像和匿名的元数据

  为了完成ANONYMOUS和THUMBPRINT列的填充,我将使用一些技巧:

  (1)首先,我将通过调用setModel 方法载入ORDDicom对象模型,这个对象模型必须在调用其它方法之前载入。

  (2)然后使用processCopy方法生成一张150×200的JPEG图像,将其存储到THUMBPRINT列中。

  (3)最后,我使用makeAnonymous方法创建一个匿名版本的DICOM_IMAGE列数据,并将其存储到ANONYMOUS列中。

  清单6 显示了如何在最初被载入MIPS.PATIENT_IMAGES表中的所有行上执行这些方法。

  清单6 从前面载入的ORDDicom对象创建和存储匿名的病人信息和指纹图像:

  1)将DICOM模型载入到内存中。

  2)将原始的DICOM文件元数据转换成对应的元素。

  3)基于原始的DICOM影像以JPEG格式创建一个缩略图。

  4)生成一个匿名的DICOM文件版本。

  5)将新的信息保存到它们对应的SecureFile LOB中。

SET SERVEROUTPUT ON

  DECLARE

  dcm_row ORDSYS.ORDDICOM;

  BEGIN

   载入DICOM数据模型

  ord_dicom.setDatamodel;

   在医学图像表中遍历所有行

  
FOR dcm_row IN (

  SELECT

  patient_id

  ,dicom_image

  ,anonymous

  ,thumbprint

  FROM mips.patient_images

  FOR UPDATE

  )

  LOOP

  BEGIN

  

  
通过setProperties存储过程初始化DICOM SecureFile LOB列属性

  


  dcm_row.dicom_image.setProperties();

  

  
 在原始DICOM影像的基础上构建一个缩略图图像

  


  dcm_row.dicom_image.processCopy(
'fileFormat=JPEG fixedScale=150,200', dcm_row.thumbprint);

  

  
 通过makeAnonymous()函数生成DICOM对象的匿名版本

  


  dcm_row.dicom_image.makeAnonymous(genUID(dcm_row.patient_id), dcm_row.anonymous);

  

  
 将新产生的信息保存到它们对应的SecureFile LOB中

  


  
UPDATE mips.patient_images

  SET dicom_image = dcm_row.dicom_image

  ,anonymous = dcm_row.anonymous

  ,thumbprint = dcm_row.thumbprint

  WHERE patient_id = dcm_row.patient_id;

  EXCEPTION

  WHEN OTHERS THEN

  DBMS_OUTPUT.PUT_LINE('Error processing image for Patient ID #' || dcm_row.patient_id);

  END;

  END LOOP;

  COMMIT;

  END;

  /

  4、使用ORDDicom和ORDImage列的内容

  最后,我已经准备好研究这些已经载入到DICOM列的DICOM信息,幸运的是,ORDDicom数据类型使得这些工作变得相当简单,因为它提供了多种方法来直接查询病人的元数据属性,清单7显示了一个简单的查询,它直接从DICOM_IMAGE和THUMBPRINT列查询多个属性。

  清单7 查看已经载入到ORDDICOM和ORDIMAGE对象中的元数据

  显示选择的内容:

  1)普通数据类型

  2)ORDDICOM数据类型

  3)ORDIMAGE数据类型 

SET LINESIZE 80

  SET PAGESIZE 80

  TTITLE 'Sample Patient Metadata|(from MIPS.PATIENT_IMAGES)'

  COLUMN patient_id FORMAT 99999 HEADING 'Pat|ID'

  COLUMN name FORMAT A20 HEADING 'Patient Name' WRAP

  COLUMN ssn FORMAT A11 HEADING 'Patient SSN'

  COLUMN di_sop_uid FORMAT A30 HEADING 'DICOM Image SOP UID' WRAP

  COLUMN tp_len FORMAT 99999 HEADING 'Thumb|Print|Image|Size'

  SELECT

  PI.patient_id

  ,PI.name

  ,PI.ssn

  ,PI.dicom_image.sop_instance_uid AS di_sop_uid

  ,PI.thumbprint.getcontentlength() AS tp_len

  FROM mips.patient_images PI

  ORDER BY PI.patient_id

  ;

  TTITLE OFF

  Sun Mar 15 page 1

  Sample Patient Metadata

  (from MIPS.PATIENT_IMAGES)

  Thumb

  Print

  Pat Image

  ID Patient Name Patient SSN DICOM Image SOP UID Size

  —- ——————– ———– —————————— ——

  
101 Ames, Aldritch 322511111 1.2.840.113619.2.1.2411.103115 5034

  2382.365.1.736169244

  202 Barry, Dave 345212222 1.3.46.670589.17.1.7.0.16 5677

  303 Colson, Charles 342433333 999.999.2.19960619.163000.1.10 3648

  3

  404 Dean, John 322094444 2.16.840.1.113662.5.8796818449 1596

  476.121423489.1.1.3101.5309511

  143

  505 Ehrlichman, John 345095555 999.999.133.1996.1.1800.1.6.25 5252

  606 Haldeman, Robert 322186666 999.999.2.19941105.112000.2.10 4683

  7

  校验存储在ORDDicom或ORDImage数据类型列中的图像需要一点技巧,因为需要一个接口才能查看这些图像,因为这些图像都采用基于工业标准的格式存储,如JPEG,TIF,PNG等,所以有大量的免费图像查看程序可以拿来使用。

  小结

  Oracle 11g新的SecureFile特性大大扩展在Oracle数据库中存储大对象的能力,并提高了安全性和有效性,SecureFILE LOB的压缩和重复数据删除功能在空间利用上更是让人惊讶,SecureFILE LOB的透明数据加密特性让存储在Oracle 11g数据库中的敏感信息和机密信息安全性更有保障,这些特性让Oracle 11g数据库成为美国新的联邦政府在启动存储医院、病人和医疗元数据方面的主要候选数据库。

Leave a Reply