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:
Data sources
Understanding the data produced by the exact version of every device that touches the data.
Understanding the nature of inputs from human operators.
Project requirements
How is the data to be organized and identified?
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.
// 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.
// 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
CODE
| |
version | 6.0 | String | Version. Provide the DicomEdit Language version of this script. Must be the first non-comment processing statement
CODE
|
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
| |
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
| |
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
| |
-tagpath | 6.0 | Delete the tag or tags, if tagpath contains wildcards.
CODE
| |
<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
| |
conditional statement | 6.1 | Conditional statements mirror Java's ternary if-then-else operator. See Conditional Operations.
CODE
| |
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
| |
echo <term> | 6.0 | Write the term to logging output.
CODE
| |
removeAllPrivateTags | 6.0 | None | Just what it says. This is the easy way to do this common operation.
CODE
|
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
|
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: Note: |
|
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:
|
|
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 |
|
Explicit Item-Number Wildcard | Used in conjunction with sequence notation above, an item number can be replaced with |
|
Implicit Item-Number | Missing item-number notation implicitly matches all items |
|
Sequence wildcards | Sequences can be nested arbitrarily deep. A sequence element may be replaced by one of the following sequence wildcards:
|
|
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:
Group number is an odd value greater than 0x10.
(ggg#,eeee)
where#
is an odd hex digit.They are placed within a range of values called a block.
(ggg#,XXee)
whereXX
are hex digits that define the block.Blocks are reserved by particular private attributes called private creator IDs.
(ggg#,00YY)
whereYY
is in the range of 0x10 to 0xFF. The valueYY
defines a block of values, used asXX
for specific attributes in the block.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.
...
(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.
...
(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
|
Number | 6.0 | Singular | A numerical value. It can be float or integer, positive or negative.
CODE
|
Variable | 6.0 | Singular | Defines and sets the variable named myVariable to the literal value 'snafu'.
CODE
|
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
|
tagpath: simple | 6.0 | Singular | Assign the value of PatientName (0010,0010) to the value of SeriesDescription (0008,103E)
CODE
|
tagpath: with element wildcard | 6.0 | Plural | delete the range of tags (0010,0010) through (0010,001F)
CODE
|
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
|
tagpath: sequence wildcard | 6.0 | Plural | delete PatientName (0010,0010) in any sequence level except the top level.
CODE
|
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
|
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
|
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 :=.
// 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:
// 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:
// 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.
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 |
Examples of Operations
Assign Always (:=
)
(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 (?=
)
(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 (-
)
- (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:
(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.
(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:
// 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:
(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:
// delete the Series description for series 1-5
(0020,0011) ~ "[1-5]" ? -(0080,103E)
Uses of the optional action include assigning default values:
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.
// 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.
// Example of undefined variable
if( "foo" = "bar") {
my_variable = "snafu"
}
// Unknown variable.
(0008,103E) := my_variable
The following snippets are better practice.
// 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).
// 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: See Supported AlterPixel Algorithms for allowed parameter values. Returns: None Example: Enables regions of pixel data with burned-in PHI to be obfuscated.
CODE
|
blankValues | 6.6 | Set to the empty string those values matching exactly any of a list of string values. Arguments: 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 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
Example: Blank the value in AccessionNumber and in every tag in which the accessionNumber value occurs.
CODE
|
collectValues | 6.6 | Create a list of string values whose values are resolved from the arguments. Arguments: 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 |
concatenate | 6.0 | Arguments: Returns: a single value that is the concatenation of the arguments. |
delete | 6.1 | Remove the attribute matching the tagpath. Arguments: 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:
Returns: string value. For example, |
getURL | 6.0 | Arguments: 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:
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: Returns: None Like 'hashUID' but the values at the specified tagpaths are replaced with the hashed UID value. |
ismatch | 6.3 | Arguments:
Returns: true if |
isPresent | 6.7 | Arguments: 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: 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
LookupTable.txt:
CODE
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:
Returns: all characters in the argument in lowercase. |
mapReferencedUIDs | 6.2 | Arguments: Returns: None Replace the value at all occurrences of the provided tagpaths with a new value composed of the prefix. |
match | 6.3 | Arguments: Returns: the content of the capturing group with the given index from matching |
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: 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: Returns: None Remove attributes with tags matching any of the supplied tagpaths |
replace | 6.0 | Arguments: Returns: a string with all occurrences of |
retainPrivateTags | 6.2 | Arguments: Returns: None Removes all private tags except those matching specified tagpaths. Example:
CODE
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
|
set | 6.1 | Set the attribute at the specified tagpath to the given value. Arguments:
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
|
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 Arguments:
Returns: A string with VR=DA format. Example: Return a date string of the specified date shifted by the specified number of days.
CODE
|
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:
<date> is a tagpath that resolves to a single attribute of VR=DA, or a string whose value is consistent with VR=DA
Returns: A string with VR=DA or DT format. Example: Assign to AcquisitionDateTime the current AcquisitionDateTime shifted by the specified number of seconds.
CODE
Example: Assign to StudyDate the current StudyDate shifted by the specified number of seconds.
CODE
|
shiftDateTimeSequenceByIncrement | 6.3 | Shift all dateTime or date attributes matching any tagpath the specified number of seconds. Deprecated in 6.6. Use Arguments:
Returns: None Example: Shift all dateTime sequence elements by the specified number of seconds.
CODE
|
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:
Returns: None Example:
CODE
|
substring | 6.0 | Arguments: Returns: a substring beginning at zero-indexed character number |
uppercase | 6.0 | Arguments: 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.
// 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
The DICOM data is broken and the private-creator-id tag is missing for a block of private tags, and
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 |
|
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 | 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:
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.