Anyone who has moved a study archive between PACS vendors, swapped a modality, or validated an anonymisation pipeline has faced the same question: are these two DICOM files the same, and if not, exactly how do they differ? It is a question that is easy to ask and surprisingly awkward to answer, because a DICOM file is not a flat list of values — it is a nested data set where identical clinical content can be encoded in materially different ways.
What a DICOM Data Set Actually Contains
A DICOM file pairs a file meta information header with a data set of attributes. Each attribute is identified by a tag written as a group and element number, such as (0010,0010) for Patient Name. Alongside the tag sits a Value Representation, or VR — a two-letter code that declares the data type: PN for a person name, DA for a date, UI for a unique identifier, SQ for a sequence, and so on. The VR determines both how the bytes are interpreted and what a valid value looks like.
Comparing two files therefore means matching attributes by tag, then comparing values within the type that the VR declares. A naive byte-level file comparison is almost useless here: two files carrying identical clinical content will differ in bytes if they use different transfer syntaxes, if the attribute order differs, or if padding differs.
Sequences: Where Flat Comparisons Fail
The attribute with VR SQ is the one that breaks simple tools. A sequence contains an ordered list of items, and each item is itself a complete nested data set that can contain further sequences. Scheduled Procedure Step Sequence (0040,0100), Referenced Image Sequence (0008,1140) and the deeply nested content trees of DICOM Structured Reporting all live inside sequences.
A comparison that reports only "(0040,0100): sequence with 2 items" on both sides tells you nothing about whether the modality, the scheduled description, or the performing physician inside those items changed. Because item order is significant within a sequence, a correct comparison pairs item 1 with item 1, descends into each, and compares the attributes there — which is what this tool does, reporting each nested attribute with its full path so you can see where in the tree the difference lives.
What Legitimately Changes During a Migration
Not every difference is a defect. Understanding which changes are expected is most of the work in validating a migration:
- Transfer syntax and encoding. A destination archive may store objects with a different transfer syntax, changing how values are encoded without changing what they mean. Explicit versus implicit VR is the common case.
- Implementation identity. Implementation Class UID (0002,0012) and Implementation Version Name (0002,0013) identify the software that wrote the file, so they legitimately change whenever a different system writes the object.
- Coercion of identifiers. Many archives coerce Patient ID, Accession Number, or Study ID to match the receiving institution's master patient index. This is intentional, configured behaviour, and it is exactly what you need to verify happened correctly.
- Timestamps of transfer. Attributes recording when an object was received or archived will differ by design.
What should generally not change during a straight migration is the clinical content: pixel-defining attributes such as Rows, Columns, Bits Allocated and Photometric Interpretation, the geometry attributes that let images reconstruct correctly, and the instance identity that ties images to their series and study.
UIDs: The Attributes That Deserve Their Own Filter
Unique Identifiers — attributes with VR UI — are the backbone of DICOM's information model. Study Instance UID, Series Instance UID and SOP Instance UID form the hierarchy that lets an archive assemble loose instances into a coherent study. Break that hierarchy and images scatter across the wrong studies or vanish from the worklist.
Two rules pull in opposite directions here, which is why the UID filter is useful. In a straight migration, instance UIDs must be preserved exactly, because they are the identity of the object; a changed SOP Instance UID means the archive has created a new object rather than moved the existing one. In de-identification, by contrast, UIDs generally must be replaced, because a retained UID links the anonymised copy back to the original study. Filtering the diff to VR UI answers "did the UIDs survive?" or "did the UIDs get regenerated?" in a single view, depending on which of the two operations you are validating.
Validating De-Identification
When the second file is meant to be a de-identified copy of the first, the diff becomes a verification report. What you want to see is that every attribute on the PHI list has changed — removed, blanked, or replaced — and that the clinical attributes have not.
Filtering to PHI tags gives you exactly that view. Any PHI attribute still reported as identical is a leak worth investigating: a patient name, birth date, institution name or referring physician that survived the anonymisation. The reverse check matters too. If a clinically significant attribute shows as changed when it should not have been, the de-identification profile is more aggressive than intended and may have damaged the diagnostic value of the data set.
DICOM PS3.15 Annex E defines confidentiality profiles that specify exactly which attributes must be handled and how. A diff does not replace reading that specification, but it is the fastest way to check a pipeline against it on real files rather than in theory.
Reading a Diff Without Drowning
A typical CT header carries a few hundred attributes, and an enhanced multi-frame object or a structured report can carry thousands once sequences are expanded. Showing everything is rarely useful, which is why the differences-only view is the default here.
A practical order of work: start with differences only and look at the summary count. If the number is small, read every row. If it is large, apply the UID filter first to confirm the identity attributes behaved as the operation requires, then the PHI filter to confirm the confidentiality behaviour, then narrow by VR to sweep categories — DA and TM for dates and times, PN for names, CS for coded values. Search by tag number or name when you have a specific attribute in mind. Export the filtered rows as CSV once the view shows exactly what the ticket needs to record.
The Limits of a Header Diff
A tag-level comparison is a header and metadata comparison. It will tell you that Rows, Columns and Bits Allocated match, which is strong evidence that the pixel data is structurally intact, but it does not decode and compare the pixels themselves. A migration that preserved every header attribute while corrupting or transcoding the image data would show a clean diff.
For that reason a header diff belongs in a validation suite alongside a pixel-level check and a visual review of a representative sample, not in place of them. It is the fastest way to find the differences a header can express, and it is deliberately silent about the ones it cannot.