# HG changeset patch # User Sebastien Jodogne # Date 1499890484 -7200 # Node ID 595919496bb3581a89877229a047bd78a8623f8c # Parent 06ff7e86638a466572639a3a76464ddc3a27036c# Parent c54c9523adf8fd6820efad96f843e836f3bcc6bf integration issue-46-anonymization->mainline diff -r 06ff7e86638a -r 595919496bb3 NEWS --- a/NEWS Wed Jul 12 09:05:08 2017 +0200 +++ b/NEWS Wed Jul 12 22:14:44 2017 +0200 @@ -5,6 +5,7 @@ -------- * Argument "Since" in URI "/tools/find" (related to issue 53) +* Argument "DicomVersion" in URIs "/{...}/{...}/anonymization" Plugins ------- diff -r 06ff7e86638a -r 595919496bb3 OrthancServer/DicomModification.cpp --- a/OrthancServer/DicomModification.cpp Wed Jul 12 09:05:08 2017 +0200 +++ b/OrthancServer/DicomModification.cpp Wed Jul 12 22:14:44 2017 +0200 @@ -41,7 +41,11 @@ #include // For std::auto_ptr -static const std::string ORTHANC_DEIDENTIFICATION_METHOD = "Orthanc " ORTHANC_VERSION " - PS 3.15-2008 Table E.1-1"; +static const std::string ORTHANC_DEIDENTIFICATION_METHOD_2008 = + "Orthanc " ORTHANC_VERSION " - PS 3.15-2008 Table E.1-1"; + +static const std::string ORTHANC_DEIDENTIFICATION_METHOD_2017c = + "Orthanc " ORTHANC_VERSION " - PS 3.15-2017c Table E.1-1 Basic Profile"; namespace Orthanc { @@ -92,7 +96,8 @@ Replacements::iterator it = replacements_.find(DICOM_TAG_DEIDENTIFICATION_METHOD); if (it != replacements_.end() && - it->second->asString() == ORTHANC_DEIDENTIFICATION_METHOD) + (it->second->asString() == ORTHANC_DEIDENTIFICATION_METHOD_2008 || + it->second->asString() == ORTHANC_DEIDENTIFICATION_METHOD_2017c)) { delete it->second; replacements_.erase(it); @@ -162,6 +167,7 @@ void DicomModification::Keep(const DicomTag& tag) { removals_.erase(tag); + clearings_.erase(tag); RemoveInternal(tag); if (tag.IsPrivate()) @@ -185,6 +191,17 @@ void DicomModification::Remove(const DicomTag& tag) { removals_.insert(tag); + clearings_.erase(tag); + RemoveInternal(tag); + privateTagsToKeep_.erase(tag); + + MarkNotOrthancAnonymization(); + } + + void DicomModification::Clear(const DicomTag& tag) + { + removals_.erase(tag); + clearings_.insert(tag); RemoveInternal(tag); privateTagsToKeep_.erase(tag); @@ -196,10 +213,16 @@ return removals_.find(tag) != removals_.end(); } + bool DicomModification::IsCleared(const DicomTag& tag) const + { + return clearings_.find(tag) != clearings_.end(); + } + void DicomModification::Replace(const DicomTag& tag, const Json::Value& value, bool safeForAnonymization) { + clearings_.erase(tag); removals_.erase(tag); privateTagsToKeep_.erase(tag); ReplaceInternal(tag, value); @@ -267,16 +290,12 @@ } } - void DicomModification::SetupAnonymization() + + void DicomModification::SetupAnonymization2008() { - removals_.clear(); - ClearReplacements(); - removePrivateTags_ = true; - level_ = ResourceType_Patient; - uidMap_.clear(); - privateTagsToKeep_.clear(); - // This is Table E.1-1 from PS 3.15-2008 - DICOM Part 15: Security and System Management Profiles + // https://raw.githubusercontent.com/jodogne/dicom-specification/master/2008/08_15pu.pdf + removals_.insert(DicomTag(0x0008, 0x0014)); // Instance Creator UID //removals_.insert(DicomTag(0x0008, 0x0018)); // SOP Instance UID => set in Apply() removals_.insert(DicomTag(0x0008, 0x0050)); // Accession Number @@ -333,7 +352,589 @@ removals_.insert(DicomTag(0x0010, 0x2000)); // Medical Alerts // Set the DeidentificationMethod tag - ReplaceInternal(DICOM_TAG_DEIDENTIFICATION_METHOD, ORTHANC_DEIDENTIFICATION_METHOD); + ReplaceInternal(DICOM_TAG_DEIDENTIFICATION_METHOD, ORTHANC_DEIDENTIFICATION_METHOD_2008); + } + + +#if 0 + /** + * This is a manual implementation by Alain Mazy. Only kept for reference. + * https://bitbucket.org/sjodogne/orthanc/commits/c6defdc4c611fca2ab528ba2c6937a742e0329a8?at=issue-46-anonymization + **/ + + void DicomModification::SetupAnonymization2011() + { + // This is Table E.1-1 from PS 3.15-2011 - DICOM Part 15: Security and System Management Profiles + // https://raw.githubusercontent.com/jodogne/dicom-specification/master/2011/11_15pu.pdf + + removals_.insert(DicomTag(0x0000, 0x1000)); // Affected SOP Instance UID + removals_.insert(DicomTag(0x0000, 0x1001)); // Requested SOP Instance UID + removals_.insert(DicomTag(0x0002, 0x0003)); // Media Storage SOP Instance UID => TODO: replace with a non-zero length UID that is internally consistent within a set of Instances + removals_.insert(DicomTag(0x0004, 0x1511)); // Referenced SOP Instance UID in File + removals_.insert(DicomTag(0x0008, 0x0010)); // Irradiation Event UID + removals_.insert(DicomTag(0x0008, 0x0014)); // Instance Creator UID + //removals_.insert(DicomTag(0x0008, 0x0018)); // SOP Instance UID => set in Apply() + clearings_.insert(DicomTag(0x0008, 0x0020)); // Study Date + clearings_.insert(DicomTag(0x0008, 0x0021)); // Series Date + clearings_.insert(DicomTag(0x0008, 0x0030)); // Study Time + clearings_.insert(DicomTag(0x0008, 0x0031)); // Series Time + removals_.insert(DicomTag(0x0008, 0x0022)); // Acquisition Date + removals_.insert(DicomTag(0x0008, 0x0023)); // Content Date + removals_.insert(DicomTag(0x0008, 0x0024)); // Overlay Date + removals_.insert(DicomTag(0x0008, 0x0025)); // Curve Date + removals_.insert(DicomTag(0x0008, 0x002a)); // Acquisition DateTime + removals_.insert(DicomTag(0x0008, 0x0032)); // Acquisition Time + removals_.insert(DicomTag(0x0008, 0x0033)); // Content Time + removals_.insert(DicomTag(0x0008, 0x0034)); // Overlay Time + removals_.insert(DicomTag(0x0008, 0x0035)); // Curve Time + removals_.insert(DicomTag(0x0008, 0x0050)); // Accession Number + removals_.insert(DicomTag(0x0008, 0x0058)); // Failed SOP Instance UID List + removals_.insert(DicomTag(0x0008, 0x0080)); // Institution Name + removals_.insert(DicomTag(0x0008, 0x0081)); // Institution Address + removals_.insert(DicomTag(0x0008, 0x0082)); // Institution Code Sequence + removals_.insert(DicomTag(0x0008, 0x0090)); // Referring Physician's Name + removals_.insert(DicomTag(0x0008, 0x0092)); // Referring Physician's Address + removals_.insert(DicomTag(0x0008, 0x0094)); // Referring Physician's Telephone Numbers + removals_.insert(DicomTag(0x0008, 0x0096)); // Referring Physician's Identification Sequence + removals_.insert(DicomTag(0x0008, 0x010d)); // Context Group Extension Creator UID + removals_.insert(DicomTag(0x0008, 0x0201)); // Timezone Offset From UTC + removals_.insert(DicomTag(0x0008, 0x0300)); // Current Patient Location + removals_.insert(DicomTag(0x0008, 0x1010)); // Station Name + removals_.insert(DicomTag(0x0008, 0x1030)); // Study Description + removals_.insert(DicomTag(0x0008, 0x103e)); // Series Description + removals_.insert(DicomTag(0x0008, 0x1040)); // Institutional Department Name + removals_.insert(DicomTag(0x0008, 0x1048)); // Physician(s) of Record + removals_.insert(DicomTag(0x0008, 0x1049)); // Physician(s) of Record Identification Sequence + removals_.insert(DicomTag(0x0008, 0x1050)); // Performing Physicians' Name + removals_.insert(DicomTag(0x0008, 0x1052)); // Performing Physicians Identification Sequence + removals_.insert(DicomTag(0x0008, 0x1060)); // Name of Physician(s) Reading Study + removals_.insert(DicomTag(0x0008, 0x1062)); // Physician Reading Study Identification Sequence + removals_.insert(DicomTag(0x0008, 0x1070)); // Operators' Name + removals_.insert(DicomTag(0x0008, 0x1072)); // Operators' Identification Sequence + removals_.insert(DicomTag(0x0008, 0x1080)); // Admitting Diagnoses Description + removals_.insert(DicomTag(0x0008, 0x1084)); // Admitting Diagnoses Code Sequence + removals_.insert(DicomTag(0x0008, 0x1110)); // Referenced Study Sequence + removals_.insert(DicomTag(0x0008, 0x1111)); // Referenced Performed Procedure Step Sequence + removals_.insert(DicomTag(0x0008, 0x1120)); // Referenced Patient Sequence + removals_.insert(DicomTag(0x0008, 0x1140)); // Referenced Image Sequence + removals_.insert(DicomTag(0x0008, 0x1155)); // Referenced SOP Instance UID + removals_.insert(DicomTag(0x0008, 0x1195)); // Transaction UID + removals_.insert(DicomTag(0x0008, 0x2111)); // Derivation Description + removals_.insert(DicomTag(0x0008, 0x2112)); // Source Image Sequence + removals_.insert(DicomTag(0x0008, 0x4000)); // Identifying Comments + removals_.insert(DicomTag(0x0008, 0x9123)); // Creator Version UID + //removals_.insert(DicomTag(0x0010, 0x0010)); // Patient's Name => cf. below (*) + //removals_.insert(DicomTag(0x0010, 0x0020)); // Patient ID => cf. below (*) + removals_.insert(DicomTag(0x0010, 0x0030)); // Patient's Birth Date + removals_.insert(DicomTag(0x0010, 0x0032)); // Patient's Birth Time + clearings_.insert(DicomTag(0x0010, 0x0040)); // Patient's Sex + removals_.insert(DicomTag(0x0010, 0x0050)); // Patient's Insurance Plan Code Sequence + removals_.insert(DicomTag(0x0010, 0x0101)); // Patient's Primary Language Code Sequence + removals_.insert(DicomTag(0x0010, 0x0102)); // Patient's Primary Language Modifier Code Sequence + removals_.insert(DicomTag(0x0010, 0x1000)); // Other Patient Ids + removals_.insert(DicomTag(0x0010, 0x1001)); // Other Patient Names + removals_.insert(DicomTag(0x0010, 0x1002)); // Other Patient IDs Sequence + removals_.insert(DicomTag(0x0010, 0x1005)); // Patient's Birth Name + removals_.insert(DicomTag(0x0010, 0x1010)); // Patient's Age + removals_.insert(DicomTag(0x0010, 0x1020)); // Patient's Size + removals_.insert(DicomTag(0x0010, 0x1030)); // Patient's Weight + removals_.insert(DicomTag(0x0010, 0x1040)); // Patient's Address + removals_.insert(DicomTag(0x0010, 0x1050)); // Insurance Plan Identification + removals_.insert(DicomTag(0x0010, 0x1060)); // Patient's Mother's Birth Name + removals_.insert(DicomTag(0x0010, 0x1080)); // Military Rank + removals_.insert(DicomTag(0x0010, 0x1081)); // Branch of Service + removals_.insert(DicomTag(0x0010, 0x1090)); // Medical Record Locator + removals_.insert(DicomTag(0x0010, 0x2000)); // Medical Alerts + removals_.insert(DicomTag(0x0010, 0x2110)); // Allergies + removals_.insert(DicomTag(0x0010, 0x2150)); // Country of Residence + removals_.insert(DicomTag(0x0010, 0x2152)); // Region of Residence + removals_.insert(DicomTag(0x0010, 0x2154)); // PatientTelephoneNumbers + removals_.insert(DicomTag(0x0010, 0x2160)); // Ethnic Group + removals_.insert(DicomTag(0x0010, 0x2180)); // Occupation + removals_.insert(DicomTag(0x0010, 0x21a0)); // Smoking Status + removals_.insert(DicomTag(0x0010, 0x21b0)); // Additional Patient's History + removals_.insert(DicomTag(0x0010, 0x21c0)); // Pregnancy Status + removals_.insert(DicomTag(0x0010, 0x21d0)); // Last Menstrual Date + removals_.insert(DicomTag(0x0010, 0x21f0)); // Patient's Religious Preference + removals_.insert(DicomTag(0x0010, 0x2203)); // Patient's Sex Neutered + removals_.insert(DicomTag(0x0010, 0x2297)); // Responsible Person + removals_.insert(DicomTag(0x0010, 0x2299)); // Responsible Organization + removals_.insert(DicomTag(0x0010, 0x4000)); // Patient Comments + removals_.insert(DicomTag(0x0018, 0x0010)); // Contrast Bolus Agent + removals_.insert(DicomTag(0x0018, 0x1000)); // Device Serial Number + removals_.insert(DicomTag(0x0018, 0x1002)); // Device UID + removals_.insert(DicomTag(0x0018, 0x1004)); // Plate ID + removals_.insert(DicomTag(0x0018, 0x1005)); // Generator ID + removals_.insert(DicomTag(0x0018, 0x1007)); // Cassette ID + removals_.insert(DicomTag(0x0018, 0x1008)); // Gantry ID + removals_.insert(DicomTag(0x0018, 0x1030)); // Protocol Name + removals_.insert(DicomTag(0x0018, 0x1400)); // Acquisition Device Processing Description + removals_.insert(DicomTag(0x0018, 0x4000)); // Acquisition Comments + removals_.insert(DicomTag(0x0018, 0x700a)); // Detector ID + removals_.insert(DicomTag(0x0018, 0xa003)); // Contribution Description + removals_.insert(DicomTag(0x0018, 0x9424)); // Acquisition Protocol Description + //removals_.insert(DicomTag(0x0020, 0x000d)); // Study Instance UID => set in Apply() + //removals_.insert(DicomTag(0x0020, 0x000e)); // Series Instance UID => set in Apply() + removals_.insert(DicomTag(0x0020, 0x0010)); // Study ID + removals_.insert(DicomTag(0x0020, 0x0052)); // Frame of Reference UID + removals_.insert(DicomTag(0x0020, 0x0200)); // Synchronization Frame of Reference UID + removals_.insert(DicomTag(0x0020, 0x3401)); // Modifying Device ID + removals_.insert(DicomTag(0x0020, 0x3404)); // Modifying Device Manufacturer + removals_.insert(DicomTag(0x0020, 0x3406)); // Modified Image Description + removals_.insert(DicomTag(0x0020, 0x4000)); // Image Comments + removals_.insert(DicomTag(0x0020, 0x9158)); // Frame Comments + removals_.insert(DicomTag(0x0020, 0x9161)); // Concatenation UID + removals_.insert(DicomTag(0x0020, 0x9164)); // Dimension Organization UID + //removals_.insert(DicomTag(0x0028, 0x1199)); // Palette Color Lookup Table UID => TODO: replace with a non-zero length UID that is internally consistent within a set of Instances + //removals_.insert(DicomTag(0x0028, 0x1214)); // Large Palette Color Lookup Table UID => TODO: replace with a non-zero length UID that is internally consistent within a set of Instances + removals_.insert(DicomTag(0x0028, 0x4000)); // Image Presentation Comments + removals_.insert(DicomTag(0x0032, 0x0012)); // Study ID Issuer + removals_.insert(DicomTag(0x0032, 0x1020)); // Scheduled Study Location + removals_.insert(DicomTag(0x0032, 0x1021)); // Scheduled Study Location AE Title + removals_.insert(DicomTag(0x0032, 0x1030)); // Reason for Study + removals_.insert(DicomTag(0x0032, 0x1032)); // Requesting Physician + removals_.insert(DicomTag(0x0032, 0x1033)); // Requesting Service + removals_.insert(DicomTag(0x0032, 0x1060)); // Requesting Procedure Description + removals_.insert(DicomTag(0x0032, 0x1070)); // Requested Contrast Agent + removals_.insert(DicomTag(0x0032, 0x4000)); // Study Comments + removals_.insert(DicomTag(0x0038, 0x0010)); // Admission ID + removals_.insert(DicomTag(0x0038, 0x0011)); // Issuer of Admission ID + removals_.insert(DicomTag(0x0038, 0x001e)); // Scheduled Patient Institution Residence + removals_.insert(DicomTag(0x0038, 0x0020)); // Admitting Date + removals_.insert(DicomTag(0x0038, 0x0021)); // Admitting Time + removals_.insert(DicomTag(0x0038, 0x0040)); // Discharge Diagnosis Description + removals_.insert(DicomTag(0x0038, 0x0050)); // Special Needs + removals_.insert(DicomTag(0x0038, 0x0060)); // Service Episode ID + removals_.insert(DicomTag(0x0038, 0x0061)); // Issuer of Service Episode ID + removals_.insert(DicomTag(0x0038, 0x0062)); // Service Episode Description + removals_.insert(DicomTag(0x0038, 0x0400)); // Patient's Institution Residence + removals_.insert(DicomTag(0x0038, 0x0500)); // Patient State + removals_.insert(DicomTag(0x0038, 0x4000)); // Visit Comments + removals_.insert(DicomTag(0x0038, 0x1234)); // Referenced Patient Alias Sequence + removals_.insert(DicomTag(0x0040, 0x0001)); // Scheduled Station AE Title + removals_.insert(DicomTag(0x0040, 0x0002)); // Scheduled Procedure Step Start Date + removals_.insert(DicomTag(0x0040, 0x0003)); // Scheduled Procedure Step Start Time + removals_.insert(DicomTag(0x0040, 0x0004)); // Scheduled Procedure Step End Date + removals_.insert(DicomTag(0x0040, 0x0005)); // Scheduled Procedure Step End Time + removals_.insert(DicomTag(0x0040, 0x0006)); // Scheduled Performing Physician Name + removals_.insert(DicomTag(0x0040, 0x0007)); // Scheduled Procedure Step Description + removals_.insert(DicomTag(0x0040, 0x000b)); // Scheduled Performing Physician Identification Sequence + removals_.insert(DicomTag(0x0040, 0x0010)); // Scheduled Station Name + removals_.insert(DicomTag(0x0040, 0x0011)); // Scheduled Procedure Step Location + removals_.insert(DicomTag(0x0040, 0x0012)); // Pre-Medication + removals_.insert(DicomTag(0x0040, 0x0241)); // Performed Station AE Title + removals_.insert(DicomTag(0x0040, 0x0242)); // Performed Station Name + removals_.insert(DicomTag(0x0040, 0x0243)); // Performed Location + removals_.insert(DicomTag(0x0040, 0x0244)); // Performed Procedure Step Start Date + removals_.insert(DicomTag(0x0040, 0x0245)); // Performed Procedure Step Start Time + removals_.insert(DicomTag(0x0040, 0x0248)); // Performed Station Name Code Sequence + removals_.insert(DicomTag(0x0040, 0x0253)); // Performed Procedure Step ID + removals_.insert(DicomTag(0x0040, 0x0254)); // Performed Procedure Step Description + removals_.insert(DicomTag(0x0040, 0x0275)); // Request Attributes Sequence + removals_.insert(DicomTag(0x0040, 0x0280)); // Comments on Performed Procedure Step + removals_.insert(DicomTag(0x0040, 0x0555)); // Acquisition Context Sequence + removals_.insert(DicomTag(0x0040, 0x1001)); // Requested Procedure ID + removals_.insert(DicomTag(0x0040, 0x1010)); // Names of Intended Recipient of Results + removals_.insert(DicomTag(0x0040, 0x1011)); // Intended Recipient of Results Identification Sequence + removals_.insert(DicomTag(0x0040, 0x1004)); // Patient Transport Arrangements + removals_.insert(DicomTag(0x0040, 0x1005)); // Requested Procedure Location + removals_.insert(DicomTag(0x0040, 0x1101)); // Person Identification Code Sequence + removals_.insert(DicomTag(0x0040, 0x1102)); // Person Address + removals_.insert(DicomTag(0x0040, 0x1103)); // Person Telephone Numbers + removals_.insert(DicomTag(0x0040, 0x1400)); // Requested Procedure Comments + removals_.insert(DicomTag(0x0040, 0x2001)); // Reason for Imaging Service Request + removals_.insert(DicomTag(0x0040, 0x2008)); // Order Entered By + removals_.insert(DicomTag(0x0040, 0x2009)); // Order Enterer Location + removals_.insert(DicomTag(0x0040, 0x2010)); // Order Callback Phone Number + removals_.insert(DicomTag(0x0040, 0x2016)); // Placer Order Number of Imaging Service Request + removals_.insert(DicomTag(0x0040, 0x2017)); // Filler Order Number of Imaging Service Request + removals_.insert(DicomTag(0x0040, 0x2400)); // Imaging Service Request Comments + removals_.insert(DicomTag(0x0040, 0x4023)); // Referenced General Purpose Scheduled Procedure Step Transaction UID + removals_.insert(DicomTag(0x0040, 0x4025)); // Scheduled Station Name Code Sequence + removals_.insert(DicomTag(0x0040, 0x4027)); // Scheduled Station Geographic Location Code Sequence + removals_.insert(DicomTag(0x0040, 0x4030)); // Performed Station Geographic Location Code Sequence + removals_.insert(DicomTag(0x0040, 0x4034)); // Scheduled Human Performers Sequence + removals_.insert(DicomTag(0x0040, 0x4035)); // Actual Human Performers Sequence + removals_.insert(DicomTag(0x0040, 0x4036)); // Human Performers Organization + removals_.insert(DicomTag(0x0040, 0x4037)); // Human Performers Name + removals_.insert(DicomTag(0x0040, 0xa027)); // Verifying Organization + removals_.insert(DicomTag(0x0040, 0xa073)); // Verifying Observer Sequence + removals_.insert(DicomTag(0x0040, 0xa075)); // Verifying Observer Name + removals_.insert(DicomTag(0x0040, 0xa078)); // Author Observer Sequence + removals_.insert(DicomTag(0x0040, 0xa07a)); // Participant Sequence + removals_.insert(DicomTag(0x0040, 0xa07c)); // Custodial Organization Sequence + removals_.insert(DicomTag(0x0040, 0xa088)); // Verifying Observer Identification Code Sequence + removals_.insert(DicomTag(0x0040, 0xa123)); // Person Name + removals_.insert(DicomTag(0x0040, 0xa124)); // UID + removals_.insert(DicomTag(0x0040, 0xa730)); // Content Sequence + removals_.insert(DicomTag(0x0040, 0x3001)); // Confidentiality Constraint on Patient Data Description + removals_.insert(DicomTag(0x0040, 0xdb0c)); // Template Extension Organization UID + removals_.insert(DicomTag(0x0040, 0xdb0d)); // Template Extension Creator UID + removals_.insert(DicomTag(0x0070, 0x0001)); // Graphic Annotation Sequence + removals_.insert(DicomTag(0x0070, 0x0084)); // Content Creator's Name + removals_.insert(DicomTag(0x0070, 0x0086)); // Content Creator's Identification Code Sequence + removals_.insert(DicomTag(0x0070, 0x031a)); // Fiducial UID + removals_.insert(DicomTag(0x0088, 0x0140)); // Storage Media File-set UID + removals_.insert(DicomTag(0x0088, 0x0200)); // Icon Image Sequence + removals_.insert(DicomTag(0x0088, 0x0904)); // Topic Title + removals_.insert(DicomTag(0x0088, 0x0906)); // Topic Subject + removals_.insert(DicomTag(0x0088, 0x0910)); // Topic Author + removals_.insert(DicomTag(0x0088, 0x0912)); // Topic Key Words + removals_.insert(DicomTag(0x0400, 0x0100)); // Digital Signature UID + removals_.insert(DicomTag(0x0400, 0x0402)); // Referenced Digital Signature Sequence + removals_.insert(DicomTag(0x0400, 0x0403)); // Referenced SOP Instance MAC Sequence + removals_.insert(DicomTag(0x0400, 0x0404)); // MAC + removals_.insert(DicomTag(0x0400, 0x0550)); // Modified Attributes Sequence + removals_.insert(DicomTag(0x0400, 0x0561)); // Original Attributes Sequence + removals_.insert(DicomTag(0x2030, 0x0020)); // Text String + removals_.insert(DicomTag(0x3006, 0x0024)); // Referenced Frame of Reference UID + removals_.insert(DicomTag(0x3006, 0x00c2)); // Related Frame of Reference UID + removals_.insert(DicomTag(0x300a, 0x0013)); // Dose Reference UID + removals_.insert(DicomTag(0x300e, 0x0008)); // Reviewer Name + removals_.insert(DicomTag(0x4000, 0x0010)); // Arbitrary + removals_.insert(DicomTag(0x4000, 0x4000)); // Text Comments + removals_.insert(DicomTag(0x4008, 0x0042)); // Results ID Issuer + removals_.insert(DicomTag(0x4008, 0x0102)); // Interpretation Recorder + removals_.insert(DicomTag(0x4008, 0x010a)); // Interpretation Transcriber + removals_.insert(DicomTag(0x4008, 0x010b)); // Interpretation Text + removals_.insert(DicomTag(0x4008, 0x010c)); // Interpretation Author + removals_.insert(DicomTag(0x4008, 0x0111)); // Interpretation Approver Sequence + removals_.insert(DicomTag(0x4008, 0x0114)); // Physician Approving Interpretation + removals_.insert(DicomTag(0x4008, 0x0115)); // Interpretation Diagnosis Description + removals_.insert(DicomTag(0x4008, 0x0118)); // Results Distribution List Sequence + removals_.insert(DicomTag(0x4008, 0x0119)); // Distribution Name + removals_.insert(DicomTag(0x4008, 0x011a)); // Distribution Address + removals_.insert(DicomTag(0x4008, 0x0202)); // Interpretation ID Issuer + removals_.insert(DicomTag(0x4008, 0x0300)); // Impressions + removals_.insert(DicomTag(0x4008, 0x4000)); // Results Comments + removals_.insert(DicomTag(0xfffa, 0xfffa)); // Digital Signature Sequence + removals_.insert(DicomTag(0xfffc, 0xfffc)); // Data Set Trailing Padding + //removals_.insert(DicomTag(0x60xx, 0x4000)); // Overlay Comments => TODO + //removals_.insert(DicomTag(0x60xx, 0x3000)); // Overlay Data => TODO + + // Set the DeidentificationMethod tag + ReplaceInternal(DICOM_TAG_DEIDENTIFICATION_METHOD, ORTHANC_DEIDENTIFICATION_METHOD_2011); + } +#endif + + + + void DicomModification::SetupAnonymization2017c() + { + /** + * This is Table E.1-1 from PS 3.15-2017c (DICOM Part 15: Security + * and System Management Profiles), "basic profile" column. It was + * generated automatically with the + * "../Resources/GenerateAnonymizationProfile.py" script. + * https://raw.githubusercontent.com/jodogne/dicom-specification/master/2017c/part15.pdf + **/ + + // TODO: (50xx,xxxx) with rule X // Curve Data + // TODO: (60xx,3000) with rule X // Overlay Data + // TODO: (60xx,4000) with rule X // Overlay Comments + // Tag (0x0008, 0x0018) is set in Apply() // SOP Instance UID + // Tag (0x0010, 0x0010) is set below (*) // Patient's Name + // Tag (0x0010, 0x0020) is set below (*) // Patient ID + // Tag (0x0020, 0x000d) is set in Apply() // Study Instance UID + // Tag (0x0020, 0x000e) is set in Apply() // Series Instance UID + clearings_.insert(DicomTag(0x0008, 0x0020)); // Study Date + clearings_.insert(DicomTag(0x0008, 0x0023)); /* Z/D */ // Content Date + clearings_.insert(DicomTag(0x0008, 0x0030)); // Study Time + clearings_.insert(DicomTag(0x0008, 0x0033)); /* Z/D */ // Content Time + clearings_.insert(DicomTag(0x0008, 0x0050)); // Accession Number + clearings_.insert(DicomTag(0x0008, 0x0090)); // Referring Physician's Name + clearings_.insert(DicomTag(0x0008, 0x009c)); // Consulting Physician's Name + clearings_.insert(DicomTag(0x0010, 0x0030)); // Patient's Birth Date + clearings_.insert(DicomTag(0x0010, 0x0040)); // Patient's Sex + clearings_.insert(DicomTag(0x0018, 0x0010)); /* Z/D */ // Contrast Bolus Agent + clearings_.insert(DicomTag(0x0020, 0x0010)); // Study ID + clearings_.insert(DicomTag(0x0040, 0x1101)); /* D */ // Person Identification Code Sequence + clearings_.insert(DicomTag(0x0040, 0x2016)); // Placer Order Number / Imaging Service Request + clearings_.insert(DicomTag(0x0040, 0x2017)); // Filler Order Number / Imaging Service Request + clearings_.insert(DicomTag(0x0040, 0xa073)); /* D */ // Verifying Observer Sequence + clearings_.insert(DicomTag(0x0040, 0xa075)); /* D */ // Verifying Observer Name + clearings_.insert(DicomTag(0x0040, 0xa088)); // Verifying Observer Identification Code Sequence + clearings_.insert(DicomTag(0x0040, 0xa123)); /* D */ // Person Name + clearings_.insert(DicomTag(0x0070, 0x0001)); /* D */ // Graphic Annotation Sequence + clearings_.insert(DicomTag(0x0070, 0x0084)); // Content Creator's Name + removals_.insert(DicomTag(0x0000, 0x1000)); // Affected SOP Instance UID + removals_.insert(DicomTag(0x0000, 0x1001)); /* TODO UID */ // Requested SOP Instance UID + removals_.insert(DicomTag(0x0002, 0x0003)); /* TODO UID */ // Media Storage SOP Instance UID + removals_.insert(DicomTag(0x0004, 0x1511)); /* TODO UID */ // Referenced SOP Instance UID in File + removals_.insert(DicomTag(0x0008, 0x0014)); /* TODO UID */ // Instance Creator UID + removals_.insert(DicomTag(0x0008, 0x0015)); // Instance Coercion DateTime + removals_.insert(DicomTag(0x0008, 0x0021)); /* X/D */ // Series Date + removals_.insert(DicomTag(0x0008, 0x0022)); /* X/Z */ // Acquisition Date + removals_.insert(DicomTag(0x0008, 0x0024)); // Overlay Date + removals_.insert(DicomTag(0x0008, 0x0025)); // Curve Date + removals_.insert(DicomTag(0x0008, 0x002a)); /* X/D */ // Acquisition DateTime + removals_.insert(DicomTag(0x0008, 0x0031)); /* X/D */ // Series Time + removals_.insert(DicomTag(0x0008, 0x0032)); /* X/Z */ // Acquisition Time + removals_.insert(DicomTag(0x0008, 0x0034)); // Overlay Time + removals_.insert(DicomTag(0x0008, 0x0035)); // Curve Time + removals_.insert(DicomTag(0x0008, 0x0058)); /* TODO UID */ // Failed SOP Instance UID List + removals_.insert(DicomTag(0x0008, 0x0080)); /* X/Z/D */ // Institution Name + removals_.insert(DicomTag(0x0008, 0x0081)); // Institution Address + removals_.insert(DicomTag(0x0008, 0x0082)); /* X/Z/D */ // Institution Code Sequence + removals_.insert(DicomTag(0x0008, 0x0092)); // Referring Physician's Address + removals_.insert(DicomTag(0x0008, 0x0094)); // Referring Physician's Telephone Numbers + removals_.insert(DicomTag(0x0008, 0x0096)); // Referring Physician Identification Sequence + removals_.insert(DicomTag(0x0008, 0x009d)); // Consulting Physician Identification Sequence + removals_.insert(DicomTag(0x0008, 0x0201)); // Timezone Offset From UTC + removals_.insert(DicomTag(0x0008, 0x1010)); /* X/Z/D */ // Station Name + removals_.insert(DicomTag(0x0008, 0x1030)); // Study Description + removals_.insert(DicomTag(0x0008, 0x103e)); // Series Description + removals_.insert(DicomTag(0x0008, 0x1040)); // Institutional Department Name + removals_.insert(DicomTag(0x0008, 0x1048)); // Physician(s) of Record + removals_.insert(DicomTag(0x0008, 0x1049)); // Physician(s) of Record Identification Sequence + removals_.insert(DicomTag(0x0008, 0x1050)); // Performing Physicians' Name + removals_.insert(DicomTag(0x0008, 0x1052)); // Performing Physician Identification Sequence + removals_.insert(DicomTag(0x0008, 0x1060)); // Name of Physician(s) Reading Study + removals_.insert(DicomTag(0x0008, 0x1062)); // Physician(s) Reading Study Identification Sequence + removals_.insert(DicomTag(0x0008, 0x1070)); /* X/Z/D */ // Operators' Name + removals_.insert(DicomTag(0x0008, 0x1072)); /* X/D */ // Operators' Identification Sequence + removals_.insert(DicomTag(0x0008, 0x1080)); // Admitting Diagnoses Description + removals_.insert(DicomTag(0x0008, 0x1084)); // Admitting Diagnoses Code Sequence + removals_.insert(DicomTag(0x0008, 0x1110)); /* X/Z */ // Referenced Study Sequence + removals_.insert(DicomTag(0x0008, 0x1111)); /* X/Z/D */ // Referenced Performed Procedure Step Sequence + removals_.insert(DicomTag(0x0008, 0x1120)); // Referenced Patient Sequence + removals_.insert(DicomTag(0x0008, 0x1140)); /* X/Z/U* */ // Referenced Image Sequence + removals_.insert(DicomTag(0x0008, 0x1155)); /* TODO UID */ // Referenced SOP Instance UID + removals_.insert(DicomTag(0x0008, 0x1195)); /* TODO UID */ // Transaction UID + removals_.insert(DicomTag(0x0008, 0x2111)); // Derivation Description + removals_.insert(DicomTag(0x0008, 0x2112)); /* X/Z/U* */ // Source Image Sequence + removals_.insert(DicomTag(0x0008, 0x3010)); /* TODO UID */ // Irradiation Event UID + removals_.insert(DicomTag(0x0008, 0x4000)); // Identifying Comments + removals_.insert(DicomTag(0x0010, 0x0021)); // Issuer of Patient ID + removals_.insert(DicomTag(0x0010, 0x0032)); // Patient's Birth Time + removals_.insert(DicomTag(0x0010, 0x0050)); // Patient's Insurance Plan Code Sequence + removals_.insert(DicomTag(0x0010, 0x0101)); // Patient's Primary Language Code Sequence + removals_.insert(DicomTag(0x0010, 0x0102)); // Patient's Primary Language Modifier Code Sequence + removals_.insert(DicomTag(0x0010, 0x1000)); // Other Patient IDs + removals_.insert(DicomTag(0x0010, 0x1001)); // Other Patient Names + removals_.insert(DicomTag(0x0010, 0x1002)); // Other Patient IDs Sequence + removals_.insert(DicomTag(0x0010, 0x1005)); // Patient's Birth Name + removals_.insert(DicomTag(0x0010, 0x1010)); // Patient's Age + removals_.insert(DicomTag(0x0010, 0x1020)); // Patient's Size + removals_.insert(DicomTag(0x0010, 0x1030)); // Patient's Weight + removals_.insert(DicomTag(0x0010, 0x1040)); // Patient Address + removals_.insert(DicomTag(0x0010, 0x1050)); // Insurance Plan Identification + removals_.insert(DicomTag(0x0010, 0x1060)); // Patient's Mother's Birth Name + removals_.insert(DicomTag(0x0010, 0x1080)); // Military Rank + removals_.insert(DicomTag(0x0010, 0x1081)); // Branch of Service + removals_.insert(DicomTag(0x0010, 0x1090)); // Medical Record Locator + removals_.insert(DicomTag(0x0010, 0x1100)); // Referenced Patient Photo Sequence + removals_.insert(DicomTag(0x0010, 0x2000)); // Medical Alerts + removals_.insert(DicomTag(0x0010, 0x2110)); // Allergies + removals_.insert(DicomTag(0x0010, 0x2150)); // Country of Residence + removals_.insert(DicomTag(0x0010, 0x2152)); // Region of Residence + removals_.insert(DicomTag(0x0010, 0x2154)); // Patient's Telephone Numbers + removals_.insert(DicomTag(0x0010, 0x2155)); // Patient's Telecom Information + removals_.insert(DicomTag(0x0010, 0x2160)); // Ethnic Group + removals_.insert(DicomTag(0x0010, 0x2180)); // Occupation + removals_.insert(DicomTag(0x0010, 0x21a0)); // Smoking Status + removals_.insert(DicomTag(0x0010, 0x21b0)); // Additional Patient's History + removals_.insert(DicomTag(0x0010, 0x21c0)); // Pregnancy Status + removals_.insert(DicomTag(0x0010, 0x21d0)); // Last Menstrual Date + removals_.insert(DicomTag(0x0010, 0x21f0)); // Patient's Religious Preference + removals_.insert(DicomTag(0x0010, 0x2203)); /* X/Z */ // Patient Sex Neutered + removals_.insert(DicomTag(0x0010, 0x2297)); // Responsible Person + removals_.insert(DicomTag(0x0010, 0x2299)); // Responsible Organization + removals_.insert(DicomTag(0x0010, 0x4000)); // Patient Comments + removals_.insert(DicomTag(0x0018, 0x1000)); /* X/Z/D */ // Device Serial Number + removals_.insert(DicomTag(0x0018, 0x1002)); /* TODO UID */ // Device UID + removals_.insert(DicomTag(0x0018, 0x1004)); // Plate ID + removals_.insert(DicomTag(0x0018, 0x1005)); // Generator ID + removals_.insert(DicomTag(0x0018, 0x1007)); // Cassette ID + removals_.insert(DicomTag(0x0018, 0x1008)); // Gantry ID + removals_.insert(DicomTag(0x0018, 0x1030)); /* X/D */ // Protocol Name + removals_.insert(DicomTag(0x0018, 0x1400)); /* X/D */ // Acquisition Device Processing Description + removals_.insert(DicomTag(0x0018, 0x2042)); /* TODO UID */ // Target UID + removals_.insert(DicomTag(0x0018, 0x4000)); // Acquisition Comments + removals_.insert(DicomTag(0x0018, 0x700a)); /* X/D */ // Detector ID + removals_.insert(DicomTag(0x0018, 0x9424)); // Acquisition Protocol Description + removals_.insert(DicomTag(0x0018, 0x9516)); /* X/D */ // Start Acquisition DateTime + removals_.insert(DicomTag(0x0018, 0x9517)); /* X/D */ // End Acquisition DateTime + removals_.insert(DicomTag(0x0018, 0xa003)); // Contribution Description + removals_.insert(DicomTag(0x0020, 0x0052)); /* TODO UID */ // Frame of Reference UID + removals_.insert(DicomTag(0x0020, 0x0200)); /* TODO UID */ // Synchronization Frame of Reference UID + removals_.insert(DicomTag(0x0020, 0x3401)); // Modifying Device ID + removals_.insert(DicomTag(0x0020, 0x3404)); // Modifying Device Manufacturer + removals_.insert(DicomTag(0x0020, 0x3406)); // Modified Image Description + removals_.insert(DicomTag(0x0020, 0x4000)); // Image Comments + removals_.insert(DicomTag(0x0020, 0x9158)); // Frame Comments + removals_.insert(DicomTag(0x0020, 0x9161)); /* TODO UID */ // Concatenation UID + removals_.insert(DicomTag(0x0020, 0x9164)); /* TODO UID */ // Dimension Organization UID + removals_.insert(DicomTag(0x0028, 0x1199)); /* TODO UID */ // Palette Color Lookup Table UID + removals_.insert(DicomTag(0x0028, 0x1214)); /* TODO UID */ // Large Palette Color Lookup Table UID + removals_.insert(DicomTag(0x0028, 0x4000)); // Image Presentation Comments + removals_.insert(DicomTag(0x0032, 0x0012)); // Study ID Issuer + removals_.insert(DicomTag(0x0032, 0x1020)); // Scheduled Study Location + removals_.insert(DicomTag(0x0032, 0x1021)); // Scheduled Study Location AE Title + removals_.insert(DicomTag(0x0032, 0x1030)); // Reason for Study + removals_.insert(DicomTag(0x0032, 0x1032)); // Requesting Physician + removals_.insert(DicomTag(0x0032, 0x1033)); // Requesting Service + removals_.insert(DicomTag(0x0032, 0x1060)); /* X/Z */ // Requested Procedure Description + removals_.insert(DicomTag(0x0032, 0x1070)); // Requested Contrast Agent + removals_.insert(DicomTag(0x0032, 0x4000)); // Study Comments + removals_.insert(DicomTag(0x0038, 0x0004)); // Referenced Patient Alias Sequence + removals_.insert(DicomTag(0x0038, 0x0010)); // Admission ID + removals_.insert(DicomTag(0x0038, 0x0011)); // Issuer of Admission ID + removals_.insert(DicomTag(0x0038, 0x001e)); // Scheduled Patient Institution Residence + removals_.insert(DicomTag(0x0038, 0x0020)); // Admitting Date + removals_.insert(DicomTag(0x0038, 0x0021)); // Admitting Time + removals_.insert(DicomTag(0x0038, 0x0040)); // Discharge Diagnosis Description + removals_.insert(DicomTag(0x0038, 0x0050)); // Special Needs + removals_.insert(DicomTag(0x0038, 0x0060)); // Service Episode ID + removals_.insert(DicomTag(0x0038, 0x0061)); // Issuer of Service Episode ID + removals_.insert(DicomTag(0x0038, 0x0062)); // Service Episode Description + removals_.insert(DicomTag(0x0038, 0x0300)); // Current Patient Location + removals_.insert(DicomTag(0x0038, 0x0400)); // Patient's Institution Residence + removals_.insert(DicomTag(0x0038, 0x0500)); // Patient State + removals_.insert(DicomTag(0x0038, 0x4000)); // Visit Comments + removals_.insert(DicomTag(0x0040, 0x0001)); // Scheduled Station AE Title + removals_.insert(DicomTag(0x0040, 0x0002)); // Scheduled Procedure Step Start Date + removals_.insert(DicomTag(0x0040, 0x0003)); // Scheduled Procedure Step Start Time + removals_.insert(DicomTag(0x0040, 0x0004)); // Scheduled Procedure Step End Date + removals_.insert(DicomTag(0x0040, 0x0005)); // Scheduled Procedure Step End Time + removals_.insert(DicomTag(0x0040, 0x0006)); // Scheduled Performing Physician Name + removals_.insert(DicomTag(0x0040, 0x0007)); // Scheduled Procedure Step Description + removals_.insert(DicomTag(0x0040, 0x000b)); // Scheduled Performing Physician Identification Sequence + removals_.insert(DicomTag(0x0040, 0x0010)); // Scheduled Station Name + removals_.insert(DicomTag(0x0040, 0x0011)); // Scheduled Procedure Step Location + removals_.insert(DicomTag(0x0040, 0x0012)); // Pre-Medication + removals_.insert(DicomTag(0x0040, 0x0241)); // Performed Station AE Title + removals_.insert(DicomTag(0x0040, 0x0242)); // Performed Station Name + removals_.insert(DicomTag(0x0040, 0x0243)); // Performed Location + removals_.insert(DicomTag(0x0040, 0x0244)); // Performed Procedure Step Start Date + removals_.insert(DicomTag(0x0040, 0x0245)); // Performed Procedure Step Start Time + removals_.insert(DicomTag(0x0040, 0x0250)); // Performed Procedure Step End Date + removals_.insert(DicomTag(0x0040, 0x0251)); // Performed Procedure Step End Time + removals_.insert(DicomTag(0x0040, 0x0253)); // Performed Procedure Step ID + removals_.insert(DicomTag(0x0040, 0x0254)); // Performed Procedure Step Description + removals_.insert(DicomTag(0x0040, 0x0275)); // Request Attributes Sequence + removals_.insert(DicomTag(0x0040, 0x0280)); // Comments on the Performed Procedure Step + removals_.insert(DicomTag(0x0040, 0x0555)); // Acquisition Context Sequence + removals_.insert(DicomTag(0x0040, 0x1001)); // Requested Procedure ID + removals_.insert(DicomTag(0x0040, 0x1004)); // Patient Transport Arrangements + removals_.insert(DicomTag(0x0040, 0x1005)); // Requested Procedure Location + removals_.insert(DicomTag(0x0040, 0x1010)); // Names of Intended Recipient of Results + removals_.insert(DicomTag(0x0040, 0x1011)); // Intended Recipients of Results Identification Sequence + removals_.insert(DicomTag(0x0040, 0x1102)); // Person Address + removals_.insert(DicomTag(0x0040, 0x1103)); // Person's Telephone Numbers + removals_.insert(DicomTag(0x0040, 0x1104)); // Person's Telecom Information + removals_.insert(DicomTag(0x0040, 0x1400)); // Requested Procedure Comments + removals_.insert(DicomTag(0x0040, 0x2001)); // Reason for the Imaging Service Request + removals_.insert(DicomTag(0x0040, 0x2008)); // Order Entered By + removals_.insert(DicomTag(0x0040, 0x2009)); // Order Enterer Location + removals_.insert(DicomTag(0x0040, 0x2010)); // Order Callback Phone Number + removals_.insert(DicomTag(0x0040, 0x2011)); // Order Callback Telecom Information + removals_.insert(DicomTag(0x0040, 0x2400)); // Imaging Service Request Comments + removals_.insert(DicomTag(0x0040, 0x3001)); // Confidentiality Constraint on Patient Data Description + removals_.insert(DicomTag(0x0040, 0x4005)); // Scheduled Procedure Step Start DateTime + removals_.insert(DicomTag(0x0040, 0x4010)); // Scheduled Procedure Step Modification DateTime + removals_.insert(DicomTag(0x0040, 0x4011)); // Expected Completion DateTime + removals_.insert(DicomTag(0x0040, 0x4023)); /* TODO UID */ // Referenced General Purpose Scheduled Procedure Step Transaction UID + removals_.insert(DicomTag(0x0040, 0x4025)); // Scheduled Station Name Code Sequence + removals_.insert(DicomTag(0x0040, 0x4027)); // Scheduled Station Geographic Location Code Sequence + removals_.insert(DicomTag(0x0040, 0x4028)); // Performed Station Name Code Sequence + removals_.insert(DicomTag(0x0040, 0x4030)); // Performed Station Geographic Location Code Sequence + removals_.insert(DicomTag(0x0040, 0x4034)); // Scheduled Human Performers Sequence + removals_.insert(DicomTag(0x0040, 0x4035)); // Actual Human Performers Sequence + removals_.insert(DicomTag(0x0040, 0x4036)); // Human Performers Organization + removals_.insert(DicomTag(0x0040, 0x4037)); // Human Performers Name + removals_.insert(DicomTag(0x0040, 0x4050)); // Performed Procedure Step Start DateTime + removals_.insert(DicomTag(0x0040, 0x4051)); // Performed Procedure Step End DateTime + removals_.insert(DicomTag(0x0040, 0x4052)); // Procedure Step Cancellation DateTime + removals_.insert(DicomTag(0x0040, 0xa027)); // Verifying Organization + removals_.insert(DicomTag(0x0040, 0xa078)); // Author Observer Sequence + removals_.insert(DicomTag(0x0040, 0xa07a)); // Participant Sequence + removals_.insert(DicomTag(0x0040, 0xa07c)); // Custodial Organization Sequence + removals_.insert(DicomTag(0x0040, 0xa124)); /* TODO UID */ // UID + removals_.insert(DicomTag(0x0040, 0xa171)); /* TODO UID */ // Observation UID + removals_.insert(DicomTag(0x0040, 0xa172)); /* TODO UID */ // Referenced Observation UID (Trial) + removals_.insert(DicomTag(0x0040, 0xa192)); // Observation Date (Trial) + removals_.insert(DicomTag(0x0040, 0xa193)); // Observation Time (Trial) + removals_.insert(DicomTag(0x0040, 0xa307)); // Current Observer (Trial) + removals_.insert(DicomTag(0x0040, 0xa352)); // Verbal Source (Trial) + removals_.insert(DicomTag(0x0040, 0xa353)); // Address (Trial) + removals_.insert(DicomTag(0x0040, 0xa354)); // Telephone Number (Trial) + removals_.insert(DicomTag(0x0040, 0xa358)); // Verbal Source Identifier Code Sequence (Trial) + removals_.insert(DicomTag(0x0040, 0xa402)); /* TODO UID */ // Observation Subject UID (Trial) + removals_.insert(DicomTag(0x0040, 0xa730)); // Content Sequence + removals_.insert(DicomTag(0x0040, 0xdb0c)); /* TODO UID */ // Template Extension Organization UID + removals_.insert(DicomTag(0x0040, 0xdb0d)); /* TODO UID */ // Template Extension Creator UID + removals_.insert(DicomTag(0x0062, 0x0021)); /* TODO UID */ // Tracking UID + removals_.insert(DicomTag(0x0070, 0x0086)); // Content Creator's Identification Code Sequence + removals_.insert(DicomTag(0x0070, 0x031a)); /* TODO UID */ // Fiducial UID + removals_.insert(DicomTag(0x0070, 0x1101)); /* TODO UID */ // Presentation Display Collection UID + removals_.insert(DicomTag(0x0070, 0x1102)); /* TODO UID */ // Presentation Sequence Collection UID + removals_.insert(DicomTag(0x0088, 0x0140)); /* TODO UID */ // Storage Media File-set UID + removals_.insert(DicomTag(0x0088, 0x0200)); // Icon Image Sequence(see Note 12) + removals_.insert(DicomTag(0x0088, 0x0904)); // Topic Title + removals_.insert(DicomTag(0x0088, 0x0906)); // Topic Subject + removals_.insert(DicomTag(0x0088, 0x0910)); // Topic Author + removals_.insert(DicomTag(0x0088, 0x0912)); // Topic Keywords + removals_.insert(DicomTag(0x0400, 0x0100)); // Digital Signature UID + removals_.insert(DicomTag(0x0400, 0x0402)); // Referenced Digital Signature Sequence + removals_.insert(DicomTag(0x0400, 0x0403)); // Referenced SOP Instance MAC Sequence + removals_.insert(DicomTag(0x0400, 0x0404)); // MAC + removals_.insert(DicomTag(0x0400, 0x0550)); // Modified Attributes Sequence + removals_.insert(DicomTag(0x0400, 0x0561)); // Original Attributes Sequence + removals_.insert(DicomTag(0x2030, 0x0020)); // Text String + removals_.insert(DicomTag(0x3006, 0x0024)); /* TODO UID */ // Referenced Frame of Reference UID + removals_.insert(DicomTag(0x3006, 0x00c2)); /* TODO UID */ // Related Frame of Reference UID + removals_.insert(DicomTag(0x3008, 0x0105)); // Source Serial Number + removals_.insert(DicomTag(0x300a, 0x0013)); /* TODO UID */ // Dose Reference UID + removals_.insert(DicomTag(0x300c, 0x0113)); // Reason for Omission Description + removals_.insert(DicomTag(0x300e, 0x0008)); /* X/Z */ // Reviewer Name + removals_.insert(DicomTag(0x4000, 0x0010)); // Arbitrary + removals_.insert(DicomTag(0x4000, 0x4000)); // Text Comments + removals_.insert(DicomTag(0x4008, 0x0042)); // Results ID Issuer + removals_.insert(DicomTag(0x4008, 0x0102)); // Interpretation Recorder + removals_.insert(DicomTag(0x4008, 0x010a)); // Interpretation Transcriber + removals_.insert(DicomTag(0x4008, 0x010b)); // Interpretation Text + removals_.insert(DicomTag(0x4008, 0x010c)); // Interpretation Author + removals_.insert(DicomTag(0x4008, 0x0111)); // Interpretation Approver Sequence + removals_.insert(DicomTag(0x4008, 0x0114)); // Physician Approving Interpretation + removals_.insert(DicomTag(0x4008, 0x0115)); // Interpretation Diagnosis Description + removals_.insert(DicomTag(0x4008, 0x0118)); // Results Distribution List Sequence + removals_.insert(DicomTag(0x4008, 0x0119)); // Distribution Name + removals_.insert(DicomTag(0x4008, 0x011a)); // Distribution Address + removals_.insert(DicomTag(0x4008, 0x0202)); // Interpretation ID Issuer + removals_.insert(DicomTag(0x4008, 0x0300)); // Impressions + removals_.insert(DicomTag(0x4008, 0x4000)); // Results Comments + removals_.insert(DicomTag(0xfffa, 0xfffa)); // Digital Signatures Sequence + removals_.insert(DicomTag(0xfffc, 0xfffc)); // Data Set Trailing Padding + + // Set the DeidentificationMethod tag + ReplaceInternal(DICOM_TAG_DEIDENTIFICATION_METHOD, ORTHANC_DEIDENTIFICATION_METHOD_2017c); + } + + + void DicomModification::SetupAnonymization(DicomVersion version) + { + removals_.clear(); + clearings_.clear(); + ClearReplacements(); + removePrivateTags_ = true; + level_ = ResourceType_Patient; + uidMap_.clear(); + privateTagsToKeep_.clear(); + + switch (version) + { + case DicomVersion_2008: + SetupAnonymization2008(); + break; + + case DicomVersion_2017c: + SetupAnonymization2017c(); + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } // Set the PatientIdentityRemoved tag ReplaceInternal(DicomTag(0x0012, 0x0062), "YES"); @@ -461,21 +1062,28 @@ toModify.RemovePrivateTags(privateTagsToKeep_); } - // (2) Remove the tags specified by the user + // (2) Clear the tags specified by the user + for (SetOfTags::const_iterator it = clearings_.begin(); + it != clearings_.end(); ++it) + { + toModify.Clear(*it, true /* only clear if the tag exists in the original file */); + } + + // (3) Remove the tags specified by the user for (SetOfTags::const_iterator it = removals_.begin(); it != removals_.end(); ++it) { toModify.Remove(*it); } - // (3) Replace the tags + // (4) Replace the tags for (Replacements::const_iterator it = replacements_.begin(); it != replacements_.end(); ++it) { toModify.Replace(it->first, *it->second, true /* decode data URI scheme */, DicomReplaceMode_InsertIfAbsent); } - // (4) Update the DICOM identifiers + // (5) Update the DICOM identifiers if (level_ <= ResourceType_Study && !IsReplaced(DICOM_TAG_STUDY_INSTANCE_UID)) { diff -r 06ff7e86638a -r 595919496bb3 OrthancServer/DicomModification.h --- a/OrthancServer/DicomModification.h Wed Jul 12 09:05:08 2017 +0200 +++ b/OrthancServer/DicomModification.h Wed Jul 12 22:14:44 2017 +0200 @@ -52,6 +52,7 @@ typedef std::map< std::pair, std::string> UidMap; SetOfTags removals_; + SetOfTags clearings_; Replacements replacements_; bool removePrivateTags_; ResourceType level_; @@ -73,6 +74,10 @@ void ReplaceInternal(const DicomTag& tag, const Json::Value& value); + void SetupAnonymization2008(); + + void SetupAnonymization2017c(); + public: DicomModification(); @@ -82,11 +87,18 @@ void Remove(const DicomTag& tag); + // Replace the DICOM tag as a NULL/empty value (e.g. for anonymization) + void Clear(const DicomTag& tag); + bool IsRemoved(const DicomTag& tag) const; + bool IsCleared(const DicomTag& tag) const; + + // "safeForAnonymization" tells Orthanc that this replacement does + // not break the anonymization process it implements (for internal use only) void Replace(const DicomTag& tag, const Json::Value& value, // Encoded using UTF-8 - bool safeForAnonymization = false); + bool safeForAnonymization); bool IsReplaced(const DicomTag& tag) const; @@ -108,7 +120,7 @@ return level_; } - void SetupAnonymization(); + void SetupAnonymization(DicomVersion version); void Apply(ParsedDicomFile& toModify); diff -r 06ff7e86638a -r 595919496bb3 OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp Wed Jul 12 09:05:08 2017 +0200 +++ b/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp Wed Jul 12 22:14:44 2017 +0200 @@ -101,7 +101,7 @@ const Json::Value& value = replacements[name]; DicomTag tag = FromDcmtkBridge::ParseTag(name); - target.Replace(tag, value); + target.Replace(tag, value, false); VLOG(1) << "Replace: " << name << " " << tag << " == " << value.toStyledString() << std::endl; @@ -178,46 +178,60 @@ { // curl http://localhost:8042/instances/6e67da51-d119d6ae-c5667437-87b9a8a5-0f07c49f/anonymize -X POST -d '{"Replace":{"PatientName":"hello","0010-0020":"world"},"Keep":["StudyDescription", "SeriesDescription"],"KeepPrivateTags": null,"Remove":["Modality"]}' > Anonymized.dcm - target.SetupAnonymization(); - std::string patientName = target.GetReplacementAsString(DICOM_TAG_PATIENT_NAME); - Json::Value request; - if (call.ParseJsonRequest(request) && request.isObject()) - { - if (request.isMember("KeepPrivateTags")) - { - target.SetRemovePrivateTags(false); - } - - if (request.isMember("Remove")) - { - ParseListOfTags(target, request["Remove"], TagOperation_Remove); - } - - if (request.isMember("Replace")) - { - ParseReplacements(target, request["Replace"]); - } - - if (request.isMember("Keep")) - { - ParseListOfTags(target, request["Keep"], TagOperation_Keep); - } - - if (target.IsReplaced(DICOM_TAG_PATIENT_NAME) && - target.GetReplacement(DICOM_TAG_PATIENT_NAME) == patientName) - { - // Overwrite the random Patient's Name by one that is more - // user-friendly (provided none was specified by the user) - target.Replace(DICOM_TAG_PATIENT_NAME, GeneratePatientName(OrthancRestApi::GetContext(call)), true); - } - - return true; - } - else + if (!call.ParseJsonRequest(request) || + !request.isObject()) { return false; } + + // As of Orthanc 1.2.1, the default anonymization is done + // according to PS 3.15-2017c Table E.1-1 (basic profile) + DicomVersion version = DicomVersion_2017c; + if (request.isMember("DicomVersion")) + { + if (request["DicomVersion"].type() != Json::stringValue) + { + throw OrthancException(ErrorCode_BadFileFormat); + } + else + { + version = StringToDicomVersion(request["DicomVersion"].asString()); + } + } + + target.SetupAnonymization(version); + std::string patientName = target.GetReplacementAsString(DICOM_TAG_PATIENT_NAME); + + if (request.isMember("KeepPrivateTags")) + { + target.SetRemovePrivateTags(false); + } + + if (request.isMember("Remove")) + { + ParseListOfTags(target, request["Remove"], TagOperation_Remove); + } + + if (request.isMember("Replace")) + { + ParseReplacements(target, request["Replace"]); + } + + if (request.isMember("Keep")) + { + ParseListOfTags(target, request["Keep"], TagOperation_Keep); + } + + if (target.IsReplaced(DICOM_TAG_PATIENT_NAME) && + target.GetReplacement(DICOM_TAG_PATIENT_NAME) == patientName) + { + // Overwrite the random Patient's Name by one that is more + // user-friendly (provided none was specified by the user) + target.Replace(DICOM_TAG_PATIENT_NAME, GeneratePatientName(OrthancRestApi::GetContext(call)), true); + } + + return true; } diff -r 06ff7e86638a -r 595919496bb3 OrthancServer/ParsedDicomFile.cpp --- a/OrthancServer/ParsedDicomFile.cpp Wed Jul 12 09:05:08 2017 +0200 +++ b/OrthancServer/ParsedDicomFile.cpp Wed Jul 12 22:14:44 2017 +0200 @@ -508,6 +508,28 @@ } + void ParsedDicomFile::Clear(const DicomTag& tag, + bool onlyIfExists) + { + InvalidateCache(); + + DcmItem* dicom = pimpl_->file_->getDataset(); + DcmTagKey key(tag.GetGroup(), tag.GetElement()); + + if (onlyIfExists && + !dicom->tagExists(key)) + { + // The tag is non-existing, do not clear it + } + else + { + if (!dicom->insertEmptyElement(key, OFTrue /* replace old value */).good()) + { + throw OrthancException(ErrorCode_InternalError); + } + } + } + void ParsedDicomFile::RemovePrivateTagsInternal(const std::set* toKeep) { diff -r 06ff7e86638a -r 595919496bb3 OrthancServer/ParsedDicomFile.h --- a/OrthancServer/ParsedDicomFile.h Wed Jul 12 09:05:08 2017 +0200 +++ b/OrthancServer/ParsedDicomFile.h Wed Jul 12 22:14:44 2017 +0200 @@ -96,6 +96,10 @@ void Remove(const DicomTag& tag); + // Replace the DICOM tag as a NULL/empty value (e.g. for anonymization) + void Clear(const DicomTag& tag, + bool onlyIfExists); + void Replace(const DicomTag& tag, const std::string& utf8Value, bool decodeDataUriScheme, diff -r 06ff7e86638a -r 595919496bb3 OrthancServer/ServerEnumerations.cpp --- a/OrthancServer/ServerEnumerations.cpp Wed Jul 12 09:05:08 2017 +0200 +++ b/OrthancServer/ServerEnumerations.cpp Wed Jul 12 22:14:44 2017 +0200 @@ -462,6 +462,41 @@ } + const char* EnumerationToString(DicomVersion version) + { + switch (version) + { + case DicomVersion_2008: + return "2008"; + break; + + case DicomVersion_2017c: + return "2017c"; + break; + + default: + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + + DicomVersion StringToDicomVersion(const std::string& version) + { + if (version == "2008") + { + return DicomVersion_2008; + } + else if (version == "2017c") + { + return DicomVersion_2017c; + } + else + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + } + + bool IsUserMetadata(MetadataType metadata) { return (metadata >= MetadataType_StartUser && diff -r 06ff7e86638a -r 595919496bb3 OrthancServer/ServerEnumerations.h --- a/OrthancServer/ServerEnumerations.h Wed Jul 12 09:05:08 2017 +0200 +++ b/OrthancServer/ServerEnumerations.h Wed Jul 12 22:14:44 2017 +0200 @@ -135,6 +135,12 @@ IdentifierConstraintType_Wildcard /* Case sensitive, "*" or "?" are the only allowed wildcards */ }; + enum DicomVersion + { + DicomVersion_2008, + DicomVersion_2017c + }; + /** * WARNING: Do not change the explicit values in the enumerations @@ -230,7 +236,11 @@ const char* EnumerationToString(TransferSyntax syntax); + const char* EnumerationToString(DicomVersion version); + ModalityManufacturer StringToModalityManufacturer(const std::string& manufacturer); + DicomVersion StringToDicomVersion(const std::string& version); + bool IsUserMetadata(MetadataType type); } diff -r 06ff7e86638a -r 595919496bb3 Resources/GenerateAnonymizationProfile.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/GenerateAnonymizationProfile.py Wed Jul 12 22:14:44 2017 +0200 @@ -0,0 +1,110 @@ +#!/usr/bin/env python + +# Orthanc - A Lightweight, RESTful DICOM Store +# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics +# Department, University Hospital of Liege, Belgium +# Copyright (C) 2017 Osimis, Belgium +# +# This program is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +import re +import sys +import xml.etree.ElementTree as ET + +# Usage: +# ./GenerateAnonymizationProfile.py ~/Subversion/dicom-specification/2017c/part15.xml + +if len(sys.argv) != 2: + raise Exception('Please provide the path to the part15.xml file from the DICOM standard') + +with open(sys.argv[1], 'r') as f: + root = ET.fromstring(f.read()) + +br = '{http://docbook.org/ns/docbook}' # Shorthand variable + + +LINES = [] + +def FormatLine(command, name): + indentation = 65 + + if len(command) > indentation: + raise Exception('Too long command') + + line = ' ' + command + (' ' * (indentation - len(command))) + '// ' + name + LINES.append(line) + +def FormatUnknown(rawTag, name, profile): + FormatLine('// TODO: %s with rule %s' % (rawTag, profile), name) + + +RAW_TAG_RE = re.compile(r'^\(\s*([0-9A-F]{4})\s*,\s*([0-9A-F]{4})\s*\)$') + + +for table in root.iter('%stable' % br): + if table.attrib['label'] == 'E.1-1': + for row in table.find('%stbody' % br).iter('%str' % br): + rawTag = row.find('%std[2]/%spara' % (br, br)).text + name = row.find('%std[1]/%spara' % (br, br)).text + profile = row.find('%std[5]/%spara' % (br, br)).text + + if len(name.strip()) == 0: + continue + + match = RAW_TAG_RE.match(rawTag) + if match == None: + FormatUnknown(rawTag, name, profile) + else: + tag = '0x%s, 0x%s' % (match.group(1).lower(), match.group(2).lower()) + + if name in [ + 'SOP Instance UID', + 'Series Instance UID', + 'Study Instance UID', + ]: + FormatLine('// Tag (%s) is set in Apply()' % tag, name) + elif name in [ + 'Patient\'s Name', + 'Patient ID', + ]: + FormatLine('// Tag (%s) is set below (*)' % tag, name) + elif profile == 'X': + FormatLine('removals_.insert(DicomTag(%s));' % tag, name) + elif profile.startswith('X/'): + FormatLine('removals_.insert(DicomTag(%s)); /* %s */' % (tag, profile), name) + elif profile == 'Z': + FormatLine('clearings_.insert(DicomTag(%s));' % tag, name) + elif profile == 'D' or profile.startswith('Z/'): + FormatLine('clearings_.insert(DicomTag(%s)); /* %s */' % (tag, profile), name) + elif profile == 'U': + FormatLine('removals_.insert(DicomTag(%s)); /* TODO UID */' % (tag), name) + else: + FormatUnknown(rawTag, name, profile) + +for line in sorted(LINES): + print line + + +# D - replace with a non-zero length value that may be a dummy value and consistent with the VR +# Z - replace with a zero length value, or a non-zero length value that may be a dummy value and consistent with the VR +# X - remove +# K - keep (unchanged for non-sequence attributes, cleaned for sequences) +# C - clean, that is replace with values of similar meaning known not to contain identifying information and consistent with the VR +# U - replace with a non-zero length UID that is internally consistent within a set of Instances +# Z/D - Z unless D is required to maintain IOD conformance (Type 2 versus Type 1) +# X/Z - X unless Z is required to maintain IOD conformance (Type 3 versus Type 2) +# X/D - X unless D is required to maintain IOD conformance (Type 3 versus Type 1) +# X/Z/D - X unless Z or D is required to maintain IOD conformance (Type 3 versus Type 2 versus Type 1) +# X/Z/U* - X unless Z or replacement of contained instance UIDs (U) is required to maintain IOD conformance (Type 3 versus Type 2 versus Type 1 sequences containing UID references) diff -r 06ff7e86638a -r 595919496bb3 UnitTestsSources/FromDcmtkTests.cpp --- a/UnitTestsSources/FromDcmtkTests.cpp Wed Jul 12 09:05:08 2017 +0200 +++ b/UnitTestsSources/FromDcmtkTests.cpp Wed Jul 12 22:14:44 2017 +0200 @@ -77,7 +77,7 @@ TEST(DicomModification, Basic) { DicomModification m; - m.SetupAnonymization(); + m.SetupAnonymization(DicomVersion_2008); //m.SetLevel(DicomRootLevel_Study); //m.ReplacePlainString(DICOM_TAG_PATIENT_ID, "coucou"); //m.ReplacePlainString(DICOM_TAG_PATIENT_NAME, "coucou"); @@ -133,7 +133,7 @@ ASSERT_FALSE(Toolbox::IsUuid(s)); DicomModification m; - m.SetupAnonymization(); + m.SetupAnonymization(DicomVersion_2008); m.Keep(privateTag); m.Apply(o); @@ -143,7 +143,7 @@ ASSERT_TRUE(o.GetTagValue(s, privateTag)); ASSERT_STREQ("private tag", s.c_str()); - m.SetupAnonymization(); + m.SetupAnonymization(DicomVersion_2008); m.Apply(o); ASSERT_FALSE(o.GetTagValue(s, privateTag)); } diff -r 06ff7e86638a -r 595919496bb3 UnitTestsSources/UnitTestsMain.cpp --- a/UnitTestsSources/UnitTestsMain.cpp Wed Jul 12 09:05:08 2017 +0200 +++ b/UnitTestsSources/UnitTestsMain.cpp Wed Jul 12 22:14:44 2017 +0200 @@ -648,6 +648,9 @@ ASSERT_STREQ("Unknown", EnumerationToString(PhotometricInterpretation_Unknown)); ASSERT_THROW(StringToPhotometricInterpretation("Unknown"), OrthancException); + + ASSERT_EQ(DicomVersion_2008, StringToDicomVersion(EnumerationToString(DicomVersion_2008))); + ASSERT_EQ(DicomVersion_2017c, StringToDicomVersion(EnumerationToString(DicomVersion_2017c))); }