This chapter describes how to develop applications using Oracle Multimedia DICOM. Oracle Multimedia DICOM provides support for Oracle Database with these application programming interfaces (APIs):
ORDDicom object API
DICOM data model utility API
DICOM relational API
DICOM Java API
Using these interfaces, you can quickly develop applications to upload to the database, retrieve from it, and manipulate DICOM content.
This chapter includes examples of how to import DICOM content into the database, write SQL queries based on DICOM metadata, perform basic image processing, make anonymous copies of ORDDicom objects, and check DICOM content for conformance to user-defined constraint rules. Some examples were extracted from the tutorial Managing DICOM Format Data in Oracle Database 11g, and adapted for this manual. See Appendix E for the location of this tutorial.
This chapter includes these sections:
For additional examples, articles, and other information about Oracle Multimedia, see the Oracle Multimedia Software section of the Oracle Technology Network Web site at
http://www.oracle.com/technology/products/multimedia/
The examples in this chapter use the table medical_image_table
with these four columns:
id
- an integer identifier
dicom
- an ORDSYS.ORDDicom object
imageThumb
- an ORDSYS.ORDImage object
anonDicom
- another ORDSYS.ORDDicom object
Issue the following statements before executing the examples in this chapter, where c:\mydir\work
is the directory where the user scott
can find the DICOM files:
CONNECT sys as sysdba
Enter password: password
CREATE OR REPLACE DIRECTORY FILE_DIR as 'c:\mydir\work';
GRANT READ ON DIRECTORY FILE_DIR TO scott;
To use a different directory for your DICOM files, replace c:\mydir\work
with the directory specification where your files are located.
Note:
All Oracle Multimedia objects and procedures provided by Oracle are defined in the schema ORDSYS.This section shows how to create a table with an ORDDicom column to store DICOM content.
The code segment shown in Example 5-1 creates the table medical_image_table
, with the four columns id
, dicom
, imageThumb
, and anonDicom
.
Example 5-1 Creating a Table for DICOM Content
CONNECT scott
Enter password: password
create table medical_image_table
(id integer primary key,
dicom ordsys.orddicom,
imageThumb ordsys.ordimage,
anonDicom ordsys.orddicom)
--
-- metadata extraction expands the ORDDicom object, allow room
pctfree 60
--
-- Use SecureFile LOBS for binary content
--
lob(dicom.source.localdata) store as SecureFile
(nocache filesystem_like_logging),
lob(imageThumb.source.localdata) store as SecureFile
(nocache filesystem_like_logging),
lob(anonDicom.source.localdata) store as SecureFile
(nocache filesystem_like_logging),
--
-- disable in row storage for the extension
-- so that it does not consume page space
-- it is usually < 4k in size
--
lob(dicom.extension) store as SecureFile
( nocache disable storage in row ),
lob(anonDicom.extension) store as SecureFile
( nocache disable storage in row ),
--
-- store the metadata as a CLOB,
-- disable storage in row
--
xmltype dicom.metadata store as SecureFile clob
( nocache disable storage in row )
xmltype anonDicom.metadata store as SecureFile clob
( nocache disable storage in row )
;
Example 5-1 uses SecureFile LOB storage for the media content. Oracle SecureFiles is a re-engineered binary large object (BLOB) that improves performance and strengthens the content management capabilities of Oracle Database.
See Also:
Oracle Database SecureFiles and Large Objects Developer's Guide for more information about SecureFile LOBs
Oracle Multimedia User's Guide for tuning tips with SecureFile LOBs
Oracle Database Security Guide for more information about creating secure passwords
This section shows how to use the SQL*Loader utility to load DICOM content into an existing table in Oracle Database. SQL*Loader is a high-performance utility for loading data from external files into tables in an Oracle database. The external data can be loaded across a network from a client system that differs from the system that is running the server for Oracle Database. The data can also be loaded locally on the same system as the database server.
A typical SQL*Loader session accepts a control file and one or more data files as input. The control file defines how to load the data into the database. The output of the SQL*Loader session is an Oracle database (where the data is loaded), a log file, and potentially, a discard file.
Example 5-2 shows a control file for loading DICOM data into the table medical_image_table
, which you created in Example 5-1. The control file contains directives that map the input data, which is specified at the end of the control file as sample1.dcm
and sample2.dcm
, to the columns of the table medical_image_table
. Only the id
and dicom
columns are loaded with externally supplied data. The imageThumb
and anonDicom
columns are initialized using constant and default values that are supplied in the control file.
Example 5-2 Loading DICOM Content
-- This file is a SQL*LDR control file to load DICOM data -- into the table MEDICAL_IMAGE_TABLE. The control file contains directives -- to load DICOM data into the DICOM column. It also contains directives -- to initialize the IMAGETHUMB and ANONDICOM columns. The data to be loaded -- is specified in this file after the BEGINDATA delimiter. -- -- The following command invokes the SQL*Loader utility and then prompts you -- to enter the password for the specified userid. -- -- sqlldr userid=USER control=load_dicom.ctl -- -- load data -- -- The input data is contained in this file after the BEGINDATA delimiter. -- infile * into table medical_image_table -- -- This example truncates the table. Change the following to "append" -- if you want to add to an existing table. -- truncate fields terminated by whitespace optionally enclosed by '"' ( -- -- The primary key column. -- id integer external, -- -- A filler field that holds the file path of the DICOM data. -- dicomFilename filler char, -- -- Load the dicom column object -- The LOB attribute source.localData is loaded with the DICOM data. -- The srcType attribute is initialized to "local". -- The updateTime attribute is initialized to "SYSDATE". -- The LOB attribute extension is initalized with an empty LOB. -- dicom column object ( source column object ( localData lobfile(dicomFilename) terminated by EOF, srcType constant 'local', updateTime expression "SYSDATE" ), extension lobfile(dicomFilename) terminated by EOF defaultif dicom.source.srcType='local' ), -- -- Initialize the imageThumb column object -- The LOB attribute source.localData is initialized with an empty LOB. -- This LOB will hold the content for the thumbnail image. -- The local attribute is initialized to "1". -- imageThumb column object ( source column object ( localData lobfile(dicomFilename) terminated by EOF defaultif imageThumb.source.local=X'1', local constant 1 ) ), -- -- Initialize the anonDicom column object -- The LOB attributes source.localData and extension are initialized. -- with empty LOBs. -- The localData LOB will hold the content for the DICOM data to be -- made anonymous. -- The extension LOB is an internal field used by ORDDICOM. -- The srcType attribute is initialized to "local". -- anonDicom column object ( source column object ( localData lobfile(dicomFilename) terminated by EOF defaultif anonDicom.source.srcType='local', srcType constant 'local' ), extension lobfile(dicomFilename) terminated by EOF defaultif dicom.source.srcType='local' ) ) -- -- Input data begins here -- -- ID DICOMFILENAME BEGINDATA 1 sample1.dcm 2 sample2.dcm
Before invoking the SQL*Loader utility, you can temporarily disable logging for the LOB data to be loaded into the dicom
column. When logging is disabled, the data is written to the database table only, and not to the redo log. Disabling logging can reduce the amount of time needed to load the DICOM data by cutting in half the amount of I/O to be performed.
To disable logging for the DICOM content in the dicom
column, use the following SQL command:
alter table medical_image_table modify lob(dicom.source.localData) (nocache nologging);
To invoke the SQL*Loader utility, use the following command, then enter the password when prompted:
sqlldr userid=USER control=load_dicom.ctl
After the DICOM data is loaded, use the following SQL command to re-enable logging for the DICOM content in the dicom
column:
alter table medical_image_table modify lob(dicom.source.localData) (nocache logging);
After the DICOM data is loaded into the table from the external files, another program is required to complete the initialization of the dicom
column and to generate the data to populate the imageThumb
and anonDicom
columns. These tasks can be performed using methods of the ORDDicom object, as shown in Example 5-3 (which includes a PL/SQL block). In this example, the dicom
column is initialized using the setProperties( ) method. The imageThumb
column object is created using the processCopy( ) method. And, the anonDicom
column object is created using the makeAnonymous( ) method, which requires a unique identifier for one of its input arguments.
Note:
Example 5-3 is a complete code sample that includes a call to the setDataModel( ) procedure. Keep in mind that this procedure call might not be required in all situations.See the setDataModel( ) Procedure for more information about when to call this procedure.
Example 5-3 Finish Loading and Initializing the DICOM Table
-- -- The ORDDicom method makeAnonymous() takes a unique UID as an input parameter. -- This utility function generates a simple UID to use in this example. -- Replace the string value of UID_ROOT with the DICOM UID for your organization. -- create or replace function genUID(in_id varchar2) return varchar2 is -- Declare the DICOM UID root value for your organization -- You must replace this value. UID_ROOT varchar2(128) := '<unique-UID root>'; begin return UID_ROOT || '.' || in_id; end; / show errors; -- -- This PL/SQL block loops over all the rows in the MEDICAL_IMAGE_TABLE and: -- 1. Calls the ORDDicom method setProperties() to initialize the dicom column -- 2. Calls the ORDDicom method processCopy() to create a JPEG thumbnail image -- that is stored in the imageThumb column. -- 3. Calls the ORDDicom method makeAnonymous() to create an anonymous version -- of the dicom column. The new version is stored in the column anonDicom. -- declare dcm ordsys.orddicom; begin -- load the DICOM data model ord_dicom.setDatamodel; -- loop over all rows in the medical image table for rec in (select * from medical_image_table for update) loop -- initialize the dicom column rec.dicom.setProperties(); -- create a JPEG thumbnail rec.dicom.processCopy('fileFormat=jpeg fixedScale=75,100', rec.imageThumb); -- make a new anonymous version of the ORDDicom object rec.dicom.makeAnonymous(genUID(rec.id), rec.anonDicom); -- write the objects back to the row update medical_image_table set dicom = rec.dicom, imageThumb = rec.imageThumb, anonDicom = rec.anonDicom where id = rec.id; end loop; commit; end; /
Example 5-3 defines a function genUID( ) to generate a unique identifier (UID) by concatenating the value of the id
column with a DICOM UID root value that you must define. You can replace this function with another function that generates unique UIDs, in accordance with the standards for your organization.
The PL/SQL block in Example 5-3 loops once over all the rows in the table medical_image_table
. Then, it reads and accesses each DICOM image in three passes. The first pass sets the properties of the dicom
column. The second pass creates a JPEG thumbnail image. And, the third pass creates an anonymous DICOM image to store in the anonDicom
column. Because of these repeated read operations, you may want to alter the LOB storage property of the dicom
column to enable caching of the DICOM content.
To enable caching for the DICOM content in the dicom
column, use the following SQL command:
alter table medical_image_table modify lob(dicom.source.localData) (cache);
After the initialization is complete, use the following SQL command to disable caching for the DICOM content in the dicom
column:
alter table medical_image_table modify lob(dicom.source.localData) (nocache logging);
Section 5.4 describes this and other PL/SQL programs in more detail.
See Also:
Oracle Database Utilities for more information about using the SQL*Loader utility to load objects and LOBs into Oracle Database
Oracle Database SecureFiles and Large Objects Developer's Guide for more information about LOBs and logging
This section builds on the code examples in Section 5.2 and Section 5.3. It shows PL/SQL code examples that store and manipulate DICOM content inside a database using Oracle Multimedia DICOM.
Oracle Multimedia DICOM enables you to store DICOM content in database tables with columns of type ORDDicom. Table 5-1 shows some attributes that are contained within an ORDDicom object in a database table.
Table 5-1 Sample Contents of an ORDDicom Object in a Database Table
ORDDicom |
---|
SOP_INSTANCE_UID |
SOP_CLASS_UID |
STUDY_INSTANCE_UID |
SERIES_INSTANCE_UID |
Source (ORDDataSource) |
Metadata (SYS.XMLType) |
ContentLength (integer) |
Internal attributes |
The following subsections describe sample tasks you can perform in PL/SQL using the code examples in this section:
This section shows how to access DICOM attributes from the DICOM content that you loaded in Section 5.3.
After the table medical_image_table
is populated and metadata has been extracted, you can access metadata using SQL queries. Example 5-4 demonstrates how to select extracted DICOM metadata from the DICOM content.
Example 5-4 Selecting Metadata from the DICOM Content
1. SOP_INSTANCE_UID. 2. SOP_CLASS_UID 3. STUDY_INSTANCE_UID 4. SERIES_INSTANCE_UID. 5. Content length (number of bytes of DICOM content) 6. Patient Name, Patient ID, and Modality from DICOM metadata select id, t.dicom.getSOPInstanceUID() as SOP_Instance_UID from medical_image_table t; select id, t.dicom.getSOPClassUID() as SOP_Class_UID from medical_image_table t; select id, t.dicom.getStudyInstanceUID() as Study_Instance_UID from medical_image_table t; select id, t.dicom.getSeriesInstanceUID() as Series_Instance_UID from medical_image_table t; select id, t.dicom.getcontentlength() as content_Length from medical_image_table t; select m.id, t.PATIENT_NAME, t.PATIENT_ID, t.MODALITY from medical_image_table m, xmltable (xmlnamespaces (default 'http://xmlns.oracle.com/ord/dicom/metadata_1_0'), '/DICOM_OBJECT' passing m.dicom.metadata columns patient_name varchar2(100) path './*[@name="Patient''''s Name"]/VALUE', patient_id varchar2(100) path './*[@name="Patient ID"]', modality varchar2(100) path './*[@name="Modality"]' ) t ;
Running Example 5-4 generates the following output:
ID SOP_INSTANCE_UID -- ------------------------------------------------------- 1 1.2.392.200036.9116.2.2.2.1762676206.1077529882.102147 ID SOP_CLASS_UID -- ------------------------------------------------------- 1 1.2.840.10008.5.1.4.1.1.2 ID STUDY_INSTANCE_UID -- ------------------------------------------------------- 1 1.2.392.200036.9116.2.2.2.1762929498.1080638122.365416 ID SERIES_INSTANCE_UID -- ------------------------------------------------------- 1 1.2.392.200036.9116.2.2.2.1762929498.1080638122.503288 ID CONTENT_LENGTH -- --------------- 1 525974 ID PATIENT_NAME PATIENT_ID MODALITY --- ------------------------------ ---------- ---------- 1 CANCIO 2HR A-02-013 CT
This section demonstrates some image processing operations that can be invoked within the database.
As an example, to create a JPEG thumbnail image from a DICOM image, you generate a new ORDImage object from the ORDDicom object. Before you can complete this task, you must describe the desired properties of the new ORDImage object.
The following description generates a JPEG thumbnail image of size 75x100 pixels:
'fileFormat=jfif fixedScale=75 100'
The code segment shown in Example 5-5 defines the procedure generate_thumb()
, which performs these tasks:
Populates the column imageThumb
of the table medical_image_table
with the identifier source_id
.
Generates an ORDImage object in the column by invoking the processCopy( ) method on the ORDDicom object in the source row.
The code statements in Example 5-5 where these tasks are performed are highlighted in bold.
Note:
Example 5-5 is a complete code sample that includes a call to the setDataModel( ) procedure. Keep in mind that this procedure call might not be required in all situations.See the setDataModel( ) Procedure for more information about when to call this procedure.
Example 5-5 Generating and Processing the New ORDImage Object
-- Set Data Model Repository execute ordsys.ord_dicom.setDataModel(); create or replace procedure generate_thumb(source_id number, verb varchar2) is dcmSrc ordsys.orddicom; imgDst ordsys.ordimage; begin select dicom, imageThumb into dcmSrc, imgDst from medical_image_table where id = source_id for update; dcmSrc.processCopy(verb, imgDst); update medical_image_table set imageThumb = imgDst where id = source_id; commit; end; / -- Create a JPEG thumbnail image for our test DICOM execute generate_thumb(1, 'fileFormat=jfif fixedScale=75 100'); -- look at our handiwork column t.imageThumb.getFileFormat() format A20; select id, t.imageThumb.getWidth(), t.imageThumb.getHeight(), t.imageThumb.getFileFormat() from medical_image_table t;
Running Example 5-5 generates the following output:
ID T.IMAGETHUMB.GETWIDTH() T.IMAGETHUMB.GETHEIGHT() T.IMAGETHUMB.GETFILE --- ----------------------- ------------------------ ---------------------- 1 75 100 JFIF
This section shows how to protect patient privacy by making ORDDicom objects anonymous.
To make ORDDicom objects anonymous, you must create a new ORDDicom object in which certain user-specifiable DICOM attributes have either been removed or overwritten in both the new DICOM content and the associated ORDDicom object metadata. An XML anonymity document specifies which DICOM attributes to remove or replace and what action to take to make each attribute anonymous.
The default anonymity document, ordcman.xml
, is loaded during installation. You can create a customized anonymity document, but that topic is beyond the scope of this example. This example uses the default anonymity document.
The code segment in Example 5-6 defines the procedure generate_anon()
, which performs these tasks:
Selects the original content dicom
and the column anonDicom
of the table medical_image_table
with the identifier source_id
.
Generates an ORDDicom object in the column anonDicom
by calling the makeAnonymous( ) method on the dicom
in the source row.
If you run this code segment, replace the temporary UID for the variable dest_sop_instance_uid
in the procedure generate_anon
with a globally unique UID.
The code statement in Example 5-6 where the makeAnonymous( ) method is called is highlighted in bold.
Note:
Example 5-6 is a complete code sample that includes a call to the setDataModel( ) procedure. Keep in mind that this procedure call might not be required in all situations.See the setDataModel( ) Procedure for more information about when to call this procedure.
Example 5-6 Populating the Column and Generating an Anonymous ORDDicom Object
-- Set Data Model Repository
execute ordsys.ord_dicom.setDataModel();
create or replace procedure generate_anon(source_id number) is
dcmSrc ordsys.orddicom;
anonDst ordsys.orddicom;
dest_sop_inst_uid varchar2(128) := '1.2.3';
begin
select dicom, anonDicom into dcmSrc, anonDst from medical_image_table
where id = source_id for update;
dcmSrc.makeAnonymous(dest_sop_inst_uid, anonDst);
update medical_image_table set anonDicom = anonDst where id = source_id;
commit;
end;
/
-- Generate an Anonymous Copy of our test DICOM
execute generate_anon(1);
select m.id, t.PATIENT_NAME, t.PATIENT_ID
from medical_image_table m,
xmltable
(xmlnamespaces
(default 'http://xmlns.oracle.com/ord/dicom/metadata_1_0'),
'/DICOM_OBJECT'
passing m.anondicom.metadata
columns
patient_name varchar2(100)
path './*[@name="Patient''''s Name"]/VALUE',
patient_id varchar2(100)
path './*[@name="Patient ID"]'
) t ;
Running Example 5-6 generates the following output:
ID PATIENT_NAME PATIENT_ID --- ------------------------------ ---------- 1 anonymous anonymous
This section shows how to check the conformance of ORDDicom objects against a set of user-specified constraint rules. Constraint rules are specified in one or more constraint documents. These XML documents specify attribute relationships and semantic constraints that cannot be expressed by the DICOM metadata schema.
A default constraint document, ordcmct.xml
, is loaded during installation. You can create a customized constraint document, but that topic is beyond the scope of this example. This example uses the default constraint document.
The code segment in Example 5-7 defines the procedure check_conform(), which performs these tasks:
Selects the original content dicom
of the table medical_image_table
with the identifier source_id
.
Displays a line of output text, which indicates either of these conditions for the DICOM content:
Conformance valid
isconformanceValid(OracleOrdObject): 1
Not conformance valid
isconformanceValid(OracleOrdObject): 0
Note:
Example 5-7 is a complete code sample that includes a call to the setDataModel( ) procedure. Keep in mind that this procedure call might not be required in all situations.See the setDataModel( ) Procedure for more information about when to call this procedure.
Example 5-7 Checking DICOM Conformance
-- Set Data Model Repository execute ordsys.ord_dicom.setDataModel(); create or replace procedure check_conform(source_id number) is dcmSrc ordsys.orddicom; begin select dicom into dcmSrc from medical_image_table where id = source_id; dbms_output.put_line('isconformanceValid(OracleOrdObject): ' || dcmSrc.isConformanceValid('OracleOrdObject')); end; / show errors;
Running Example 5-7 generates the following output:
SQL> execute check_conform(1) ; isconformanceValid(OracleOrdObject): 1
The value of 1
indicates that the DICOM content used in this example was valid because it conformed to the Oracle default constraint rules. If the DICOM content had not been valid, running the example would have returned a value of 0
. And, one or more constraint messages generated during the previous conformance validation would have been found in the view orddcm_conformance_vld_msgs.
The following code segment shows the description of this view:
SQL> describe orddcm_conformance_vld_msgs; Name Null? Type ---------------------- ------------- --------------------- SOP_INSTANCE_UID VARCHAR2(128 CHAR) RULE_NAME NOT NULL VARCHAR2(64 CHAR) MESSAGE VARCHAR2(1999 CHAR) MSG_TYPE NOT NULL VARCHAR2(20 CHAR) MSG_TIME NOT NULL TIMESTAMP(6)
You can query this view to examine any constraint messages that are generated during conformance validation. Because the DICOM content used in this example conformed with the Oracle constraint rules, there are no messages in the orddcm_conformance_vld_msgs view.
select * from orddcm_conformance_vld_msgs;
Thus, invoking the preceding select query generates the following output:
no rows selected
See Section 3.2.8 for information about what to do if your DICOM content does not conform to the constraint rules defined for your organization. See DICOM Repository Public Views for information about the view orddcm_conformance_vld_msgs.
Possible errors that can occur during run time should always be handled in your application. This practice enables the program to continue its operation even when it encounters a run-time error. This practice also enables you to know what went wrong during program operation. Proper error handling practices ensure that, whenever possible, you are always able to recover from an error while running an application. In addition, proper error handling provides you with the information you need so you always know what went wrong.
When handling exceptions, PL/SQL uses exception blocks. For example, in PL/SQL, the exception can appear as:
BEGIN <some program logic> EXCEPTION WHEN OTHERS THEN <some exception logic> END;
When you design, code, and debug your application, you are aware of the places in your program where processing might stop due to a failure to anticipate an error. Those are the places in your program where you must add exception handling blocks to handle the potential errors.
See Also:
Oracle Database PL/SQL Language Reference for more information about handling PL/SQL exceptions
Developers who are familiar with Java and Java Database Connectivity (JDBC) can write DICOM applications using Oracle Multimedia DICOM Java API. The OrdDicom class in Oracle Multimedia DICOM Java API is the Java proxy class for the ORDDicom database object. This class enables developers to write Java applications using the Oracle Multimedia object designed to store Digital Imaging and Communications in Medicine (DICOM) content.
This Java class is included in the oracle.ord.dicom.*
package. This class is used to represent an instance of the ORDSYS.ORDDicom database object type in a Java application.
The following subsections describe tasks you must perform when developing DICOM applications in Java:
See Also:
See Oracle Multimedia DICOM Java API Reference for more information about the available methods in this class.
Before you can begin using Oracle Multimedia DICOM Java API, you must set up your environment to compile and run Java programs. First, you must specify the environment variable CLASSPATH. In addition, you must ensure that this variable includes the appropriate Oracle Java libraries for the Oracle Multimedia and other features that you intend to use.
See Also:
Oracle Multimedia User's Guide for complete details about this setup
After setting up your environment variables and including the appropriate Oracle Java libraries, you must include the appropriate import statements in your Java application before using Oracle Multimedia DICOM Java API.
Execute the following statements to import the required classes from the oracle.ord.dicom.*
package and the oracle.ord.im.*
package:
import oracle.ord.dicom.OrdDicom; import oracle.ord.im.OrdImage;
Along with the standard JDBC classes included in the java.sql
package, you must also import the Oracle JDBC extension class oracle.jdbc.OracleResultSet
, with the following statement:
import oracle.jdbc.OracleResultSet;
Possible errors that can occur during run time should always be handled in your application. This practice enables the program to continue its operation even when it encounters a run-time error. This practice also enables you to know what went wrong during program operation. Proper error handling practices ensure that, whenever possible, you are always able to recover from an error while running an application. In addition, proper error handling provides you with the information you need so you always know what went wrong.
When handling exceptions, Java uses the try/catch block. For example, in Java, the exception can appear as:
try { //<some program logic>) } catch (exceptionName a) { //Exception logic } finally { //Execute logic if try block is executed even if an exception is caught }
When you design, code, and debug your application, you are aware of the places in your program where processing might stop due to a failure to anticipate an error. Those are the places in your program where you must add exception handling blocks to handle the potential errors.
See Also:
Oracle Database Java Developer's Guide for more information about handling Java exceptions
Oracle Database JDBC Developer's Guide for more information about handling Java exceptions using JDBC