DICOM Basics using Java - Presentation States (GSPS)
Introduction
This is part of my series of articles on the DICOM standard. In this tutorial, we'll explore DICOM Presentation States, which allow you to store display settings like window/level, annotations, and overlays separately from the original image data.
Presentation States enable saving "views" of images without modifying the original pixel data, supporting use cases like multiple window presets (lung, bone, soft tissue) and annotation sharing.
Prerequisites
Before you begin, ensure you have the following:
- Java JDK installed (Java 8 or later)
- PixelMed Java DICOM Toolkit
- You can find all the code demonstrated in this tutorial on GitHub here
“The details are not the details. They make the design.” ~ Charles Eames
The Theory Behind Presentation States
Presentation States implement a crucial principle in medical imaging: separation of data from presentation. The original pixel data should never be modified, but multiple "views" of that data should be shareable.
The Diagnostic Display Challenge
Consider a CT scan with 500 images. A single image might be viewed with:
- Lung Window: WW=1500, WL=-600 to see pulmonary structures
- Mediastinal Window: WW=400, WL=40 to see soft tissue
- Bone Window: WW=1800, WL=400 to see osseous structures
Each window reveals different diagnostic information from the same data. Without Presentation States, there's no standard way to share these views - a finding noted on lung windows might not be visible with the recipient's default display settings.
The Data Integrity Principle
Presentation States enforce original data preservation:
- Pixel data is never modified
- Display transformations are stored separately
- Multiple Presentation States can reference the same image
- Viewers can ignore Presentation States and use raw data
This is critical for legal and regulatory compliance - the original acquisition data remains untouched and available for alternative interpretations.
The Annotation Problem
When a radiologist circles a finding on an image, where should that annotation live?
- Burned into pixels: Modifies original data (bad), visible to everyone (good)
- Stored in viewer database: Preserves data (good), not shareable (bad)
- In Presentation State: Preserves data (good), standardized and shareable (good)
Presentation States store annotations as Graphic Annotation Sequences with coordinates in the image coordinate system, making them portable between compliant viewers.
Grayscale Pipeline Model
DICOM defines a precise grayscale display pipeline that Presentation States can modify at each stage:
Stored Pixel Values
↓ Modality LUT Transformation:
Either Rescale Slope/Intercept OR Modality LUT
(these are mutually exclusive alternatives)
Modality LUT Output
↓ Window Width/Center or VOI LUT
VOI LUT Output
↓ Presentation LUT
Display-ready Values
Note that Rescale Slope/Intercept and the Modality LUT are mutually exclusive alternatives for the Modality LUT transformation stage -- either one or the other is used, but not both. Each stage in this pipeline can be overridden by Presentation State attributes, giving complete control over how stored pixels map to displayed brightness.
Presentation State Types
| Type | SOP Class UID | Use Case |
|---|---|---|
| Grayscale Softcopy PS | 1.2.840.10008.5.1.4.1.1.11.1 | Grayscale images |
| Color Softcopy PS | 1.2.840.10008.5.1.4.1.1.11.2 | Color images |
| Pseudo-Color Softcopy PS | 1.2.840.10008.5.1.4.1.1.11.3 | Pseudo-color mapping |
| Blending Softcopy PS | 1.2.840.10008.5.1.4.1.1.11.4 | Image blending |
| XA/XRF Grayscale Softcopy PS | 1.2.840.10008.5.1.4.1.1.11.5 | Fluoroscopy |
What Can Be Stored in a Presentation State?
- Window Width/Level: Brightness and contrast settings
- Zoom and Pan: Displayed area selection
- Rotation and Flip: Image orientation
- Annotations: Text and graphic overlays
- Display Shutters: Hide portions of the image
- LUTs: Lookup tables for intensity mapping
Creating a Grayscale Presentation State
package com.saravanansubramanian.dicom.pixelmedtutorial;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.pixelmed.dicom.*;
public class CreatePresentationStateDemo {
public static void main(String[] args) {
try {
System.out.println("=== DICOM Presentation State Demo ===\n");
// The image this presentation state references
String referencedStudyUID = "1.2.3.4.5.6.7.8.9";
String referencedSeriesUID = "1.2.3.4.5.6.7.8.9.1";
String referencedSOPInstanceUID = "1.2.3.4.5.6.7.8.9.1.1";
String referencedSOPClassUID = SOPClass.CTImageStorage;
String outputFile = "C:\\temp\\presentation_state.dcm";
// Create the presentation state
AttributeList psList = createGrayscalePresentationState(
referencedStudyUID,
referencedSeriesUID,
referencedSOPInstanceUID,
referencedSOPClassUID
);
// Add file meta information
FileMetaInformation.addFileMetaInformation(
psList,
TransferSyntax.ExplicitVRLittleEndian,
"OurAET"
);
psList.write(outputFile);
System.out.println("Presentation State created: " + outputFile);
System.out.println("Window Width: 1500, Window Center: -600 (Lung)");
} catch (Exception e) {
e.printStackTrace(System.err);
}
}
}
Setting Window Width and Level
// === Softcopy VOI LUT Module (Window Width/Level) ===
SequenceAttribute voiLutSeq = new SequenceAttribute(TagFromName.SoftcopyVOILUTSequence);
AttributeList voiLutItem = new AttributeList();
// Reference the image this applies to
SequenceAttribute refImageSeq = new SequenceAttribute(TagFromName.ReferencedImageSequence);
AttributeList refImageItem = new AttributeList();
refImageItem.put(createUID(TagFromName.ReferencedSOPClassUID, sopClassUID));
refImageItem.put(createUID(TagFromName.ReferencedSOPInstanceUID, sopInstanceUID));
refImageSeq.addItem(refImageItem);
voiLutItem.put(refImageSeq);
// Window Center and Width for lung window
Attribute windowCenter = new DecimalStringAttribute(TagFromName.WindowCenter);
windowCenter.addValue("-600"); // Lung window center
voiLutItem.put(windowCenter);
Attribute windowWidth = new DecimalStringAttribute(TagFromName.WindowWidth);
windowWidth.addValue("1500"); // Lung window width
voiLutItem.put(windowWidth);
// VOI LUT Function
Attribute voiFunc = new CodeStringAttribute(TagFromName.VOILUTFunction);
voiFunc.addValue("LINEAR");
voiLutItem.put(voiFunc);
voiLutSeq.addItem(voiLutItem);
list.put(voiLutSeq);
Adding Text Annotations
// === Graphic Annotation Module ===
SequenceAttribute graphicAnnotationSeq = new SequenceAttribute(
TagFromName.GraphicAnnotationSequence);
AttributeList annotationItem = new AttributeList();
// Graphic Layer
annotationItem.put(createCode(TagFromName.GraphicLayer, "LAYER1"));
// Text Object Sequence
SequenceAttribute textObjectSeq = new SequenceAttribute(TagFromName.TextObjectSequence);
AttributeList textItem = new AttributeList();
// Bounding box for text
Attribute boundingBoxTLHC = new DecimalStringAttribute(
TagFromName.BoundingBoxTopLeftHandCorner);
boundingBoxTLHC.addValue("10");
boundingBoxTLHC.addValue("10");
textItem.put(boundingBoxTLHC);
Attribute boundingBoxBRHC = new DecimalStringAttribute(
TagFromName.BoundingBoxBottomRightHandCorner);
boundingBoxBRHC.addValue("200");
boundingBoxBRHC.addValue("50");
textItem.put(boundingBoxBRHC);
// The text to display
Attribute unformattedText = new ShortTextAttribute(TagFromName.UnformattedTextValue);
unformattedText.addValue("Lung Window Preset");
textItem.put(unformattedText);
textObjectSeq.addItem(textItem);
annotationItem.put(textObjectSeq);
graphicAnnotationSeq.addItem(annotationItem);
list.put(graphicAnnotationSeq);
Common Window Presets for CT
| Preset | Window Center | Window Width | Use Case |
|---|---|---|---|
| Lung | -600 | 1500 | Lung parenchyma |
| Mediastinum | 40 | 400 | Soft tissue |
| Bone | 400 | 1800 | Bone detail |
| Brain | 40 | 80 | Brain tissue |
| Liver | 60 | 150 | Abdominal organs |
| Stroke | 35 | 35 | Acute stroke |
Presentation State Key Attributes
| Tag | Name | Description |
|---|---|---|
| (0008,0060) | Modality | PR (Presentation State) |
| (0070,0080) | Content Label | User-friendly name |
| (0070,0081) | Content Description | Description |
| (0070,0082) | Presentation Creation Date | When created |
| (0008,1115) | Referenced Series Sequence | Links to images |
| (0028,3110) | Softcopy VOI LUT Sequence | Window/level |
| (0070,0001) | Graphic Annotation Sequence | Annotations |
| (0070,0060) | Graphic Layer Sequence | Layer definitions |
Conclusion
DICOM Presentation States provide a powerful mechanism for storing display settings separately from image data. This enables multiple views of the same image (different window presets), annotation sharing between users, and preservation of original pixel data while documenting specific findings.
Understanding Presentation States is particularly important for building diagnostic viewing applications that need to save and restore user-defined display configurations. In the next tutorial in this series, I will cover DICOM Key Object Selection for marking significant images. See you then!