JPEG DCT Scrubbing

The dicom_dre.jpeg_dct_scrubber module blanks rectangular regions in JPEG Baseline images by zeroing DCT coefficients directly in the compressed bitstream. The module performs no full decompression or recompression, so non-blanked regions stay bit-for-bit identical with no quality loss.

Note

This module implements the JPEG Baseline path of the pixel-scrub stage. For how it fits into the end-to-end de-identification pipeline, see Architecture.

Background

The DCT-domain redaction approach comes from the PixelMed DICOM Toolkit. PixelMed demonstrated removing burned-in text from JPEG-compressed DICOM images by operating on the Huffman-coded DCT coefficients, without decoding and re-encoding pixel data. This module is a Python port of the PixelMed JPEG Selective Block Redaction Codec by David A. Clunie, with an optional C accelerator. The module source reproduces the PixelMed BSD license at the top.

Supported format

Only JPEG Baseline (SOF0) is supported:

  • 8-bit sample precision

  • Huffman-coded entropy coding

  • Sequential DCT

The module rejects progressive JPEG, arithmetic coding, and extended JPEG processes with a ValueError.

How it works

  1. The module parses the JPEG bytestream marker by marker (SOI, SOF0, DHT, DQT, DRI, SOS, EOI).

  2. It builds decode and encode lookup structures from the Huffman tables in the DHT segments.

  3. It decodes the entropy-coded segment after each SOS marker MCU by MCU. For each 8x8 DCT block, it checks whether the block overlaps any of the specified blanking regions.

  4. For a block that overlaps a blanking region, the module sets the DC coefficient to zero and replaces all 63 AC coefficients with an EOB (end-of-block) marker. This produces a uniform mid-gray patch in the output.

  5. The module passes blocks outside blanking regions through with their original coefficients. Two separate DC prediction chains (one for the input stream, one for the output stream) ensure that modifying one block does not corrupt the DC values of later blocks.

  6. The module handles restart markers (DRI/RST) by resetting both prediction chains at each restart interval boundary.

C accelerator

The pure-Python entropy codec processes one bit at a time through Python method calls (BitReader, BitWriter, HuffmanTable). This is correct but slow. A single DICOM study can contain hundreds of JPEG frames that each need DCT-domain blanking.

The C extension _jpeg_dct_accel reimplements _process_entropy_segment in C via CFFI, yielding approximately 300x speedup over the Python fallback.

When the C extension is not available, the module emits a warning and falls back to the Python implementation.

To compile the extension locally:

just build-ext

This runs uv run python -m dicom_dre._jpeg_dct_accel_build and writes the compiled _jpeg_dct_accel*.so next to the module.

API

scrub_jpeg(input_path, output_path, regions)

Read a JPEG file from input_path, blank the specified regions, and write the result to output_path.

Name

Type

Description

input_path

str

Path to the input JPEG file

output_path

str

Path to write the scrubbed JPEG file

regions

list[tuple[int, int, int, int]]

Rectangles to blank as (x, y, width, height) in pixel coordinates

scrub_jpeg_bytes(data, regions)

Scrub JPEG data in memory and return the modified bytes.

Name

Type

Description

data

bytes

Raw JPEG data

regions

list[ScrubRegion]

Rectangles to blank as ScrubRegion(x, y, width, height) in pixel coordinates

Returns: bytes, the modified JPEG data.

Command-line usage

Run the module directly for testing:

python -m dicom_dre.jpeg_dct_scrubber input.jpg output.jpg 10,5,200,50 300,400,100,30

Each region argument is a comma-separated x,y,width,height rectangle.