"""Strict de-identification profile.
The most aggressive redaction that still yields a DICOM file that opens in most
DICOM viewers and libraries. It is an allow-list profile: it retains a fixed set
of technical elements and removes every element without an explicit rule. Use
only when the pixel data is the sole item of interest and study demographics or
other metadata can be discarded. No UID salt and minimal retained elements; all
unspecified elements are removed. Date and datetime elements are removed entirely
rather than date-shifted, so no jitter is applied; allow-listed time elements
(for example StudyTime, SeriesTime, AcquisitionTime, ContentTime) are retained.
Key Object Selection (KO) and Presentation State (PR) objects carry no pixel
data but hold clinician-curated labels. Their structured-content and
graphic-annotation subtrees are retained through ``content_root_tags`` so the
labels survive, while the shared PHI-removal, date-removal, and free-text
redaction rules de-identify every element inside those subtrees. The PR
rendering sequences that bind annotations to their images and define their
geometry are also retained as content roots (Referenced Series Sequence
``(0008,1115)``, Displayed Area Selection Sequence ``(0070,005A)``, and Graphic
Layer Sequence ``(0070,0060)``), so a viewer can render the annotations.
The resulting file is likely not conformant to the DICOM specification, since
required elements may be removed; it is intended only for pixel-data use, not
for interchange with systems that enforce conformance.
"""
from pydicom.tag import BaseTag
from pydicom.tag import Tag
from dicom_dre.actions import TagAction
from dicom_dre.actions import hash_identifier_param
from dicom_dre.actions import hash_uid
from dicom_dre.actions import hash_value_identifier
from dicom_dre.actions import keep
from dicom_dre.actions import remove
from dicom_dre.actions import set_value
from dicom_dre.profile import DeidProfile
from dicom_dre.profiles.config import ProfileSettings
from dicom_dre.profiles.default import DATE_TAGS
from dicom_dre.profiles.default import EMPTY_TAGS
from dicom_dre.profiles.default import PHI_REMOVE_TAGS
from dicom_dre.profiles.default import PRESERVED_PRIVATE_SPECS
from dicom_dre.profiles.default import redact_description
from dicom_dre.profiles.default import redact_free_text
# 16 UID tags re-hashed in the strict profile, without a salt.
STRICT_UID_TAGS: frozenset[BaseTag] = frozenset(
{
Tag(0x0002, 0x0003), # MediaStorageSOPInstanceUID
Tag(0x0008, 0x0018), # SOPInstanceUID
Tag(0x0008, 0x1155), # ReferencedSOPInstanceUID
Tag(0x0020, 0x000D), # StudyInstanceUID
Tag(0x0020, 0x000E), # SeriesInstanceUID
Tag(0x0020, 0x0052), # FrameOfReferenceUID
Tag(0x0020, 0x0200), # SynchronizationFrameOfReferenceUID
Tag(0x0020, 0x9164), # DimensionOrganizationUID
Tag(0x0028, 0x1199), # PaletteColorLookupTableUID
Tag(0x0028, 0x1214), # LargePaletteColorLookupTableUID
Tag(0x0040, 0xA124), # UID
Tag(0x0070, 0x1101), # PresentationDisplayCollectionUID
Tag(0x0070, 0x1102), # PresentationSequenceCollectionUID
Tag(0x0088, 0x0140), # StorageMediaFileSetUID
Tag(0x3006, 0x0024), # ReferencedFrameOfReferenceUID
Tag(0x3006, 0x00C2), # RelatedFrameOfReferenceUID
}
)
# 326 tags preserved unchanged in the strict profile.
STRICT_KEEP_TAGS: frozenset[BaseTag] = frozenset(
{
Tag(0x0008, 0x0005), # SpecificCharacterSet
Tag(0x0008, 0x0008), # ImageType
Tag(0x0008, 0x0013), # InstanceCreationTime
Tag(0x0008, 0x0016), # SOPClassUID
Tag(0x0008, 0x0030), # StudyTime
Tag(0x0008, 0x0031), # SeriesTime
Tag(0x0008, 0x0032), # AcquisitionTime
Tag(0x0008, 0x0033), # ContentTime
Tag(0x0008, 0x0034), # OverlayTime
Tag(0x0008, 0x0035), # CurveTime
Tag(0x0008, 0x0060), # Modality
Tag(0x0008, 0x0070), # Manufacturer
Tag(0x0008, 0x1090), # ManufacturerModelName
Tag(0x0008, 0x2111), # DerivationDescription
Tag(0x0008, 0x2130), # EventElapsedTime
Tag(0x0008, 0x3010), # IrradiationEventUID
Tag(0x0008, 0x9092), # ReferencedImageEvidenceSequence
Tag(0x0010, 0x0040), # PatientSex
Tag(0x0018, 0x0010), # ContrastBolusAgent
Tag(0x0018, 0x0015), # BodyPartExamined
Tag(0x0018, 0x0020), # ScanningSequence
Tag(0x0018, 0x0021), # SequenceVariant
Tag(0x0018, 0x0022), # ScanOptions
Tag(0x0018, 0x0023), # MRAcquisitionType
Tag(0x0018, 0x0026), # InterventionDrugInformationSequence
Tag(0x0018, 0x0027), # InterventionDrugStopTime
Tag(0x0018, 0x0031), # Radiopharmaceutical
Tag(0x0018, 0x0032), # EnergyWindowCenterline
Tag(0x0018, 0x0035), # InterventionDrugStartTime
Tag(0x0018, 0x0040), # CineRate
Tag(0x0018, 0x0050), # SliceThickness
Tag(0x0018, 0x0060), # KVP
Tag(0x0018, 0x0070), # CountsAccumulated
Tag(0x0018, 0x0071), # AcquisitionTerminationCondition
Tag(0x0018, 0x0073), # AcquisitionStartCondition
Tag(0x0018, 0x0075), # AcquisitionTerminationConditionData
Tag(0x0018, 0x0080), # RepetitionTime
Tag(0x0018, 0x0081), # EchoTime
Tag(0x0018, 0x0082), # InversionTime
Tag(0x0018, 0x0083), # NumberOfAverages
Tag(0x0018, 0x0084), # ImagingFrequency
Tag(0x0018, 0x0085), # ImagedNucleus
Tag(0x0018, 0x0086), # EchoNumbers
Tag(0x0018, 0x0087), # MagneticFieldStrength
Tag(0x0018, 0x0088), # SpacingBetweenSlices
Tag(0x0018, 0x0090), # DataCollectionDiameter
Tag(0x0018, 0x0091), # EchoTrainLength
Tag(0x0018, 0x0095), # PixelBandwidth
Tag(0x0018, 0x1000), # DeviceSerialNumber
Tag(0x0018, 0x1020), # SoftwareVersions
Tag(0x0018, 0x1040), # ContrastBolusRoute
Tag(0x0018, 0x1041), # ContrastBolusVolume
Tag(0x0018, 0x1042), # ContrastBolusStartTime
Tag(0x0018, 0x1043), # ContrastBolusStopTime
Tag(0x0018, 0x1050), # SpatialResolution
Tag(0x0018, 0x1060), # TriggerTime
Tag(0x0018, 0x1063), # FrameTime
Tag(0x0018, 0x1065), # FrameTimeVector
Tag(0x0018, 0x1066), # FrameDelay
Tag(0x0018, 0x106E), # TriggerSamplePosition
Tag(0x0018, 0x1072), # RadiopharmaceuticalStartTime
Tag(0x0018, 0x1073), # RadiopharmaceuticalStopTime
Tag(0x0018, 0x1074), # RadionuclideTotalDose
Tag(0x0018, 0x1075), # RadionuclideHalfLife
Tag(0x0018, 0x1088), # HeartRate
Tag(0x0018, 0x1090), # CardiacNumberOfImages
Tag(0x0018, 0x1100), # ReconstructionDiameter
Tag(0x0018, 0x1110), # DistanceSourceToDetector
Tag(0x0018, 0x1111), # DistanceSourceToPatient
Tag(0x0018, 0x1114), # EstimatedRadiographicMagnificationFactor
Tag(0x0018, 0x1120), # GantryDetectorTilt
Tag(0x0018, 0x1130), # TableHeight
Tag(0x0018, 0x1140), # RotationDirection
Tag(0x0018, 0x1147), # FieldOfViewShape
Tag(0x0018, 0x1149), # FieldOfViewDimensions
Tag(0x0018, 0x1150), # ExposureTime
Tag(0x0018, 0x1151), # XRayTubeCurrent
Tag(0x0018, 0x1152), # Exposure
Tag(0x0018, 0x1153), # ExposureInuAs
Tag(0x0018, 0x1155), # RadiationSetting
Tag(0x0018, 0x1160), # FilterType
Tag(0x0018, 0x1164), # ImagerPixelSpacing
Tag(0x0018, 0x1166), # Grid
Tag(0x0018, 0x1170), # GeneratorPower
Tag(0x0018, 0x1180), # CollimatorGridName
Tag(0x0018, 0x1190), # FocalSpots
Tag(0x0018, 0x1191), # AnodeTargetMaterial
Tag(0x0018, 0x1201), # TimeOfLastCalibration
Tag(0x0018, 0x1210), # ConvolutionKernel
Tag(0x0018, 0x1242), # ActualFrameDuration
Tag(0x0018, 0x1243), # CountRate
Tag(0x0018, 0x1250), # ReceiveCoilName
Tag(0x0018, 0x1300), # ScanVelocity
Tag(0x0018, 0x1301), # WholeBodyTechnique
Tag(0x0018, 0x1302), # ScanLength
Tag(0x0018, 0x1310), # AcquisitionMatrix
Tag(0x0018, 0x1312), # InPlanePhaseEncodingDirection
Tag(0x0018, 0x1314), # FlipAngle
Tag(0x0018, 0x1320), # B1rms
Tag(0x0018, 0x1400), # AcquisitionDeviceProcessingDescription
Tag(0x0018, 0x1401), # AcquisitionDeviceProcessingCode
Tag(0x0018, 0x1405), # RelativeXRayExposure
Tag(0x0018, 0x1450), # ColumnAngulation
Tag(0x0018, 0x1470), # TomoAngle
Tag(0x0018, 0x1480), # TomoTime
Tag(0x0018, 0x1500), # PositionerMotion
Tag(0x0018, 0x1508), # PositionerType
Tag(0x0018, 0x1510), # PositionerPrimaryAngle
Tag(0x0018, 0x1511), # PositionerSecondaryAngle
Tag(0x0018, 0x1530), # DetectorPrimaryAngle
Tag(0x0018, 0x1531), # DetectorSecondaryAngle
Tag(0x0018, 0x1600), # ShutterShape
Tag(0x0018, 0x1620), # VerticesOfThePolygonalShutter
Tag(0x0018, 0x1622), # ShutterPresentationValue
Tag(0x0018, 0x1624), # ShutterPresentationColorCIELabValue
Tag(0x0018, 0x1700), # CollimatorShape
Tag(0x0018, 0x1702), # CollimatorLeftVerticalEdge
Tag(0x0018, 0x1704), # CollimatorRightVerticalEdge
Tag(0x0018, 0x1710), # CenterOfCircularCollimator
Tag(0x0018, 0x1720), # VerticesOfThePolygonalCollimator
Tag(0x0018, 0x2001), # PageNumberVector
Tag(0x0018, 0x5010), # TransducerData
Tag(0x0018, 0x5012), # FocusDepth
Tag(0x0018, 0x5020), # ProcessingFunction
Tag(0x0018, 0x5021), # PostprocessingFunction
Tag(0x0018, 0x5022), # MechanicalIndex
Tag(0x0018, 0x5024), # BoneThermalIndex
Tag(0x0018, 0x5026), # CranialThermalIndex
Tag(0x0018, 0x5027), # SoftTissueThermalIndex
Tag(0x0018, 0x5028), # SoftTissueFocusThermalIndex
Tag(0x0018, 0x5029), # SoftTissueSurfaceThermalIndex
Tag(0x0018, 0x5030), # DynamicRange
Tag(0x0018, 0x5040), # TotalGain
Tag(0x0018, 0x5050), # DepthOfScanField
Tag(0x0018, 0x5100), # PatientPosition
Tag(0x0018, 0x6000), # Sensitivity
Tag(0x0018, 0x6011), # SequenceOfUltrasoundRegions
Tag(0x0018, 0x6020), # ReferencePixelX0
Tag(0x0018, 0x6031), # TransducerType
Tag(0x0018, 0x6032), # PulseRepetitionFrequency
Tag(0x0018, 0x6040), # TMLinePositionX1Retired
Tag(0x0018, 0x7000), # DetectorConditionsNominalFlag
Tag(0x0018, 0x7001), # DetectorTemperature
Tag(0x0018, 0x7004), # DetectorType
Tag(0x0018, 0x7014), # DetectorActiveTime
Tag(0x0018, 0x7020), # DetectorElementPhysicalSize
Tag(0x0018, 0x7022), # DetectorElementSpacing
Tag(0x0018, 0x7030), # FieldOfViewOrigin
Tag(0x0018, 0x7032), # FieldOfViewRotation
Tag(0x0018, 0x7034), # FieldOfViewHorizontalFlip
Tag(0x0018, 0x7040), # GridAbsorbingMaterial
Tag(0x0018, 0x7041), # GridSpacingMaterial
Tag(0x0018, 0x7042), # GridThickness
Tag(0x0018, 0x7044), # GridPitch
Tag(0x0018, 0x7046), # GridAspectRatio
Tag(0x0018, 0x7050), # FilterMaterial
Tag(0x0018, 0x7052), # FilterThicknessMinimum
Tag(0x0018, 0x7054), # FilterThicknessMaximum
Tag(0x0018, 0x7060), # ExposureControlMode
Tag(0x0018, 0x7062), # ExposureControlModeDescription
Tag(0x0018, 0x9005), # PulseSequenceName
Tag(0x0018, 0x9070), # CardiacRRIntervalSpecified
Tag(0x0018, 0x9073), # AcquisitionDuration
Tag(0x0018, 0x9075), # DiffusionDirectionality
Tag(0x0018, 0x9076), # DiffusionGradientDirectionSequence
Tag(0x0018, 0x9077), # ParallelAcquisition
Tag(0x0018, 0x9082), # EffectiveEchoTime
Tag(0x0018, 0x9087), # DiffusionBValue
Tag(0x0018, 0x9371), # XRayDetectorID
Tag(0x0018, 0x9417), # FrameAcquisitionSequence
Tag(0x0018, 0x9434), # ExposureControlSensingRegionsSequence
Tag(0x0018, 0x9435), # ExposureControlSensingRegionShape
Tag(0x0018, 0x9436), # ExposureControlSensingRegionLeftVerticalEdge
Tag(0x0018, 0x9438), # ExposureControlSensingRegionUpperHorizontalEdge
Tag(0x0018, 0x9461), # FieldOfViewDimensionsInFloat
Tag(0x0018, 0x9462), # IsocenterReferenceSystemSequence
Tag(0x0018, 0x9476), # XRayGeometrySequence
Tag(0x0018, 0x9477), # IrradiationEventIdentificationSequence
Tag(0x0018, 0x9504), # XRay3DFrameTypeSequence
Tag(0x0018, 0x9506), # ContributingSourcesSequence
Tag(0x0018, 0x9507), # XRay3DAcquisitionSequence
Tag(0x0018, 0x9508), # PrimaryPositionerScanArc
Tag(0x0018, 0x9528), # AlgorithmDescription
Tag(0x0018, 0x9530), # XRay3DReconstructionSequence
Tag(0x0018, 0x9531), # ReconstructionDescription
Tag(0x0018, 0x9538), # PerProjectionAcquisitionSequence
Tag(0x0018, 0x9732), # PETFrameAcquisitionSequence
Tag(0x0018, 0x9733), # PETDetectorMotionDetailsSequence
Tag(0x0018, 0x9736), # PETFrameCorrectionFactorsSequence
Tag(0x0018, 0x9737), # RadiopharmaceuticalUsageSequence
Tag(0x0018, 0x9738), # AttenuationCorrectionSource
Tag(0x0018, 0x9749), # PETReconstructionSequence
Tag(0x0018, 0x9751), # PETFrameTypeSequence
Tag(0x0018, 0x9810), # ZeroVelocityPixelValue
Tag(0x0020, 0x0011), # SeriesNumber
Tag(0x0020, 0x0012), # AcquisitionNumber
Tag(0x0020, 0x0013), # InstanceNumber
Tag(0x0020, 0x0020), # PatientOrientation
Tag(0x0020, 0x0032), # ImagePositionPatient
Tag(0x0020, 0x0037), # ImageOrientationPatient
Tag(0x0020, 0x0060), # Laterality
Tag(0x0020, 0x0062), # ImageLaterality
Tag(0x0020, 0x1002), # ImagesInAcquisition
Tag(0x0020, 0x1040), # PositionReferenceIndicator
Tag(0x0020, 0x1041), # SliceLocation
Tag(0x0020, 0x9056), # StackID
Tag(0x0020, 0x9057), # InStackPositionNumber
Tag(0x0020, 0x9111), # FrameContentSequence
Tag(0x0020, 0x9113), # PlanePositionSequence
Tag(0x0020, 0x9116), # PlaneOrientationSequence
Tag(0x0020, 0x9128), # TemporalPositionIndex
Tag(0x0020, 0x9153), # TriggerDelayTime
Tag(0x0020, 0x9157), # DimensionIndexValues
Tag(0x0020, 0x9165), # DimensionIndexPointer
Tag(0x0020, 0x9167), # FunctionalGroupPointer
Tag(0x0020, 0x9221), # DimensionOrganizationSequence
Tag(0x0020, 0x9222), # DimensionIndexSequence
Tag(0x0020, 0x9311), # DimensionOrganizationType
Tag(0x0022, 0x0001), # LightPathFilterPassThroughWavelength
Tag(0x0022, 0x0002), # LightPathFilterPassBand
Tag(0x0022, 0x0003), # ImagePathFilterPassThroughWavelength
Tag(0x0022, 0x0005), # PatientEyeMovementCommanded
Tag(0x0022, 0x0006), # PatientEyeMovementCommandCodeSequence
Tag(0x0022, 0x0007), # SphericalLensPower
Tag(0x0022, 0x0008), # CylinderLensPower
Tag(0x0022, 0x0009), # CylinderAxis
Tag(0x0022, 0x1007), # OphthalmicAxialMeasurementsRightEyeSequence
Tag(0x0022, 0x1019), # OphthalmicAxialLength
Tag(0x0022, 0x1050), # OphthalmicAxialLengthMeasurementsSequence
Tag(0x0028, 0x0002), # SamplesPerPixel
Tag(0x0028, 0x0004), # PhotometricInterpretation
Tag(0x0028, 0x0006), # PlanarConfiguration
Tag(0x0028, 0x0008), # NumberOfFrames
Tag(0x0028, 0x0009), # FrameIncrementPointer
Tag(0x0028, 0x000A), # FrameDimensionPointer
Tag(0x0028, 0x0010), # Rows
Tag(0x0028, 0x0011), # Columns
Tag(0x0028, 0x0012), # Planes
Tag(0x0028, 0x0030), # PixelSpacing
Tag(0x0028, 0x0034), # PixelAspectRatio
Tag(0x0028, 0x0100), # BitsAllocated
Tag(0x0028, 0x0101), # BitsStored
Tag(0x0028, 0x0102), # HighBit
Tag(0x0028, 0x0103), # PixelRepresentation
Tag(0x0028, 0x0106), # SmallestImagePixelValue
Tag(0x0028, 0x0107), # LargestImagePixelValue
Tag(0x0028, 0x0120), # PixelPaddingValue
Tag(0x0028, 0x0121), # PixelPaddingRangeLimit
Tag(0x0028, 0x0200), # ImageLocation
Tag(0x0028, 0x0300), # QualityControlImage
Tag(0x0028, 0x0A02), # PixelSpacingCalibrationType
Tag(0x0028, 0x0A04), # PixelSpacingCalibrationDescription
Tag(0x0028, 0x1050), # WindowCenter
Tag(0x0028, 0x1051), # WindowWidth
Tag(0x0028, 0x1052), # RescaleIntercept
Tag(0x0028, 0x1053), # RescaleSlope
Tag(0x0028, 0x1054), # RescaleType
Tag(0x0028, 0x1055), # WindowCenterWidthExplanation
Tag(0x0028, 0x1056), # VOILUTFunction
Tag(0x0028, 0x1090), # RecommendedViewingMode
Tag(0x0028, 0x1101), # RedPaletteColorLookupTableDescriptor
Tag(0x0028, 0x1102), # GreenPaletteColorLookupTableDescriptor
Tag(0x0028, 0x1103), # BluePaletteColorLookupTableDescriptor
Tag(0x0028, 0x1201), # RedPaletteColorLookupTableData
Tag(0x0028, 0x1202), # GreenPaletteColorLookupTableData
Tag(0x0028, 0x1203), # BluePaletteColorLookupTableData
Tag(0x0028, 0x2000), # ICCProfile
Tag(0x0028, 0x2110), # LossyImageCompression
Tag(0x0028, 0x2112), # LossyImageCompressionRatio
Tag(0x0028, 0x2114), # LossyImageCompressionMethod
Tag(0x0028, 0x6010), # RepresentativeFrameNumber
Tag(0x0028, 0x9001), # DataPointRows
Tag(0x0028, 0x9002), # DataPointColumns
Tag(0x0028, 0x9108), # DataRepresentation
Tag(0x0032, 0x0033), # StudyVerifiedTime
Tag(0x0032, 0x0035), # StudyReadTime
Tag(0x0032, 0x1001), # ScheduledStudyStartTime
Tag(0x0032, 0x1011), # ScheduledStudyStopTime
Tag(0x0032, 0x1041), # StudyArrivalTime
Tag(0x0032, 0x1051), # StudyCompletionTime
Tag(0x0038, 0x001B), # ScheduledAdmissionTime
Tag(0x0038, 0x001D), # ScheduledDischargeTime
Tag(0x0038, 0x0021), # AdmittingTime
Tag(0x0038, 0x0032), # DischargeTime
Tag(0x0040, 0x0003), # SPSStartTime
Tag(0x0040, 0x0005), # SPSEndTime
Tag(0x0040, 0x0245), # PPSStartTime
Tag(0x0040, 0x0251), # PPSEndTime
Tag(0x0040, 0x0318), # OrganExposed
Tag(0x0040, 0x8302), # EntranceDoseInmGy
Tag(0x0040, 0xA122), # Time
Tag(0x0040, 0xA193), # TrialObservationTime
Tag(0x0054, 0x0010), # EnergyWindowVector
Tag(0x0054, 0x0011), # NumberOfEnergyWindows
Tag(0x0054, 0x0016), # RadiopharmaceuticalInformationSequence
Tag(0x0054, 0x0020), # DetectorVector
Tag(0x0054, 0x0021), # NumberOfDetectors
Tag(0x0054, 0x0022), # DetectorInformationSequence
Tag(0x0054, 0x0030), # PhaseVector
Tag(0x0054, 0x0031), # NumberOfPhases
Tag(0x0054, 0x0050), # RotationVector
Tag(0x0054, 0x0051), # NumberOfRotations
Tag(0x0054, 0x0060), # RRIntervalVector
Tag(0x0054, 0x0061), # NumberOfRRIntervals
Tag(0x0054, 0x0070), # TimeSlotVector
Tag(0x0054, 0x0071), # NumberOfTimeSlots
Tag(0x0054, 0x0080), # SliceVector
Tag(0x0054, 0x0081), # NumberOfSlices
Tag(0x0054, 0x0090), # AngularViewVector
Tag(0x0054, 0x0100), # TimeSliceVector
Tag(0x0054, 0x0202), # TypeOfDetectorMotion
Tag(0x0054, 0x0300), # RadionuclideCodeSequence
Tag(0x0054, 0x1000), # SeriesType
Tag(0x0054, 0x1001), # Units
Tag(0x0054, 0x1006), # SUVType
Tag(0x0054, 0x1102), # DecayCorrection
Tag(0x0054, 0x1300), # FrameReferenceTime
Tag(0x0054, 0x1323), # ScatterFractionFactor
Tag(0x0054, 0x1330), # ImageIndex
Tag(0x0054, 0x1400), # CountsIncluded
Tag(0x0054, 0x1401), # DeadTimeCorrectionFlag
Tag(0x0062, 0x0002), # SegmentSequence
Tag(0x2050, 0x0020), # PresentationLUTShape
Tag(0x5200, 0x9229), # SharedFunctionalGroupsSequence
Tag(0x5200, 0x9230), # PerFrameFunctionalGroupsSequence
Tag(0x7FE0, 0x0010), # PixelData
}
)
# Content-root sequences whose subtree is retained verbatim under
# remove_unspecified. These carry the KO/PR labels, cross-object references, and
# the PR rendering sequences (image binding, displayed area, graphic layers)
# that let a viewer render retained annotations; the shared element rules (PHI
# removal, date removal, free-text redaction, UID hashing) still de-identify
# every element within them. Structural and coded members (Value Type, Concept
# Name/Code sequences, Graphic Data/Type, reference SOP sequences) carry no PHI
# and are preserved.
STRICT_CONTENT_ROOT_TAGS: frozenset[BaseTag] = frozenset(
{
Tag(0x0008, 0x1115), # ReferencedSeriesSequence (PR-to-image binding)
Tag(0x0008, 0x2218), # AnatomicRegionSequence
Tag(0x0040, 0xA043), # ConceptNameCodeSequence (KO document title)
Tag(0x0040, 0xA370), # ReferencedRequestSequence
Tag(0x0040, 0xA375), # CurrentRequestedProcedureEvidenceSequence
Tag(0x0040, 0xA525), # IdenticalDocumentsSequence
Tag(0x0040, 0xA730), # ContentSequence
Tag(0x0070, 0x0001), # GraphicAnnotationSequence
Tag(0x0070, 0x005A), # DisplayedAreaSelectionSequence (PR spatial reference)
Tag(0x0070, 0x0060), # GraphicLayerSequence (PR layer definitions)
}
)
[docs]
def strict_profile(settings: ProfileSettings | None = None) -> DeidProfile:
"""Construct a strict profile.
Minimal metadata. Removes all unspecified elements. Date and datetime
elements are removed entirely rather than date-shifted, so no jitter is
applied; allow-listed time elements are retained. UIDs are re-derived
without the study-ID salt.
Per-patient identity values are
supplied at apply time via :class:`DeidParameters`. When PatientID,
PatientName, or AccessionNumber are not supplied, the original element value
is hashed with ``settings.hash_salt`` and the study identifier;
PatientName reuses the PatientID hash.
The free-text description fields take a per-patient value verbatim; when it
is ``None`` they are redacted from the dataset using
``settings.allowlist_csv`` at apply time.
KO and PR label subtrees, along with the PR rendering sequences that bind
annotations to their images and define their geometry, are retained through
``content_root_tags``. The
shared PHI-removal, date-removal, and free-text redaction rules are applied
so those retained subtrees are de-identified: dates are removed entirely
(not date-shifted), and the free-text label
fields are redacted against the allowlist. Because the profile hashes UIDs
without the study salt, references resolve within a single strict
export but not against objects de-identified by another profile.
"""
settings = settings or ProfileSettings()
uid_action = hash_uid(settings.uid_root) # no study-ID salt
rules: dict[BaseTag, TagAction] = {}
# PHI removal shared with the default profile, applied first so the
# image-technical keep rules below win on the few overlapping tags. These
# remove PHI wherever it appears, including inside the retained KO/PR
# content subtrees. Dates are removed rather than jittered.
remove_action = remove()
rules.update(dict.fromkeys(PHI_REMOVE_TAGS, remove_action))
rules.update(dict.fromkeys(EMPTY_TAGS, remove_action))
rules.update(dict.fromkeys(DATE_TAGS, remove_action))
# Preserve tags unchanged
rules.update({t: keep() for t in STRICT_KEEP_TAGS})
# Hash UID tags -- no salt
rules.update(dict.fromkeys(STRICT_UID_TAGS, uid_action))
# Identifier substitution -- caller value wins, else hash the original.
# PatientName runs before the PatientID rule so it hashes the original
# PatientID element and matches the value the PatientID rule then writes.
rules[Tag(0x0010, 0x0010)] = hash_identifier_param(
"patient_name", salt=settings.hash_salt, fallback_field="patient_id", source_tag=Tag(0x0010, 0x0020)
)
rules[Tag(0x0010, 0x0020)] = hash_identifier_param("patient_id", salt=settings.hash_salt) # PatientID
rules[Tag(0x0008, 0x0050)] = hash_identifier_param("accession_number", salt=settings.hash_salt) # AccessionNumber
rules[Tag(0x0008, 0x103E)] = redact_description("series_description", settings.allowlist_csv, False)
rules[Tag(0x0008, 0x1030)] = redact_description("study_description", settings.allowlist_csv, False)
rules[Tag(0x0018, 0x1030)] = redact_description("protocol_name", settings.allowlist_csv, False)
# KO/PR free-text label redaction and identifier hashing inside retained
# content subtrees.
redact = redact_free_text(settings.allowlist_csv, False)
rules[Tag(0x0070, 0x0006)] = redact # UnformattedTextValue (ST)
rules[Tag(0x0070, 0x0289)] = redact # TickLabel (SH)
rules[Tag(0x0040, 0xA160)] = redact # TextValue (UT), KO/SR content free text
rules[Tag(0x0062, 0x0020)] = hash_value_identifier(salt=settings.hash_salt) # TrackingID (UT)
# PatientIdentityRemoved -- created if missing and set to YES
rules[Tag(0x0012, 0x0062)] = set_value("YES", create_if_missing=True)
return DeidProfile(
name="Strict",
rules=rules,
keep_groups=frozenset(),
remove_private=True,
remove_curves=True,
remove_overlays=True,
remove_unspecified=True,
allowlist_csv=settings.allowlist_csv,
hash_salt=settings.hash_salt,
uid_root=settings.uid_root,
uid_use_study_salt=False,
preserved_private_specs=PRESERVED_PRIVATE_SPECS,
emits_basic_profile=False,
deid_options=frozenset({"113103", "113104"}),
content_root_tags=STRICT_CONTENT_ROOT_TAGS,
)