Skip to main content
Skip table of contents

DicomEdit 6 Language Reference

DicomEdit defines a small language for scripting modifications to DICOM metadata and pixel data. The DicomEdit library is embedded within XNAT and is used to define anonymization scripts, but the language can be used outside of XNAT as well. There are version dependencies between XNAT and DicomEdit, please refer to the version compatibility matrix for details.  

Effectively anonymizing data is a complex endeavor. There is not a single process that works for all datasets. A thorough understanding of each dataset is required before a script can be written. Major considerations are:

  1. Data sources

    1. Understanding the data produced by the exact version of every device that touches the data.

    2. Understanding the nature of inputs from human operators.

  2. Project requirements

    1. How is the data to be organized and identified?

    2. What characteristics must the data have to meet processing and analysis requirements?

The goal of DicomEdit’s scripting language is to provide the code needed to transform DICOM data to meet a project’s unique requirements.

Version Compatibility Matrix

XNAT Versions

Supported DicomEdit Versions

1.8.10

DicomEdit 6.0 – 6.6

1.8.8 – 1.8.9.x

DicomEdit 6.0 – 6.5

1.8.7.x

DicomEdit 6.0 – 6.4

1.8.0 – 1.8.6.x

DicomEdit 6.0 – 6.3

1.7.0 – 1.7.6.x

DicomEdit 6.0 – 6.1

Note: DicomEdit 6.6 is backwards compatible with 6.0 through 6.5. All previous 6.x scripts should work unchanged.

Version Release Notes

Syntax Basics

DicomEdit scripts are composed of a sequence of statements.

DicomEdit scripts are interpreted, not compiled. Scripts are applied to each DICOM object in statement order from top to bottom. Conditional statements may result in some operations not being applied.

DicomEdit scripts for versions 6+ must be marked with a version. Versioning of scripts was introduced with XNAT 1.7.3. A significant break in syntax occurred then between the the older DicomEdit 4.x syntax and the newer DicomEdit 6.x syntax.  XNAT supports both versions.  XNAT assumes scripts without a version statement use DicomEdit version <= 4.x.

CODE
// The leading statement should be the version identifier
version "6.6"

While version numbers are required, specifying a version in your script does not signal to XNAT what minor version of DicomEdit (e.g. 6.3 versus 6.4) should be executed. XNAT will always execute the latest installed major version of the DicomEdit library once it has determined the appropriate major version (e.g. 4 versus 6) of the library from the provided version.

Each version update within DicomEdit 6.x is considered backwards compatible. Scripts written in earlier versions of DicomEdit 6 will run in any later version of the DicomEdit library.

Processing Statements

Processing statements are statements that are part of the language grammar.  Each statement is terminated with a newline character or the end of file. White space between the elements of a statement is ignored. Beginning in version 6.6, statements can be on multiple lines by escaping internal newlines with a backslash character. 

CODE
// Example of script with a statement formatted across multiple lines.
version "6.6"
// whitespace between statement elements is ignored in this single-line statement
   myVariable   :=     "A statement on one line."

// Be careful of spacing if breaking lines within statement elements.
myStringOnMultipleLines := \
   "Something that aids readability \
by being on multiple lines"

This script has a statement that assigns a string to myVariable. It is formatted on a single line. The second assignment statement is formatted on multiple lines by adding backslash, \, immediately followed by a newline. Escaped newlines, \\r?\n, are handled in a pre-processing step and are globally removed from the script prior to the script being parsed. Thus escaped newlines may be placed anywhere within a file to aid formatting. Note that the example breaks a statement within a string element. Whitespace within the string is significant, so be careful to not insert additional whitespace within statement elements.

Formatting a list of values is unique in that newlines between list-value elements need not be escaped. See List of Values.

Processing Statement

Since

Arguments

Description

//

6.0


Comment. The two-character sequence // indicates the start of a comment; everything on a line after // is ignored. It may be on a line of its own or terminate a processing statement.

CODE
// a comment line
(0008,0080) := "My Institution"     // in-line comment

version

6.0

String

Version. Provide the DicomEdit Language version of this script. Must be the first non-comment processing statement

CODE
version "6.6"

tagpath-singular := singular-value

6.0


Assign always. Overwrite the existing value or create the tag if it does not exist. The left-hand tagpath must resolve to a single tag because missing tags are created. A plural tagpath could result in the creation of an infinite number of tags. The right-hand side must resolve to a single value so there is no ambiguity in which value to assign.

CODE
(0008,0080) := "My Institution"

tagpath-singular ?= singular-value

6.3


Assign if exists. Like assign always, overwrite the existing value but do nothing if the assigned-to tag does not exist.

CODE
(0008,0080) ?= "My Institution"

tagpath ?= singular-value

6.5


Plural Assign if exists. Extends assign-if-exists to allow plural tagpaths on the left side. All attributes matching the left-side tagpath will be overwritten with the right-side value.

CODE
+/(0010,0010) ?= "anon"

-tagpath

6.0


Delete the tag or tags, if tagpath contains wildcards.

CODE
-(0060,10XX)

<variable> := <value>

6.0


Define a variable named <variable> and assign it the value at <value>. Term can be a number, string, tagpath, function or variable. Beginning with version 6.6, <value> may also be a list of values. This variable is now defined for the scope of the script. Statement blocks, introduced with if_elseif_else statements, do not create a new scope. All variables are globally scoped. It is an error to use a variable that has not been previously defined.

CODE
modality := (0008,0060)

conditional statement

6.1


Conditional statements mirror Java's ternary if-then-else operator. See Conditional Operations.

CODE
(0020,0011) = "1" ? (0008,103E) := "Series One" : (0008,103E) := default_series_description

if_elseif_else

6.4


Enable conditional execution of blocks of statements. See If_Elseif_Else Statements.

<function>

6.0


See Functions for the list of built-in functions or how to implement custom functions. This form of statement is used for functions that do not return a value.

CODE
retainPrivateTags[ "(0029,{SIEMENS MEDCOM HEADER2}XX)"]

echo <term>

6.0


Write the term to logging output.

CODE
echo format["Series Description is ", (0008,103E)]

removeAllPrivateTags

6.0

None

Just what it says. This is the easy way to do this common operation.

CODE
removeAllPrivateTags

describe

6.0

<variable>, string label

Provide the user-defined variable, <variable>, with the external label, label. Used by external GUI tools that interact with scripts

CODE
describe myvar "myvar-label"

Language Elements

DicomEdit statements are composed of four major elements:

String literals are delimited by quotes ("") and contain a concrete value. Examples: "Jones^Desmond", "1.3.12.2.1107.5.2.32.35177.1.1""" (empty string)

Identifiers are names that can represent either user-defined variables or the names of functions. Identifiers consist of a sequence of letters, digits, and the underscore character _, except that no identifier may begin with a digit. Examples: format, uppercase,  patient_name.

Operators are symbols or words that represent actions to be performed. Some symbolic operators are := (value assignment or variable initialization), - (attribute deletion), == (value comparison). Some word operators are echo (print a value to the console output) and describe (define variable characteristics).

Tagpaths represent a DICOM attribute or a set of attributes. Depending on context, a tagpath may represent a location or it may represent the value at that location. For example, on the left side of an assignment operator, a tagpath represents the location at which to do the assignment.  On the right side of the assignment operator, the tagpath represents the value at that location which is to be assigned.

Working with Tagpaths

Tagpath Type

Description

Examples

Single Tag

The simplest tagpath represents a single attribute in the same notation used in the DICOM standard: (ggg@,eeee) that specify the group ggg@ and the element eeee portions of the tag.

Note: g and e are any hex digit and @ is an even hex digit  All standard DICOM attributes have even group numbers.

(0010,0010) refers to the Patient Name attribute.

Element Wildcards

Tagpaths may use element wild cards to match a range of attributes. Replace any of the simple tag hex digits with one of the following element wildcard characters:  

  • X or x matches any hex digit

  • # matches odd hex digits

  • @ matches any even hex digits

(50x@,xxxx) matches all elements in all even groups 5000 through 50FE

Sequences

DICOM attributes can be sequence attributes. These are attributes that contain zero or more Items where each item is a list of zero or more attributes.

A specific attribute within a sequence is addressed in DicomEdit by (gggg,eeee)[<item-number>]/(gggg,eeee).

(0008,0110)[0]/(0008,0115) matches the Coding Scheme Name (0008,0115) in the first item [0] in the Coding Scheme Identification Sequence (0008,0110)

Explicit Item-Number Wildcard

Used in conjunction with sequence notation above, an item number can be replaced with % to match all item numbers.

(0008,0110)[%]/(0008,0115) matches the Coding Scheme Name (0008,0115) in all items in the Coding Scheme Identification Sequence (0008,0110)

Implicit Item-Number

Missing item-number notation implicitly matches all items

(0008,0110)/(0008,0115) is short-hand notation for (0008,0110)[%]/(0008,0115)

Sequence wildcards

Sequences can be nested arbitrarily deep. A sequence element may be replaced by one of the following sequence wildcards:

  • * matches zero or more levels

  • . matches one level

  • + matches 1 or more levels

* / (0010,0010) will match Patient Name anywhere it occurs.

+ / (0010,0010) will match Patient Name anywhere it occurs provided it is in at least one sequence level, that is, everywhere except the top level Patient Name attribute. 

Private Tags

See Working with Private Tags

Working With Private Tags

Private data elements do not have unique tags. Instead, they are mapped into one of a block of tags and the block used can vary depending on the context in which the DICOM object was created and processed. DicomEdit uses a syntax that accounts for the variability of private tag addresses and enables scripts to be written that are more universally applicable across different environments.

All DICOM attributes are identified by a unique integer tag. Tags have a group and element. Tags are typically written as (gggg,eeee), where g and e are any hex digit. The four g digits specify the group and the four e digits specify the element. For example, (0020,000D) specifies StudyInstanceUID.

Private attributes do not have meaning specified in the standard. They provide a mechanism by which users can extend the standard to meet individual needs. The key features of a private attribute are:

  1. Group number is an odd value greater than 0x10. (ggg#,eeee) where # is an odd hex digit.

  2. They are placed within a range of values called a block. (ggg#,XXee) where XX are hex digits that define the block.

  3. Blocks are reserved by particular private attributes called private creator IDs. (ggg#,00YY) where YY is in the range of 0x10 to 0xFF. The value YY defines a block of values, used as XX for specific attributes in the block.

  4. Blocks are reserved at the time the DICOM object is created and can be assigned different blocks based on pre-existing private data in the object.

The following is a contrived example to illustrate.

C
...
(0019,0010)  [SIEMENS MR HEADER]
(0019,0011)  [Vendor2 block-id #1]
(0019,100C)  [<diffusion b-value>]
(0019,110C)  [A Vendor2-defined value]
...

The value of the group is odd so the tags are private. The creator-id attributes (0019,0010) and (0019,0011) define two blocks of private attributes, (0019,10XX) associated with the ID "SIEMENS MR HEADER” and (0019,11XX) associated with ID "Vendor2 block-id #1". The tags (0019,100C) and (0019,110C) are specific tags in the blocks and have meaning associated with their specific creator IDs.

However, this data can have a different encoding but exactly the same meaning. The data could be encoded as follows.

C
...
(0019,0010)  [Vendor2 block-id #1]
(0019,0011)  [SIEMENS MR HEADER]
(0019,100C)  [A Vendor2-defined value]
(0019,110C)  [<diffusion b-value>]
...

Note that “SIEMENS MR HEADER“ now has been given block (0019,11XX) and Vendor2 has (0019,10XX). If we were to examine a particular DICOM object and find the attribute (0019,100C), we could not say what its meaning is without examining the wider context for its exact encoding.

This provides a challenge to creating scripts that are not specific to particular datasets. The standard does not provide any guarantee that specific private attributes will have a particular tag.

Tagpaths provide a syntax for specifying private attributes in a way that accounts for the variable nature of private attributes. The syntax is this: (ggg#,{<private-creator id>}ee). The digits that define the block are denoted by the associated creator-id string between left and right braces '{}'. Using the example above, the Siemens Diffusion-b-value tag at (0019,XX0C) would be written as (0019,{SIEMENS MR HEADER}0C). This enables DicomEdit to address the intended attribute independent from the encoding used on a per-DICOM-object basis.

The DICOM specification for private tags is found here: http://dicom.nema.org/medical/dicom/current/output/html/part05.html#sect_7.8.

Values

A value is a string produced by evaluating part of the script. String literals evaluate to themselves; tags evaluate to the string representation of the DICOM attribute (or null, if the attribute is not defined); user-defined variables (described in more detail below) evaluate to the value they have been assigned. Built-in functions also produce values, and are described below. Tagpaths can reference single attributes or a set of attributes. A value may also be a list of values and lists may contain other lists.

Value Type

Since

Multiplicity

Description

String literal

6.0

Singular

Assign the literal value "Doe^John" to the value of (0010,0010).

CODE
(0010,0010) := "Doe^John"

Number

6.0

Singular

A numerical value. It can be float or integer, positive or negative.

CODE
3.14

Variable

6.0

Singular

Defines and sets the variable named myVariable to the literal value 'snafu'.

CODE
myVariable := "snafu"

Function

6.0

None, Singular, or Plural

Returns a single-string value of (0008,003E) with all characters lowercase. See the definition of individual functions for their specifics. Functions

CODE
lowercase[(0008,003E)]

tagpath: simple

6.0

Singular

Assign the value of PatientName (0010,0010) to the value of SeriesDescription (0008,103E)

CODE
(0008,103E) := (0010,0010)

tagpath: with element wildcard

6.0

Plural

delete the range of tags (0010,0010) through (0010,001F)

CODE
-(0010,001X)

tagpath: sequence

6.0

Singular

assign the value of the variable 'myVariable' to the value of (0008,1150) in item 0 of (0008,1140) sequence.

CODE
(0008,1140)[0]/(0008,1150) := myVariable

tagpath: sequence wildcard

6.0

Plural

delete PatientName (0010,0010) in any sequence level except the top level.

CODE
- +/(0010,0010)

tagpath: private element

6.0

Singular or plural if used in conjunction with or without wildcards as above.

remove all private tags except those in the SIEMENS MEDCOM HEADER2 block of group 0029. Note that the tag is in quotes because the function needs a tagpath and not the value at that tagpath.

CODE
retainPrivateTags[ "(0029,{SIEMENS MEDCOM HEADER2}XX)"]

List of values

6.6

Singular or plural

Bracketed with braces, “{}”. Comma-separated elements. Elements may have an optional comment. Lists may be formatted across multiple lines without the need to escape the embedded newlines. See List of Values.

A value that is a list of 3 values; a string, a number, and the value returned by the function. Lists may contain lists.

CODE
{"Doe^John", 32, lowercase[(0008,103E)]}

User-Defined Variables

Scripts may contain variables, identifiers that represent a value. Variables can be defined and initialized to a particular value using the assignment operator :=.

CODE
// Define the variable 'patientID' and initialize it to the value in the tag.
patientID := (0010,0020)

Externally-Modified Variables

DicomEdit provides methods for applications to create and set variables and inject them into scripts. This enables users to interactively modify variable values. Such applications use the variable label to identify the variable, and pre-populate the value field with the initialized value. The value is set in the application and then injected into the script. The variable label is the name of the variable, unless the script specifies otherwise using the describe operation, as shown below:

CODE
// Allow the user to set Patient ID
// Initial value is the pre-modification value
patientID := (0010,0020)
describe patientID "Subject ID"
(0010,0020) := patientID

The code snippet above creates a user-defined variable named patientID, and sets its initial value to the contents of DICOM attribute (0010,0020). Applications that allow users to modify the values interactively will provide a field where the variable value can be edited (next to the label Subject ID), and the content of the DICOM attribute (0010,0020) will be set to the resulting value of the variable patientID.

Variables can also be declared to be hidden, in which applications are expected not to allow the user to edit them. Such variables are typically intermediate steps in a complex value construction, as illustrated here:

CODE
// Get visit ID from a web service using Patient ID and Study Date
describe visurl hidden
visurl := format["http://nrg111:3000/services/visitID?id={0}&date={1}", \
urlEncode[(0010,0020)], urlEncode[(0008,0020)]]
describe visit hidden
visit := getURL[visurl]

// Generate new Study Description based on Patient ID, Visit ID, modality
describe studyDesc "Study Description"
studyDesc := format["{0}_{1}_{2}", (0010,0020), visit, (0008,0060)]
(0008,1030) := studyDesc

XNAT-Predefined Variables

XNAT defines and sets these variables to aid in translating between DICOM and XNAT metadata:

Name

Value

project

The project's label

subject

The subject's label

session

The session's label

modalityLabel

This refers not to the DICOM tag (0008,0060) Modality, but to the ultimate modality the session will receive.  For example, a dual-modality PET-CT session is stored as PET. 

XNAT users can use these variables in scripts, relying on XNAT to initialize their values. See How XNAT Scans DICOM to Map to Project/Subject/Session for the details. 

Lists of Values

Beginning with version 6.6, a value may be a list of values, including lists (a list of lists). A list is bracketed by braces, "{}". It contains zero or more comma-separated values.

The major utility of lists is allow the creation of any-sized collections of values to be used as arguments to functions.

An extended example of the utility of lists is the following.

CODE
tagsToRemove := { 
    (0040,0555),    // AcquisitionContextSequence
    (0010,21B0),    // AdditionalPatientHistory
    (0038,0010),    // AdmissionID
    (0008,1080)     // AdmittingDiagnosesDescription
}

removeTags[ tagsToRemove]

A list of tagpaths is created and assigned to the variable tagsToRemove, which is passed as an argument to the function removeTags.

Note that the list is formatted as a multi-line statement (the list contains newlines). Lists are unique within DicomEdit6 in that embedded newlines do not need to be escaped. A comma following the final element is not allowed.

Operations

An operation specifies a change to an attribute value. The operations are defined in the following table.

Operation

Symbol

Since

Example

Assign - always

:=

6.0

(0008,0080) := "My Institution"

Assign - if exists

?=

6.3

(0008,0080) ?= "My Institution"

Deletion

-

6.0

- (0010,1030)

Conditional Operators

_ ? _ : _

6.1

See Conditional Operations

Examples of Operations

Assign Always (:=)

CODE
(0008,0080) := "My Institution"

Assigns the literal value "My Institution" to tag (0008,0080). Overwrite the existing value or create the tag if it does not exist.

Assign If Exists (?=)

CODE
(0008,0080) ?= "My Institution"

Assigns the literal value "My Institution" to tag (0008,0080). Overwrite the existing value or do nothing if the tag does not exist.

Delete (-)

CODE
- (0010,1030)

Remove Patient Weight, (0010,1030), from the DICOM. Note this is different from setting the value of the tag to the empty value.

More examples:

CODE
(0008,0080) := "Washington University School of Medicine"
 
// you can NOT assign to a set of attributes
// assign "new value" to all pre-existing attributes in range (0010,0100-010F)
// Fails
(0010,010X) := "new value" 
 
// you can NOT assign from multiple values.
// Fails
(0008,0080) := (0010,010X)

-(0010,1030)     // delete Patient Weight
// referencing a set of attributes makes more sense in the context of deletion.
-(50X@,XXXX)     // delete Curve Data
-*/(0010,0010)   // delete Patient Name no matter where it appears
 
// delete private tags
// delete all SIEMENS MEDCOM HEADER attributes, regardless if they are mapped to (0029,10XX), or (0029,11XX) , or ...
- (0029,{SIEMENS MEDCOM HEADER}XX)  


Operations occur in the order they appear in the script.

CODE
(0010,0010) := "Patient Name 1"
(0010,0010) := "Patient Name 2"

This will result in (0010,0010) containing the value "Patient Name 2".

Conditional Operations

Conditional statements mirror Java's ternary if-then-else operator.  Conditional statements have the form 'condition ? if-true-operation : if-false-operation', where ': if-false-operation' is optional. 

Condition Operator

Meaning

=

equal

!=

not equal

~

matches

!~

not matches

For example, the following code sets the Series Description based on the Series Number:

CODE
// Set Series Description based on Series Number.
(0020,0011) = "1" ? (0008,103E) := "Series One"
(0020,0011) = "2" ? (0008,103E) := "Series Two"

In addition to exact value matches as above, constraints can use a tilde ~ to specify regular expressions (see the Java Pattern class) to which attribute values will be matched:

CODE
(0020,0010) ~ "\d" ? (0008,1030) := "One digit study"
(0020,0010) ~ "\d\d" ? (0008,1030) := "Two digit study"

Constraints can similarly be applied to deletion operations:

CODE
// delete the Series description for series 1-5
(0020,0011) ~ "[1-5]" ? -(0080,103E)

Uses of the optional action include assigning default values:

CODE
default_series_description := "Some other series"
(0020,0011) = "1" ? (0008,103E) := "Series One" : (0008,103E) := default_series_description
(0020,0011) = "2" ? (0008,103E) := "Series Two" : (0008,103E) := default_series_description

If/Elseif/Else Block Statements

This statement enables the ability to conditionally execute blocks of statements.  The 'if' block is mandatory. It is followed by zero or more 'elseif' blocks. Any 'elseif' blocks are followed by an optional 'else' block. The brackets are required to define the block.

NONE
// Example illustrating If_Elseif_Else blocks.
modality := (0008,0060)
if( modality = "MR") {
    (0008,103E) := "Series description for MR"
}
elseif ( modality = "CT") {      
    (0008,103E) := "Series description for CT"
}
else {      
    (0008,103E) := "Series description for other modality"
}

Variable Scope in If/Elseif/Else Blocks

All variables are globally scoped and must be defined before being referenced. The following script snippet results in the error "Unknown variable 'my_variable'" due to the statement defining the variable never being interpreted.

NONE
// Example of undefined variable
if( "foo" = "bar") {
    my_variable = "snafu"
}
// Unknown variable.
(0008,103E) := my_variable

The following snippets are better practice.

NONE
// Insure the variable is defined.
my_variable := "default value"
if( "foo" = "bar") {
    my_variable = "snafu"
}
(0008,103E) := my_variable

This snippet will also insure that the variable is defined in the global scope (the only scope).

NONE
// Insure the variable is defined.
if( "foo" = "bar") {
    my_variable = "snafu"
}
else {
    my_variable := "default value"
}
(0008,103E) := my_variable

Single-Word Operations

DicomEdit provides several single-word operations, either for convenience or to provide special functionality.

Name

Arguments

Description

version

String

Provide the DicomEdit Language version of this script.

removeAllPrivateTags

None

Just what it says. This is the easy way to do this common operation.

describe

<variable>, string label

Provide the user-defined variable, <variable>, with the external label, label

Functions

The DicomEdit language includes the general concept of functions to support complex operations. Functions have the general form <function>[arg-1, arg-2, ...]. The term <function> is the name of the function and it is followed by zero or more arguments bracketed by square brackets "[]". A function can return a value or not. A function may have zero or more arguments or may take a variable number of arguments. A function may or may not directly affect the DICOM object. The arguments to functions may be Strings, numbers, variables, functions, tagpaths, or lists of these types.  Note: Tagpaths passed as arguments to functions may be treated as tagpaths or as the value at that tagpath. Consult the function’s documentation for the meaning of arguments and returned values.

Built-in Functions

The functions included in the base DicomEdit language are described in the following table:

Name

Since

Details

alterPixels

6.3

Change pixel values in the image with the specified shape and fill pattern.

Arguments: shape, shape-params, fill-pattern, fill-pattern-params

  See Supported AlterPixel Algorithms for allowed parameter values.

Returns: None

Example: Enables regions of pixel data with burned-in PHI to be obfuscated.

CODE
alterPixels["rectangle", "l=100, t=100, r=200, b=200", "solid", "v=100"]

blankValues

6.6

Set to the empty string those values matching exactly any of a list of string values.

Arguments: value [<, value>...] where value is one of tagpath, list-of-tagpaths, variable-with-list-of-tagpaths

Returns: None

The argument values are resolved to strings. Tagpath values are resolved to string values of all attributes matching the tagpath. Iterate over every attribute in the DICOM object. Replace the value of the attribute with empty string if the attribute’s value exactly matches any of the string values derived from the value arguments. The intent is to enable the removal of known strings without having to specify the attribute(s) that contain it.

Note: This function will alter the attributes in all matching tagpaths. Be careful to not blank a value the script latter relies on. See collectValues script function.

Note: This is limited to exact matches. In the example below, an attribute containing “MyHospital_West” will not match and will not be altered.

This function will only work on attributes whose value representation is known. For example, it is not uncommon for private elements to have VR=UN. This function will not attempt to test alternate encodings for a match.

Example: Remove these strings from all attributes in which they occur exactly.

CODE
blankValues[ “My Hospital”, “My_Hospital”, “MyHospital”]

Example: Blank the value in AccessionNumber and in every tag in which the accessionNumber value occurs.

CODE
blankValues[(0008,0050)]

collectValues

6.6

Create a list of string values whose values are resolved from the arguments.

Arguments: value [<, value>...] where value is one of tagpath, list-of-tagpaths, variable-with-list-of-tagpaths

The argument values are resolved to strings. Tagpath values are resolved to string values of all attributes matching the tagpath.

Returns: A list of string values.

This function can be used as one of the first statements in a script to capture values in the DICOM object before any subsequent statements have had the opportunity to alter the object. This is especially effective in conjunction with blankValues to remove values of known PHI. The blankValues function can be one of the last statements which then alters the original values.

concatenate

6.0

Arguments: value-1, value-2, ...

Returns: a single value that is the concatenation of the arguments.

delete

6.1

Remove the attribute matching the tagpath.

Arguments: string-literal tagpath

Returns: None

The preferred way to delete tags is with the delete-statement syntax. See Notes on Using the Delete Function. 

format

6.0

Format the values according to the format string.

Arguments: format-string, value-1, value-2, ...

format-string specifies the format using the same syntax as java.text.MessageFormat.

value-1... zero or more values required by the format.

Returns: string value.

For example, format[ "{1}—{0}", "foo", "bar"] returns "bar—foo"

getURL

6.0

Arguments: string containing URL

Returns: string-value content of the resource at URL. If a username and password are included in the URL (as described in RFC 3986, section 3.2.1), HTTP Basic Authentication is used.

hashUID

6.0

Arguments: <arg>

<arg> is a tagpath resolving to the value of a single attribute, or <arg> is a string containing value to hash.

Returns: string-value one-way hash UID by first creating a Version 5 UUID (SHA-1 hash) from the provided string, then converting that UUID to a UID. Returns the hash value.

hashUIDList

6.6

Arguments: arg, [<, arg>...] where arg is one of tagPath, list-of-tagpaths, variable-with-list-of-tagpaths

Returns: None

Like 'hashUID' but the values at the specified tagpaths are replaced with the hashed UID value.

ismatch

6.3

Arguments: value, regex

value is a tagPath resolving to the value of a single attribute, or value is a string containing the value to match.

Returns: true if value matches the regex.

isPresent

6.7

Arguments: arg, [<, arg>...] where arg is one of tagPath, sequence, variable-with-list-of-tagpaths

Returns: true if all tagpaths specified within the function are found.

Wildcards in tagpaths are not supported in the isPresent[] function

lookup

6.2

Arguments: key, value

Returns: the value from a lookup table matching the specified key and value. The method to specify the lookup table varies by the context in which DicomEdit is used.

Example:

DicomEdit script fragment:

CODE
(0010,0010) := lookup[ "pn", (0010,0010)]
(0010,0020) := lookup[ "pid", (0010,0020)]

LookupTable.txt:

CODE
// Example lookup table
pn/PatientName1 = MappedPatientName1
pid/PatientID1 = MappedPatientID1
pn/PatientName2 = MappedPatientName2
pid/PatientID2 = MappedPatientID2

Causes DicomEdit to take the 'key' ("pn") and the 'value' (value of the tag (0010,0010)) and return the matching value from the lookup table or null if no match is found. Thus, all DICOM files with current PatientName (0010,0010) of "PatientName1" will be assigned a new value of "MappedPatientName1".

This function is available to the DicomEdit command line tools but as of XNAT 1.7.6, there is no way to specify the lookuptable file.

lowercase

6.0

Arguments: value

value is a tagpath resolving to the value of a single attribute, or value is a string containing the value itself.

Returns: all characters in the argument in lowercase.

mapReferencedUIDs

6.2

Arguments: prefix, tagpath-1, tagpath-2, ...

Returns: None

Replace the value at all occurrences of the provided tagpaths with a new value composed of the prefix.

match

6.3

Arguments: value, regex, group-index

Returns: the content of the capturing group with the given index from matching value against the regular expression regex.
This is a more powerful and flexible method for extracting substrings than the index-based substring function.

newUID

6.0

Arguments: none

Returns: a new UID. UID root for UUIDs (Universally Unique Identifiers) generated as per Rec. ITU-T X.667 | ISO/IEC 9834-8.

normalizeString

6.6

Arguments: <tagpath> | <string> [, <replacement-string>]

Returns: a string with all non-ascii characters in the string value at <tagpath> or in <string> replaced with <replacement-string>. The default replacement string is '_' (underscore), if <replacement-string> is not specified.

reject

6.6

Immediately stop processing and signal that the current object is “rejected”.

Arguments: None

Returns: None

The response to this signal is the responsibility of the context using this function.

This function should be used with care, as any rejected instance file will not be included in your import. See How to Write a Series Import Filter for approaches in using this function.

removeTags

6.6

Arguments: arg, [<, arg>...] where arg is one of tagpath, list-of-tagpaths, variable-with-list-of-tagpaths

Returns: None

Remove attributes with tags matching any of the supplied tagpaths

replace

6.0

Arguments: string, target, replacement

Returns: a string with all occurrences of target in the given string with replacement.

retainPrivateTags

6.2

Arguments: value [<, value>...] where value is one of tagpath, list-of-tagpaths, variable-with-list-of-tagpaths

Returns: None

Removes all private tags except those matching specified tagpaths.

Example:

CODE
retainPrivateTags[ (0029,{SIEMENS MEDCOM HEADER2}XX)]

Causes all private tags except those in the "SIEMENS MEDCOM HEADER2" block of group 0029 to be removed.

scalePatientAgeAndDobFromStudyDate

6.6

Arguments: None

Returns: None

  1. Shift all occurrences of PatientDateOfBirth, (0010,0030), to be <= 89 years relative to StudyDate, (0008,0020). DOB is unchanged if StudyDate is not present.

  2. Modify all occurrences of PatientAge, (0010,1010), to be <= 089Y.

set

6.1

Set the attribute at the specified tagpath to the given value.

Arguments: <string-literal tagpath>, value

<string-literal tagpath> is the attribute to set. See Notes on String-Literal Tagpaths for format details.

Returns: None

Sometimes your DICOM just doesn't play by the rules. Especially private tags.  The correct way to address private tags is using the private creator ID. If the private creator ID is missing from the DICOM or in some other DICOM emergency, bust this out.

CODE
set["(0009,1010)", "fubar"]

shiftDateByIncrement

6.2

Return a date-string of the specified-date shifted by the specified shift (default units are days).

The returned date will have the same precision as the original date. If the shift has more precision than the date-to-be-shifted, missing precision in the date-to-be-shifted is filled in with the midpoint of the date range. See Notes on DateTime-Shift Precision.

Deprecated in 6.6. Use shiftDateTimeByIncrement instead.

Arguments: <date>, shift [, shift-units]

<date> is a tagpath that resolves to a single attribute of VR = DA, or a string whose value is consistent with VR=DA.

shift is an integer, or a string that evaluates to an integer, specifying the amount of shift.

shift-units is an optional argument specifying the shift units. Supported values are “days” and “seconds”. The default value is “days”.

Returns: A string with VR=DA format.

Example: Return a date string of the specified date shifted by the specified number of days.

CODE
(0008,0020) := shiftDateByIncrement[ (0008,0020), "14"]

shiftDateTimeByIncrement

6.3

Return a dateTime-string of the specified-dateTime, or date, shifted by the specified shift (default units are seconds).

The returned value will have the same precision as the original dateTime or date. If the shift has more precision than the date-to-be-shifted, missing precision in the date-to-be-shifted is filled in with the midpoint of the date range. Fractional-second digits, if present, will be retained with the same precision as the original value. Timezone offset, if present, will be retained. See Notes on DateTime-Shift Precision.

Arguments: <dateTime> | <date>, shift [, shift-units]

<dateTime> is a tagpath that resolves to a single attribute of VR=DT or a string whose value is consistent with VR=DT

<date> is a tagpath that resolves to a single attribute of VR=DA, or a string whose value is consistent with VR=DA

shift is an integer, or a string that evaluates to an integer, specifying the amount of shift.

shift-units is an optional argument specifying the shift units. Supported values are “days” and “seconds”. The default value is “seconds”.

Returns: A string with VR=DA or DT format.

Example: Assign to AcquisitionDateTime the current AcquisitionDateTime shifted by the specified number of seconds.

CODE
// shift acquisition datetime by 14 days and 3 hours (1220400 seconds)
(0008,002A) := shiftDateTimeByIncrement[ (0008,002A), "1220400"]

Example: Assign to StudyDate the current StudyDate shifted by the specified number of seconds.

CODE
// shift study date by 14 days and 3 hours (1220400 seconds)
(0008,0020) := shiftDateTimeByIncrement[ (0008,0020), "1220400"]

shiftDateTimeSequenceByIncrement

6.3

Shift all dateTime or date attributes matching any tagpath the specified number of seconds.

Deprecated in 6.6. Use shiftDateTimeListByIncrement instead.

Arguments: shift, <dateTime>...

shift is a string that evaluates to an integer, specifying the amount of shift in seconds.

<dateTime>... are a variable number of strings containing a tagpath.

Returns: None

Example: Shift all dateTime sequence elements by the specified number of seconds.

CODE
// shift per-frame-functional-groups-sequence/frame-content-sequence/frame-acquisition-datetime by 14 days and 3 hours (1220400 seconds)
shiftDateTimeSequenceByIncrement[ "1220400", "(5200,9230)/(0020,9111)/(0018,9151)"]

shiftDateTimeListByIncrement

6.6

Shift all attributes matching the specified tagpaths by the specified increment.

The shifted values will have the same precision as the original value. If the shift has more precision than the date-to-be-shifted, missing precision in the date-to-be-shifted is filled in with the midpoint of the date range. Fractional-second digits, if present, will be retained with the same precision as the original value. Timezone offset, if present, will be retained. See Notes on DateTime-Shift Precision.

Arguments: <list of tagpath>, shift [, shift-units]

<list of tagpath> are tagpaths that resolves to attributes of VR=DA or DT.

shift is an integer, or a string that evaluates to an integer, specifying the amount of shift.

shift-units is an optional argument specifying the shift units. Supported values are “days” and “seconds”. The default value is “seconds”.

Returns: None

Example:

CODE
// assign a list of tagpaths to a variable
tagPathsToShift := {
*/(0008,0020) /* all occurrences of studyDate */,
*/(0008,0021) /* all occurrences of seriesDate */'}

// shift all matching attributes back by 20 days.
shiftDateTimeListByIncrement[ tagPathsToShift, -20, "days"]

substring

6.0

Arguments: String, start-index, end-index

Returns: a substring beginning at zero-indexed character number start-index and extending to character number end-index – 1.

uppercase

6.0

Arguments: string

Returns: all lowercase characters in the argument to uppercase.

Notes on Using the Delete Function

The preferred way to delete tags is with the delete-statement syntax, e.g. 

CODE
// Preferred DicomEdit-6 delete syntax
- (0019,{SIEMENS}10)

The preferred syntax allows the use of the full tagpath syntax, complete with wild-cards and creator-specific private tags. The 'delete' function is useful when the full tagpath syntax is insufficient. This can occur, for example, when

  1. The DICOM data is broken and the private-creator-id tag is missing for a block of private tags, and

  2. To facilitate migration from DICOMEdit version 4 scripts to version 6 scripts. DE-4 allowed syntax like

    CODE
    // DicomEdit-4 syntax
    - (0019,1010)

    which would delete this tag regardless of the private tag's owner. The 'delete' function allows DICOMEdit-6 to replicate this behavior using a string-literal tagpath syntax. See Notes on String-Literal Tagpaths.

    CODE
    //DicomEdit-6 syntax replicating DicomEdit-4 delete syntax
    delete[ "(0019,1010)" ]

Notes on String-Literal Tagpaths

Tagpaths provide the preferred way to address DICOM attributes. See Working with Tagpaths and Working with Private Tags. The preferred syntax allows the use of the full tagpath syntax, complete with wild-cards and creator-specific private tags. However, there are situations in which the preferred syntax is insufficient, caused by DICOM objects which do not conform to the standard. This occurs when DICOM objects contain private attributes but do not contain required private-creator-id attributes. String-literal tagpaths provide a syntax that allows private attributes to be addressed directly. The use of this syntax bypasses the safety and utility provided by tagpaths, and is not intended for general use. Its intended use is reserved for “emergencies” dealing with broken data. The syntax explicitly specifies the attribute’s tag as a string value. For example, "(0019,100C)" is the string-literal tagpath addressing a private attribute.

Notes on DateTime-Shift Precision

DICOM attributes representing dates have value representation ‘DA’ and are strings of the form “YYYYMMDD”. Dates can be represented with reduced precision as “YYYYMM”, or “YYYY”. DICOM attributes representing dates and times have value representation ‘DT’ and are strings of the form “YYYYMMDDHHMMSS.FFFFFF&ZZXX”. The characters “&ZZXX” are optional timezone offset characters. The shift functions will not alter timezone offset and will carry the offset through if present. Fractional seconds are represented as “.FFFFFF” with variable precision. The shift functions will not alter fractional seconds and will carry fractional seconds forward with pre-existing precision. The remaining date-time characters may also be present in reduced precision by omitting lower precision digits. A valid VR=DT attribute may have precision as low as “year” with format “YYYY”.

The shift functions fill-in missing precision with the midpoint of the time range in order to compute shifts. The computed shift will be truncated to the precision of the original dateTime.

Precision

Compute shift from

YYYY

YYYY0701000000

YYYYMM

YYYYMMDD000000

DD = daysInMonth(YYYY, MM) / 2 + 1. Note: shifts from Februarys in a leap-year are from 15, 14 otherwise.

YYYYMMDD

YYYYMMDD120000

YYYYMMDDHH

YYYYMMDDHH3000

YYYYMMDDHHMM

YYYYMMDDHHMM30

Computing shifts from range midpoints means that shifts of the same magnitude forward and backward in time are symmetric.

Supported AlterPixel Algorithms

DICOM supports a very wide array of image types and encodings. This means that it is difficult to specify a single approach that can handle image modifications in a manner that is appropriate to every type of image data that can be encountered. It is quite likely that an algorithm that works with data from one source could easily render data from a second source unusable.

Note: It is essential that all types of data from all data sources processed by alterPixels be vetted for expected behavior.

The "alterPixels" function takes 4 string parameters: shape, shape-parameters, fill-type, and fill-parameters.  The shape parameter specifies the shape of the image region to alter. The shape-parameters specify the detailed location of the shape and the content of this parameter will be dependent on the specified shape. The fill-type specifies how the pixels will be altered. Fill parameters provide details needed by the fill type and will specific to the fill type chosen.

Algorithm

Since

Description

Basic

6.3

  • Images input with jpeg lossy encoding are written out uncompressed

  • The blanked regions are given a value of zero. The fill color is ignored.

  • The current implementation is based on ImageEditUtilities. blackout() from the Pixelmed Toolkit.

Algorithm

Shape

Shape Parameters

Fill Type

Fill Parameters

Basic

rectangle

"l=n, t=n, r=n, b=n" where l,t,r,b stand for left, top, right, and bottom and n are integer indices of pixel coordinates. Pixel indices start at 0 in the top left corner and increase to the right and down.

solid

"v=c" where v stands for value and c is the integer value to be assigned to the region of the image. This value is currently ignored.

Custom Functions

Applications can define custom functions that extend the DicomEdit and make sense only in the context of that application. XNAT provides these custom functions:

Name

Arguments

Description

makeSessionLabel

customized format using ## and modalityLabel

Provides a unique session identifier. The '##' refers to the count of sessions (zero based) this subject has of the given modalityLabel + 1. Example: makeSessionLabel[format["{0}_v##_{1}", subject, lowercase[modalityLabel]]] returns a string of the form 'subject1_v09_mr' for an MR session for subject 'subject1' who has 9 preexisting MR sessions in XNAT.

User-defined Custom Functions

Users may create their own functions. This is a programming exercise requiring the creation of a class that extends AbstractScriptFunction. This is described in the documentation for developers.

Other Language Features

The base DicomEdit language includes one additional statement type that is neither an operation nor a variable declaration: echo prints a value to the console output:

CODE
echo format["Study Description '{0}' -> '{1}'", (0008,1030), studyDesc]

Logging

DicomEdit generates output that is passed to a logging framework. The base logger is named org.nrg.dicom.dicomedit. The default configuration of XNAT logs output to ${xnat.home}/logs/anon.log

Handling of syntax errors in the DicomEdit interpreter is primitive: while invalid scripts generally produce error messages, the messages tend to be inscrutable.

We firmly recommend making no errors in your scripts.

Reference

Author: Dave Maffitt

Download / Source Code: https://bitbucket.org/xnatdcm/dicom-edit6

This document is partially derived from DicomBrowser: Software for Viewing and Modifying DICOM Metadata, Archie KA and Marcus DS, J. Digit. Imaging, 2012.

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.