Skip to content

Why a CT pixel is not automatically HU

Chapter 3 treats DICOM as the contract through which stored values acquire physical meaning from metadata and imaging objects are identified and organized into studies, series, and instances. Object identity remains distinct from spatial identity: this chapter is the packaging and identity contract between Chapter 2’s discussion of modality, signal, and contrast and Chapter 4’s treatment of physical geometry. The chapter ends by reading one real acquisition as a complete contract. It begins with the smallest version of that problem: why a stored value such as 37 is not automatically a CT measurement.

A CT slice can contain the stored integer 37. If code treats that number as a tissue measurement, it may report +37 HU. In the encoding used by the example below, the same stored value decodes to −987 HU. The difference is not a display detail: the pixel array is the payload, while metadata supplies the contract that gives the payload a physical interpretation.

The encoded value: 37 is not 37

Hounsfield units are a defined scale, but a DICOM object need not store that scale’s value directly in each pixel. For classic CT Image Storage, the CT Image Module specifies BitsAllocated = 16, while PixelRepresentation declares whether the stored samples are unsigned or signed. One permitted example uses unsigned samples with an offset: a stored value of 0 can then represent a value near −1000 HU without requiring a sign bit. Recovering the CT quantity requires applying the linear mapping described by that object’s metadata:

HU = stored_pixel × RescaleSlope + RescaleIntercept

Using the declared example settings RescaleSlope = 1 and RescaleIntercept = −1024:

stored pixel = 37
HU = 37 × 1 + (−1024) = −987

The integer 37 is therefore not +37 HU; under this mapping it is an air-adjacent value. If code applies a threshold written in HU directly to the stored value, the boundary moves to a different physical value, so voxels near that boundary can be assigned to the wrong class. This is the bounded failure to keep in mind; its extent depends on the threshold, transform, and image.

The two tags, precisely

  • RescaleSlope (0028,1053) — the multiplicative factor in the linear rescale. Read it from each object; do not assume it is 1.
  • RescaleIntercept (0028,1052) — the additive offset in that rescale. Read it from each object rather than assuming a particular value.

For the linear CT rescaling considered here, these attributes describe the linear-rescale alternative in the Modality LUT Module; they are not a LUT themselves. In an enhanced multi-frame object, the equivalent transform can be carried in a Pixel Value Transformation functional group, as part of the Enhanced CT Image IOD. For the scoped classic, non-localizer, non-multi-energy CT example, the output of this transform is HU. The practical point is that the mapping belongs to the DICOM contract carried alongside the pixel bytes; the array alone does not tell you which numerical scale it uses.

Why the contract matters

The failure is dangerous because it is numerically quiet. The array remains a valid array and may look entirely plausible, even though code is interpreting one scale as another. When a downstream operation expects HU but receives stored values, the error propagates into operations whose thresholds or statistics are defined in that physical scale:

  • window/level or threshold operations use a different numerical scale;
  • intensity discretisation for texture features bins the wrong grey levels;
  • an HU clip such as the example [-1000, +400] selects the wrong stored values;
  • in the declared slope-1, intercept-−1024 case, a reported “mean tumour attenuation” differs by exactly 1024 before any other processing. That arithmetic does not generalise to a different slope or intercept.

The error is silent because the array looks fine: it is a valid image, just shifted or rescaled. A project-specific illustration appears in the TRACE-CT learning map, where load_ct_volume_explicit() applies volume[k] = pixel × slope + intercept while building the volume. Later stages then receive the decoded volume rather than stored pixels. The implementation is an example of enforcing the contract at ingestion, not a substitute for reading the contract in each DICOM object.

A decoding check

An executable, versioned pydicom 3.0.2 example is kept in the 3D Slicer companion, alongside the official pydicom pixel-data guide and apply_rescale reference. Its stable logic is to use the library’s decoded pixel_array, then apply the object’s linear rescale or Modality LUT, and inspect the resulting values. The result is a sanity check, not proof that the complete image contract is correct.

If outside-body air is present, inspect a representative region or distribution there and compare it with the air reference near −1000 HU, while checking for padding, artifacts, and the encoded transform. A region known to be water can be compared with the 0 HU reference. Neither check makes the global hu.min() a required air statistic. The arithmetic 0 + intercept is useful only as an encoding-plausibility check: it predicts what stored zero would map to, but does not show that stored zero was sampled in air or validate the image minimum.

In the declared slope-1, intercept-−1024 example, stored zero maps to −1024 by arithmetic. That is not a universal air band or a rule about scanners or reconstructions, and a global minimum near zero does not by itself prove that rescaling was skipped; padding, artifacts, anatomy, or clipping may control the minimum.

The check exposes the central distinction: the array holds encoded intensity values, while the physical interpretation lives in metadata. The slope/intercept mapping is the bridge between them. The same contract-versus- payload idea appears later with geometry, where metadata gives an array a location in patient space; this unit does not need to repeat that spatial derivation. It is enough to recognise that correct intensity decoding is one part of a larger image contract.

Signed vs unsigned: the other half of the trap

RescaleIntercept only does its job after the pixel bit pattern has been decoded with the correct dtype. Two more tags describe that interpretation:

  • PixelRepresentation (0028,0103) — 0 = unsigned, 1 = signed.
  • BitsAllocated (0028,0100) — 16 for classic CT Image Storage.

These fields are only part of the raw-decoding contract. Reading PixelData directly also requires the applicable Transfer Syntax and byte order, as well as BitsAllocated, BitsStored, HighBit, and PixelRepresentation. A conditional example with unsigned uint16 samples and RescaleIntercept = −1024 stores an air-like value as a small positive integer; another conditional example with signed int16 samples and intercept 0 can already encode negative values. These are examples, not assumptions about every object. The preferred path is pydicom’s decoded pixel_array, which honours the declared representation; if a uint16 array is reinterpreted as int16, values below 32768 remain unchanged while upper-half values wrap by 65536, not by a uniform shift. The DICOM pixel-encoding rules define the relevant image-pixel fields.

Intensity decoding is therefore the first contract check, not the whole act of identifying an image. Before combining slices or comparing a derived object, a pipeline must know which DICOM object and series supplied the values and which instances belong together. The next section turns from the numerical payload to that organisational context: the Study, Series, and Instance hierarchy and the identifiers that keep apparently similar objects distinct. The separate spatial contract, including the geometry tags that assign an array to patient space, is developed in the geometry chapter.

© 2026 Adrian Tkacz · BioMedical-IT
Original text and figures are licensed under the Creative Commons Attribution-NonCommercial 4.0 International License (CC BY-NC 4.0), unless otherwise noted. Please credit Adrian Tkacz / BioMedical-IT and link to the original source. Third-party materials retain their respective rights and licenses.