Mercurial > hg > orthanc
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 } |