Device Catalog¶
The device catalog is a Python module that unifies image filtering and pixel scrubbing into a single, structured representation of known imaging devices. Each entry identifies a specific hardware device by a set of DICOM attribute patterns (manufacturer, model, modality, software version, image dimensions) that match the images that device produces. The engine uses these patterns to recognize those images and blank the pixel regions where the device burns in text.
Note
The bundled catalog comes from studies on a single PACS at one medical research center. Its device rules and pixel scrub regions reflect the scanner fleet seen there. They are unlikely to be complete or correct for another site. Treat the included catalog as a starting point that requires local validation. See Limitations and portability.
Goals¶
The device catalog is built on the following design choices:
Model known devices, not arbitrary expressions. Each entry matches a specific imaging device by manufacturer, model, modality, software version, and image dimensions. The rules read as data, not code.
Couple resolution to scrub regions. Each device entry contains resolution-specific variants that bind image dimensions directly to the pixel regions to blank. Each device defines its filter decision and its scrub regions together.
Return a structured decision with a reason. Every evaluation returns a
CatalogDecisioncontaining the action (allow,deny,scrub), a human-readable reason string, and the list of scrub regions. The reason propagates through the pipeline into theFILTEREDresult so callers can distinguish why a file was rejected.Validate at import time. The catalog is a Python module. The interpreter catches syntax errors when it loads the module. It runs no runtime script parsing.
Modules¶
Module |
Description |
|---|---|
|
Core matching engine, dataclasses, |
|
Device and exclusion rule data |
|
Python pixel blanking (JPEG DCT + pydicom/numpy) |
String matching¶
Pattern strings on device fields use a prefix to select the match operator.
All comparisons are case-insensitive except /regex/ patterns, which are
case-sensitive unless the pattern includes an inline flag such as (?i).
Prefix |
Operator |
Example |
|---|---|---|
(none) |
case-insensitive substring |
|
|
case-insensitive exact |
|
|
case-insensitive starts-with |
|
|
|
|
|
tag absent or blank |
|
A list of values on any field uses OR semantics: at least one must match. All fields on a single device rule are AND’d together.
The = exact match compares the full value case-insensitively. A /regex/
pattern is evaluated with re.search and is case-sensitive unless the pattern
includes an inline flag such as (?i). To match an exact value case-insensitively
with a regex (for example when you also need anchoring, alternation, or a
negative lookahead), anchor the pattern and add (?i), as in
"/(?i)^SIGNA PET\\/MR$/". This is why many regex rules in the default catalog
use the /(?i)^...$/ form.
Device rules¶
Each device entry is created with the device() factory function:
device(
"KONICA 0402 CR",
"allow",
manufacturer="KONICA",
modality="^CR",
manufacturer_model_name="0402",
variants=[
variant(software_versions="^6.", rows=2446, cols=2446, scrub=[(0, 2308, 2446, 137)]),
variant(software_versions="^2.", rows=2010, cols=2446, scrub=[(0, 0, 2446, 115)]),
],
)
The first positional argument is the device name, which becomes the reason
field of the returned CatalogDecision. The second positional argument is the
action: "allow", "deny", or "scrub".
Device rule fields¶
All keyword fields on device() are optional and default to None
(unconstrained). A field constrains the match only when you specify it. Every
specified field is AND’d together: a device matches only when all of its
fields match. When you give a field a list, the list uses OR semantics.
Field |
DICOM tag |
Purpose |
|---|---|---|
|
|
Vendor name |
|
|
|
|
|
Device model |
|
|
Firmware/software revision |
|
|
All listed components must appear |
|
|
At least one component must appear |
|
|
None of the components may appear |
|
|
|
|
|
SOP class UID pattern |
|
|
SC device model (used by GE workstations) |
|
|
Free-text series label |
|
|
Coded concept meaning |
|
|
Dose report text |
|
|
Free-text study label |
|
|
Secondary-capture conversion type |
|
|
Physical pixel spacing |
|
|
Anatomic region |
|
|
When |
|
n/a |
List of resolution/version variants (see below) |
|
|
Shorthand for a single-variant device |
Every match attribute has a dedicated field; there is no free-form keyword dict.
The catalog evaluates each field with the same string matching
rules. It matches only the attributes that DicomTags.from_dataset() reads.
The three image_type* fields match differently depending on whether the
pattern is a single string or a list. ImageType is a multi-valued tag stored
as backslash-joined components (for example ORIGINAL\PRIMARY\AXIAL). A list
value such as image_type=["DERIVED", "PRIMARY"] matches each element against
the individual components. The catalog matches a single string containing
backslashes such as image_type="DERIVED\\PRIMARY\\DIXON\\WATER" against the
full joined string, which lets you require a specific component order.
The rows, cols, and scrub shorthand parameters are mutually exclusive with
variants. Supplying both raises ValueError at import time. Use the shorthand
for a device with a single resolution; use variants for a device with multiple
resolutions or software versions.
Variants¶
A variant binds a resolution (rows, cols) or software version to a set of scrub regions. All fields in a variant are optional. A device with no variants has no dimension constraint.
variant(
rows=2446, # exact Rows match
cols=2446, # exact Columns match
software_versions="^6.", # further narrows the device-level version
image_type=["DERIVED"], # ImageType components, all must appear
scrub=[(x, y, w, h)], # pixel regions to blank
)
A variant with only scrub (no rows/cols) applies those regions to all
resolutions. A variant with only rows/cols (no scrub) allows files at that
resolution without blanking.
When a device has variants, the evaluation finds the first variant whose constraints match the DICOM file. If no variant matches, the engine skips the device.
Skipping abandons the whole device rule, including its filter decision.
Evaluation continues to later device rules and then the exclusion list, which
commonly denies the file. As a result, every resolution you want allowed needs
its own variant, even one with no scrub regions (variant(rows=780, cols=800)). The engine does not implicitly allow a resolution with no matching
variant.
Scrub region coordinates¶
Scrub regions are (x, y, width, height) tuples in pixel units. The origin
(0, 0) is the top-left corner of the image. x increases to the right, y
increases downward. A region is the rectangle spanning columns x to
x + width and rows y to y + height.
Regions may extend past the image bounds; the pixel blanker clips to the actual
dimensions. The default catalog uses a wide sentinel width such as 10000 to
blank a full-width banner regardless of the exact column count (for example,
scrub=[(0, 0, 10000, 70)] blanks the top 70 rows). The blanker blanks multiple
regions on one variant independently:
scrub=[(0, 0, 500, 80), (256, 0, 256, 22)].
Scrub-only devices¶
A device with action="scrub" accumulates scrub regions but does not make a
filter decision. Evaluation continues to subsequent devices and exclusion rules
to determine whether the file is ultimately allowed or denied.
Scrub accumulation is forward-only. An earlier scrub device attaches its
regions to a later allow (or exclusion deny) decision, but not the reverse.
An allow or deny match returns immediately, so evaluation never reaches a
scrub device declared after it. Because default_devices appends
_scrub_only_devices last, its rules contribute only when no earlier device
returned allow. To apply a scrub-only overlay to a file that a specific device
already allows, add the scrub region to that device’s variant instead of relying
on a later scrub-only rule.
device(
"Siemens CT Dose overlay",
"scrub",
manufacturer="SIEMENS",
modality="CT",
image_type_any=["DOSE"],
scrub=[(0, 0, 512, 30)],
)
Exclusion rules¶
Exclusion rules form the deny-list. The engine checks them after all device rules.
deny_modalities¶
deny_modalities(
exact=["SR", "RTPLAN", "RTDOSE"], # exact modality match
substring=["XA", "MG", "US"], # substring modality match
)
deny_when¶
deny_when("Burned-in annotation", burned_in_annotation="=YES")
deny_when("Encapsulated PDF", sop_class="=1.2.840.10008.5.1.4.1.1.104.1")
deny_when("INFINITT PACS", manufacturer="INFINITT", series_number="/[1-9]\\d{3,}/")
All keyword arguments to deny_when are AND’d together. Available parameters
are: sop_class, sop_class_not, burned_in_annotation, image_type_empty,
image_type_any, image_type_exclude, manufacturer, manufacturer_model_name,
manufacturer_model_name_fallback, modality, modality_not,
series_number, conversion_type_present, graphic_annotation_absent,
referenced_instance_absent.
sop_class_not matches when the SOP Class UID matches none of its patterns
(an allowlist condition). graphic_annotation_absent=True matches when the
Graphic Annotation Sequence (0070,0001) is absent or empty.
referenced_instance_absent=True matches when the Current Requested Procedure
Evidence Sequence (0040,A375) nests no Referenced SOP Instance UID
(0008,1155).
Presentation state admission¶
Modality PR instances are admitted on the presence of a graphic annotation.
Two ordered deny_when rules implement this:
deny_when(
"unsupported presentation state",
modality="=PR",
sop_class_not=[
"=1.2.840.10008.5.1.4.1.1.11.1", # Grayscale (GSPS)
"=1.2.840.10008.5.1.4.1.1.11.2", # Color
"=1.2.840.10008.5.1.4.1.1.11.3", # Pseudo-Color
"=1.2.840.10008.5.1.4.1.1.11.4", # Blending
"=1.2.840.10008.5.1.4.1.1.11.5", # XA/XRF Grayscale
"=1.2.840.10008.5.1.4.1.1.11.12", # Variable Modality LUT
],
),
deny_when(
"no annotation data",
modality="=PR",
graphic_annotation_absent=True,
),
A PR instance whose SOP Class UID is outside the six admitted 2D softcopy
presentation state classes is denied with reason unsupported presentation state. An admitted-class PR instance carrying no Graphic Annotation Sequence
is denied with reason no annotation data. An admitted-class PR instance
with a non-empty Graphic Annotation Sequence reaches the default allow
action. The Empty ImageType exclusion is scoped with modality_not=["=PR"]
so it does not reject presentation states, which carry no Image Type.
Key Object Selection admission¶
Key Object Selection (KO) documents, SOP Class
1.2.840.10008.5.1.4.1.1.88.59, are admitted on the presence of a referenced
instance. Two ordered deny_when rules replace the former single SR/KO deny:
deny_when(
"unsupported structured report",
sop_class="^1.2.840.10008.5.1.4.1.1.8",
sop_class_not=["=1.2.840.10008.5.1.4.1.1.88.59"],
),
deny_when(
"no referenced instances",
sop_class="=1.2.840.10008.5.1.4.1.1.88.59",
referenced_instance_absent=True,
),
The first rule denies the SR family and the three non-SR classes caught by the
^1.2.840.10008.5.1.4.1.1.8 prefix (Ophthalmic Visual Field, Ophthalmic
Thickness Map, Corneal Topography Map), while exempting KO. The second denies a
KO that references no instance with reason no referenced instances. A KO that
references at least one instance reaches the default allow action. The
Empty ImageType exclusion is scoped with modality_not=["=PR", "=KO"] so it
does not reject KO, which carries no Image Type.
Evaluation order¶
Iterate device rules top-to-bottom. For each rule, check device-level fields, then check variants.
On first match:
"allow": return allow decision with accumulated scrub regions, stop."deny": return deny decision, stop."scrub": accumulate scrub regions, continue.
After all devices, iterate exclusion rules top-to-bottom.
On first exclusion match, return deny decision.
If nothing matched, return the default action (
"allow"for the default catalog).
CatalogDecision¶
Every call to DeviceCatalog.evaluate() returns a frozen CatalogDecision:
@dataclass(frozen=True, slots=True)
class CatalogDecision:
action: str # "allow", "deny", or "scrub"
reason: str # matched rule name or exclusion reason
scrub_regions: list[ScrubRegion] # (x, y, w, h) regions to blank
matched_variant: Variant | None # the variant that matched, if any
The reason field receives the device name argument for device matches, or
a constructed string for exclusion matches (for example, "Denied modality: XA").
Default catalog¶
dicom_dre.default_catalog contains the device and exclusion rules. It
groups device rules by modality:
CR/DX (computed radiography and digital radiography)
CT, CT/PET, PET/MR
NM (nuclear medicine)
Mammography
Breast MRI
Ultrasound
Scrub-only devices (dose overlays and workstation-generated images)
The exclusion list covers denied modalities, SOP classes, burned-in annotations,
empty ImageType, specific vendors (INFINITT, Vidar, iCAD, biopsy equipment),
and image type combinations (DERIVED, SECONDARY, MRSC).
get_default_catalog() assembles all device lists and the exclusion list into a
DeviceCatalog with default_action="allow".
For a step-by-step walkthrough of adding support for a new device, see Extending the catalog.