comparison OrthancServer/OrthancRestApi.cpp @ 311:26efccdff583

anonymisation
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 21 Dec 2012 12:44:32 +0100
parents 6ab6cdeedf4e
children 1642a354fec8
comparison
equal deleted inserted replaced
310:6ab6cdeedf4e 311:26efccdff583
839 839
840 840
841 841
842 // Modification of DICOM instances ------------------------------------------ 842 // Modification of DICOM instances ------------------------------------------
843 843
844 typedef std::set<DicomTag> Removals; 844 namespace
845 typedef std::map<DicomTag, std::string> Replacements; 845 {
846 typedef std::set<DicomTag> Removals;
847 typedef std::map<DicomTag, std::string> Replacements;
848 }
846 849
847 static void ReplaceInstanceInternal(ParsedDicomFile& toModify, 850 static void ReplaceInstanceInternal(ParsedDicomFile& toModify,
848 const Removals& removals, 851 const Removals& removals,
849 const Replacements& replacements, 852 const Replacements& replacements,
850 DicomReplaceMode mode) 853 DicomReplaceMode mode)
905 target[tag] = value; 908 target[tag] = value;
906 } 909 }
907 } 910 }
908 911
909 912
913 static std::string GeneratePatientName(ServerContext& context)
914 {
915 uint64_t seq = context.GetIndex().IncrementGlobalSequence(GlobalProperty_AnonymizationSequence);
916 return "Anonymized" + boost::lexical_cast<std::string>(seq);
917 }
918
919
910 static void SetupAnonymization(Removals& removals, 920 static void SetupAnonymization(Removals& removals,
911 Replacements& replacements) 921 Replacements& replacements)
912 { 922 {
913 removals.clear(); 923 removals.clear();
914 replacements.clear(); 924 replacements.clear();
915 925
916 // This is table X.1-1 from DICOM supplement 55: Attribute Level Confidentiality 926 // This is Table E.1-1 from PS 3.15-2008 - DICOM Part 15: Security and System Management Profiles
917 removals.insert(DicomTag(0x0008, 0x0014)); // Instance Creator UID 927 removals.insert(DicomTag(0x0008, 0x0014)); // Instance Creator UID
918 // 0008-0018 - SOP Instance UID is automatically set by ReplaceInstanceInternal() 928 //removals.insert(DicomTag(0x0008, 0x0018)); // SOP Instance UID => set by ReplaceInstanceInternal()
919 removals.insert(DicomTag(0x0008, 0x0050)); // Accession number 929 removals.insert(DicomTag(0x0008, 0x0050)); // Accession Number
920 /* removals.insert(DicomTag(0x0008, 0x )); // 930 removals.insert(DicomTag(0x0008, 0x0080)); // Institution Name
921 removals.insert(DicomTag(0x , 0x )); // 931 removals.insert(DicomTag(0x0008, 0x0081)); // Institution Address
922 removals.insert(DicomTag(0x , 0x )); // 932 removals.insert(DicomTag(0x0008, 0x0090)); // Referring Physician's Name
923 removals.insert(DicomTag(0x , 0x )); // 933 removals.insert(DicomTag(0x0008, 0x0092)); // Referring Physician's Address
924 removals.insert(DicomTag(0x , 0x )); // 934 removals.insert(DicomTag(0x0008, 0x0094)); // Referring Physician's Telephone Numbers
925 removals.insert(DicomTag(0x , 0x )); // 935 removals.insert(DicomTag(0x0008, 0x1010)); // Station Name
926 removals.insert(DicomTag(0x , 0x )); // 936 removals.insert(DicomTag(0x0008, 0x1030)); // Study Description
927 removals.insert(DicomTag(0x , 0x )); // 937 removals.insert(DicomTag(0x0008, 0x103e)); // Series Description
928 removals.insert(DicomTag(0x , 0x )); // 938 removals.insert(DicomTag(0x0008, 0x1040)); // Institutional Department Name
929 removals.insert(DicomTag(0x , 0x )); // 939 removals.insert(DicomTag(0x0008, 0x1048)); // Physician(s) of Record
930 removals.insert(DicomTag(0x , 0x )); // 940 removals.insert(DicomTag(0x0008, 0x1050)); // Performing Physicians' Name
931 removals.insert(DicomTag(0x , 0x )); // 941 removals.insert(DicomTag(0x0008, 0x1060)); // Name of Physician(s) Reading Study
932 removals.insert(DicomTag(0x , 0x )); // 942 removals.insert(DicomTag(0x0008, 0x1070)); // Operators' Name
933 removals.insert(DicomTag(0x , 0x )); // 943 removals.insert(DicomTag(0x0008, 0x1080)); // Admitting Diagnoses Description
934 removals.insert(DicomTag(0x , 0x )); // 944 removals.insert(DicomTag(0x0008, 0x1155)); // Referenced SOP Instance UID
935 removals.insert(DicomTag(0x , 0x )); // 945 removals.insert(DicomTag(0x0008, 0x2111)); // Derivation Description
936 removals.insert(DicomTag(0x , 0x )); // 946 removals.insert(DicomTag(0x0010, 0x0010)); // Patient's Name
937 removals.insert(DicomTag(0x , 0x )); // 947 removals.insert(DicomTag(0x0010, 0x0020)); // Patient ID
938 */ 948 removals.insert(DicomTag(0x0010, 0x0030)); // Patient's Birth Date
949 removals.insert(DicomTag(0x0010, 0x0032)); // Patient's Birth Time
950 removals.insert(DicomTag(0x0010, 0x0040)); // Patient's Sex
951 removals.insert(DicomTag(0x0010, 0x1000)); // Other Patient Ids
952 removals.insert(DicomTag(0x0010, 0x1001)); // Other Patient Names
953 removals.insert(DicomTag(0x0010, 0x1010)); // Patient's Age
954 removals.insert(DicomTag(0x0010, 0x1020)); // Patient's Size
955 removals.insert(DicomTag(0x0010, 0x1030)); // Patient's Weight
956 removals.insert(DicomTag(0x0010, 0x1090)); // Medical Record Locator
957 removals.insert(DicomTag(0x0010, 0x2160)); // Ethnic Group
958 removals.insert(DicomTag(0x0010, 0x2180)); // Occupation
959 removals.insert(DicomTag(0x0010, 0x21b0)); // Additional Patient's History
960 removals.insert(DicomTag(0x0010, 0x4000)); // Patient Comments
961 removals.insert(DicomTag(0x0018, 0x1000)); // Device Serial Number
962 removals.insert(DicomTag(0x0018, 0x1030)); // Protocol Name
963 //removals.insert(DicomTag(0x0020, 0x000d)); // Study Instance UID => generated below
964 //removals.insert(DicomTag(0x0020, 0x000e)); // Series Instance UID => generated below
965 removals.insert(DicomTag(0x0020, 0x0010)); // Study ID
966 removals.insert(DicomTag(0x0020, 0x0052)); // Frame of Reference UID
967 removals.insert(DicomTag(0x0020, 0x0200)); // Synchronization Frame of Reference UID
968 removals.insert(DicomTag(0x0020, 0x4000)); // Image Comments
969 removals.insert(DicomTag(0x0040, 0x0275)); // Request Attributes Sequence
970 removals.insert(DicomTag(0x0040, 0xa124)); // UID
971 removals.insert(DicomTag(0x0040, 0xa730)); // Content Sequence
972 removals.insert(DicomTag(0x0088, 0x0140)); // Storage Media File-set UID
973 removals.insert(DicomTag(0x3006, 0x0024)); // Referenced Frame of Reference UID
974 removals.insert(DicomTag(0x3006, 0x00c2)); // Related Frame of Reference UID
975
976 // Some more removals (from the experience of DICOM files at the CHU of Liege)
977 removals.insert(DicomTag(0x0010, 0x1040)); // Patient's Address
978 removals.insert(DicomTag(0x0032, 0x1032)); // Requesting Physician
979
980 // Set the DeidentificationMethod tag
981 replacements.insert(std::make_pair(DicomTag(0x0012, 0x0063), "Orthanc " ORTHANC_VERSION " - PS 3.15-2008 Table E.1-1"));
982
983 // Set the PatientIdentityRemoved
984 replacements.insert(std::make_pair(DicomTag(0x0012, 0x0062), "YES"));
985
986 replacements.insert(std::make_pair(DICOM_TAG_STUDY_INSTANCE_UID,
987 FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Study)));
988 replacements.insert(std::make_pair(DICOM_TAG_SERIES_INSTANCE_UID,
989 FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Series)));
939 } 990 }
940 991
941 992
942 static bool ParseModifyRequest(Removals& removals, 993 static bool ParseModifyRequest(Removals& removals,
943 Replacements& replacements, 994 Replacements& replacements,
960 replacementsPart = request["Replace"]; 1011 replacementsPart = request["Replace"];
961 } 1012 }
962 1013
963 ParseRemovals(removals, removalsPart); 1014 ParseRemovals(removals, removalsPart);
964 ParseReplacements(replacements, replacementsPart); 1015 ParseReplacements(replacements, replacementsPart);
1016
1017 return true;
1018 }
1019 else
1020 {
1021 return false;
1022 }
1023 }
1024
1025
1026 static bool ParseAnonymizationRequest(Removals& removals,
1027 Replacements& replacements,
1028 bool& removePrivateTags,
1029 const RestApi::PostCall& call)
1030 {
1031 removePrivateTags = true;
1032
1033 Json::Value request;
1034 if (call.ParseJsonRequest(request) &&
1035 request.isObject())
1036 {
1037 Json::Value keepPart = Json::arrayValue;
1038 if (request.isMember("Keep"))
1039 {
1040 keepPart = request["Keep"];
1041 }
1042
1043 if (request.isMember("KeepPrivateTags"))
1044 {
1045 removePrivateTags = false;
1046 }
1047
1048 Removals toKeep;
1049 ParseRemovals(toKeep, keepPart);
1050
1051 SetupAnonymization(removals, replacements);
1052
1053 for (Removals::iterator it = toKeep.begin(); it != toKeep.end(); it++)
1054 {
1055 removals.erase(*it);
1056 }
965 1057
966 return true; 1058 return true;
967 } 1059 }
968 else 1060 else
969 { 1061 {
986 { 1078 {
987 std::auto_ptr<ParsedDicomFile> modified(dicom.Clone()); 1079 std::auto_ptr<ParsedDicomFile> modified(dicom.Clone());
988 ReplaceInstanceInternal(*modified, removals, replacements, DicomReplaceMode_InsertIfAbsent); 1080 ReplaceInstanceInternal(*modified, removals, replacements, DicomReplaceMode_InsertIfAbsent);
989 context.GetIndex().SetMetadata(id, MetadataType_ModifiedFrom, id); 1081 context.GetIndex().SetMetadata(id, MetadataType_ModifiedFrom, id);
990 modified->Answer(call.GetOutput()); 1082 modified->Answer(call.GetOutput());
1083 }
1084 }
1085
1086
1087 static void AnonymizeInstance(RestApi::PostCall& call)
1088 {
1089 RETRIEVE_CONTEXT(call);
1090
1091 std::string id = call.GetUriComponent("id", "");
1092 ParsedDicomFile& dicom = context.GetDicomFile(id);
1093
1094 Removals removals;
1095 Replacements replacements;
1096 bool removePrivateTags;
1097
1098 if (ParseAnonymizationRequest(removals, replacements, removePrivateTags, call))
1099 {
1100 // Generate random Patient's name
1101 removals.erase(DicomTag(0x0010, 0x0010));
1102 replacements.insert(std::make_pair(DicomTag(0x0010, 0x0010), GeneratePatientName(context)));
1103
1104 // Generate random Patient ID
1105 removals.erase(DICOM_TAG_PATIENT_ID);
1106 replacements.insert(std::make_pair(DICOM_TAG_PATIENT_ID,
1107 FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Patient)));
1108
1109 std::auto_ptr<ParsedDicomFile> anonymized(dicom.Clone());
1110
1111 if (removePrivateTags)
1112 {
1113 anonymized->RemovePrivateTags();
1114 }
1115
1116 ReplaceInstanceInternal(*anonymized, removals, replacements, DicomReplaceMode_InsertIfAbsent);
1117 context.GetIndex().SetMetadata(id, MetadataType_AnonymizedFrom, id);
1118
1119 anonymized->Answer(call.GetOutput());
991 } 1120 }
992 } 1121 }
993 1122
994 1123
995 static void ModifySeriesInplace(RestApi::PostCall& call) 1124 static void ModifySeriesInplace(RestApi::PostCall& call)
1182 Register("/modalities/{id}/store", DicomStore); 1311 Register("/modalities/{id}/store", DicomStore);
1183 1312
1184 Register("/instances/{id}/modify", ModifyInstance); 1313 Register("/instances/{id}/modify", ModifyInstance);
1185 Register("/series/{id}/modify", ModifySeriesInplace); 1314 Register("/series/{id}/modify", ModifySeriesInplace);
1186 Register("/studies/{id}/modify", ModifyStudyInplace); 1315 Register("/studies/{id}/modify", ModifyStudyInplace);
1316
1317 Register("/instances/{id}/anonymize", AnonymizeInstance);
1187 } 1318 }
1188 } 1319 }