Mercurial > hg > orthanc
comparison UnitTestsSources/FromDcmtk.cpp @ 786:b6d6b65142e8
DicomModification
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 05 May 2014 13:07:10 +0200 |
parents | 7cbed653476f |
children | ecedd89055db |
comparison
equal
deleted
inserted
replaced
785:7cbed653476f | 786:b6d6b65142e8 |
---|---|
1 #include "gtest/gtest.h" | 1 #include "gtest/gtest.h" |
2 | 2 |
3 #include "../OrthancServer/FromDcmtkBridge.h" | 3 #include "../OrthancServer/FromDcmtkBridge.h" |
4 #include "../OrthancServer/OrthancInitialization.h" | 4 #include "../OrthancServer/OrthancInitialization.h" |
5 #include "../OrthancServer/DicomModification.h" | |
5 #include "../Core/OrthancException.h" | 6 #include "../Core/OrthancException.h" |
6 | 7 |
7 using namespace Orthanc; | 8 using namespace Orthanc; |
8 | 9 |
9 TEST(DicomFormat, Tag) | 10 TEST(DicomFormat, Tag) |
20 | 21 |
21 // Test ==() and !=() operators | 22 // Test ==() and !=() operators |
22 ASSERT_TRUE(DICOM_TAG_PATIENT_ID == DicomTag(0x0010, 0x0020)); | 23 ASSERT_TRUE(DICOM_TAG_PATIENT_ID == DicomTag(0x0010, 0x0020)); |
23 ASSERT_FALSE(DICOM_TAG_PATIENT_ID != DicomTag(0x0010, 0x0020)); | 24 ASSERT_FALSE(DICOM_TAG_PATIENT_ID != DicomTag(0x0010, 0x0020)); |
24 } | 25 } |
25 | |
26 | |
27 namespace Orthanc | |
28 { | |
29 class DicomModification | |
30 { | |
31 /** | |
32 * Process: | |
33 * (1) Remove private tags | |
34 * (2) Remove tags specified by the user | |
35 * (3) Replace tags | |
36 **/ | |
37 | |
38 private: | |
39 typedef std::set<DicomTag> Removals; | |
40 typedef std::map<DicomTag, std::string> Replacements; | |
41 typedef std::map< std::pair<DicomRootLevel, std::string>, std::string> UidMap; | |
42 | |
43 Removals removals_; | |
44 Replacements replacements_; | |
45 bool removePrivateTags_; | |
46 DicomRootLevel level_; | |
47 UidMap uidMap_; | |
48 | |
49 void MapDicomIdentifier(ParsedDicomFile& dicom, | |
50 DicomRootLevel level) | |
51 { | |
52 std::auto_ptr<DicomTag> tag; | |
53 | |
54 switch (level) | |
55 { | |
56 case DicomRootLevel_Study: | |
57 tag.reset(new DicomTag(DICOM_TAG_STUDY_INSTANCE_UID)); | |
58 break; | |
59 | |
60 case DicomRootLevel_Series: | |
61 tag.reset(new DicomTag(DICOM_TAG_SERIES_INSTANCE_UID)); | |
62 break; | |
63 | |
64 case DicomRootLevel_Instance: | |
65 tag.reset(new DicomTag(DICOM_TAG_SOP_INSTANCE_UID)); | |
66 break; | |
67 | |
68 default: | |
69 throw OrthancException(ErrorCode_InternalError); | |
70 } | |
71 | |
72 std::string original; | |
73 if (!dicom.GetTagValue(original, *tag)) | |
74 { | |
75 original = ""; | |
76 } | |
77 | |
78 std::string mapped; | |
79 //bool isNew; | |
80 | |
81 UidMap::const_iterator previous = uidMap_.find(std::make_pair(level, original)); | |
82 if (previous == uidMap_.end()) | |
83 { | |
84 mapped = FromDcmtkBridge::GenerateUniqueIdentifier(level); | |
85 uidMap_.insert(std::make_pair(std::make_pair(level, original), mapped)); | |
86 //isNew = true; | |
87 } | |
88 else | |
89 { | |
90 mapped = previous->second; | |
91 //isNew = false; | |
92 } | |
93 | |
94 dicom.Replace(*tag, mapped); | |
95 | |
96 //return isNew; | |
97 } | |
98 | |
99 public: | |
100 DicomModification() | |
101 { | |
102 removePrivateTags_ = false; | |
103 level_ = DicomRootLevel_Instance; | |
104 } | |
105 | |
106 void Keep(const DicomTag& tag) | |
107 { | |
108 removals_.erase(tag); | |
109 } | |
110 | |
111 void Remove(const DicomTag& tag) | |
112 { | |
113 removals_.insert(tag); | |
114 replacements_.erase(tag); | |
115 } | |
116 | |
117 bool IsRemoved(const DicomTag& tag) const | |
118 { | |
119 return removals_.find(tag) != removals_.end(); | |
120 } | |
121 | |
122 void Replace(const DicomTag& tag, | |
123 const std::string& value) | |
124 { | |
125 removals_.erase(tag); | |
126 replacements_[tag] = value; | |
127 } | |
128 | |
129 bool IsReplaced(const DicomTag& tag) const | |
130 { | |
131 return replacements_.find(tag) != replacements_.end(); | |
132 } | |
133 | |
134 const std::string& GetReplacement(const DicomTag& tag) const | |
135 { | |
136 Replacements::const_iterator it = replacements_.find(tag); | |
137 | |
138 if (it == replacements_.end()) | |
139 { | |
140 throw OrthancException(ErrorCode_InexistentItem); | |
141 } | |
142 else | |
143 { | |
144 return it->second; | |
145 } | |
146 } | |
147 | |
148 void SetRemovePrivateTags(bool removed) | |
149 { | |
150 removePrivateTags_ = removed; | |
151 } | |
152 | |
153 bool ArePrivateTagsRemoved() const | |
154 { | |
155 return removePrivateTags_; | |
156 } | |
157 | |
158 void SetLevel(DicomRootLevel level) | |
159 { | |
160 uidMap_.clear(); | |
161 level_ = level; | |
162 } | |
163 | |
164 DicomRootLevel GetLevel() const | |
165 { | |
166 return level_; | |
167 } | |
168 | |
169 void SetupAnonymization() | |
170 { | |
171 removals_.clear(); | |
172 replacements_.clear(); | |
173 removePrivateTags_ = true; | |
174 level_ = DicomRootLevel_Patient; | |
175 uidMap_.clear(); | |
176 | |
177 // This is Table E.1-1 from PS 3.15-2008 - DICOM Part 15: Security and System Management Profiles | |
178 removals_.insert(DicomTag(0x0008, 0x0014)); // Instance Creator UID | |
179 //removals_.insert(DicomTag(0x0008, 0x0018)); // SOP Instance UID => set in Apply() | |
180 removals_.insert(DicomTag(0x0008, 0x0050)); // Accession Number | |
181 removals_.insert(DicomTag(0x0008, 0x0080)); // Institution Name | |
182 removals_.insert(DicomTag(0x0008, 0x0081)); // Institution Address | |
183 removals_.insert(DicomTag(0x0008, 0x0090)); // Referring Physician's Name | |
184 removals_.insert(DicomTag(0x0008, 0x0092)); // Referring Physician's Address | |
185 removals_.insert(DicomTag(0x0008, 0x0094)); // Referring Physician's Telephone Numbers | |
186 removals_.insert(DicomTag(0x0008, 0x1010)); // Station Name | |
187 removals_.insert(DicomTag(0x0008, 0x1030)); // Study Description | |
188 removals_.insert(DicomTag(0x0008, 0x103e)); // Series Description | |
189 removals_.insert(DicomTag(0x0008, 0x1040)); // Institutional Department Name | |
190 removals_.insert(DicomTag(0x0008, 0x1048)); // Physician(s) of Record | |
191 removals_.insert(DicomTag(0x0008, 0x1050)); // Performing Physicians' Name | |
192 removals_.insert(DicomTag(0x0008, 0x1060)); // Name of Physician(s) Reading Study | |
193 removals_.insert(DicomTag(0x0008, 0x1070)); // Operators' Name | |
194 removals_.insert(DicomTag(0x0008, 0x1080)); // Admitting Diagnoses Description | |
195 removals_.insert(DicomTag(0x0008, 0x1155)); // Referenced SOP Instance UID | |
196 removals_.insert(DicomTag(0x0008, 0x2111)); // Derivation Description | |
197 //removals_.insert(DicomTag(0x0010, 0x0010)); // Patient's Name => cf. below (*) | |
198 //removals_.insert(DicomTag(0x0010, 0x0020)); // Patient ID => cf. below (*) | |
199 removals_.insert(DicomTag(0x0010, 0x0030)); // Patient's Birth Date | |
200 removals_.insert(DicomTag(0x0010, 0x0032)); // Patient's Birth Time | |
201 removals_.insert(DicomTag(0x0010, 0x0040)); // Patient's Sex | |
202 removals_.insert(DicomTag(0x0010, 0x1000)); // Other Patient Ids | |
203 removals_.insert(DicomTag(0x0010, 0x1001)); // Other Patient Names | |
204 removals_.insert(DicomTag(0x0010, 0x1010)); // Patient's Age | |
205 removals_.insert(DicomTag(0x0010, 0x1020)); // Patient's Size | |
206 removals_.insert(DicomTag(0x0010, 0x1030)); // Patient's Weight | |
207 removals_.insert(DicomTag(0x0010, 0x1090)); // Medical Record Locator | |
208 removals_.insert(DicomTag(0x0010, 0x2160)); // Ethnic Group | |
209 removals_.insert(DicomTag(0x0010, 0x2180)); // Occupation | |
210 removals_.insert(DicomTag(0x0010, 0x21b0)); // Additional Patient's History | |
211 removals_.insert(DicomTag(0x0010, 0x4000)); // Patient Comments | |
212 removals_.insert(DicomTag(0x0018, 0x1000)); // Device Serial Number | |
213 removals_.insert(DicomTag(0x0018, 0x1030)); // Protocol Name | |
214 //removals_.insert(DicomTag(0x0020, 0x000d)); // Study Instance UID => set in Apply() | |
215 //removals_.insert(DicomTag(0x0020, 0x000e)); // Series Instance UID => set in Apply() | |
216 removals_.insert(DicomTag(0x0020, 0x0010)); // Study ID | |
217 removals_.insert(DicomTag(0x0020, 0x0052)); // Frame of Reference UID | |
218 removals_.insert(DicomTag(0x0020, 0x0200)); // Synchronization Frame of Reference UID | |
219 removals_.insert(DicomTag(0x0020, 0x4000)); // Image Comments | |
220 removals_.insert(DicomTag(0x0040, 0x0275)); // Request Attributes Sequence | |
221 removals_.insert(DicomTag(0x0040, 0xa124)); // UID | |
222 removals_.insert(DicomTag(0x0040, 0xa730)); // Content Sequence | |
223 removals_.insert(DicomTag(0x0088, 0x0140)); // Storage Media File-set UID | |
224 removals_.insert(DicomTag(0x3006, 0x0024)); // Referenced Frame of Reference UID | |
225 removals_.insert(DicomTag(0x3006, 0x00c2)); // Related Frame of Reference UID | |
226 | |
227 // Some more removals (from the experience of DICOM files at the CHU of Liege) | |
228 removals_.insert(DicomTag(0x0010, 0x1040)); // Patient's Address | |
229 removals_.insert(DicomTag(0x0032, 0x1032)); // Requesting Physician | |
230 removals_.insert(DicomTag(0x0010, 0x2154)); // PatientTelephoneNumbers | |
231 removals_.insert(DicomTag(0x0010, 0x2000)); // Medical Alerts | |
232 | |
233 // Set the DeidentificationMethod tag | |
234 replacements_.insert(std::make_pair(DicomTag(0x0012, 0x0063), "Orthanc " ORTHANC_VERSION " - PS 3.15-2008 Table E.1-1")); | |
235 | |
236 // Set the PatientIdentityRemoved tag | |
237 replacements_.insert(std::make_pair(DicomTag(0x0012, 0x0062), "YES")); | |
238 | |
239 // (*) Choose a random patient name and ID | |
240 std::string patientId = FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Patient); | |
241 replacements_[DICOM_TAG_PATIENT_ID] = patientId; | |
242 replacements_[DICOM_TAG_PATIENT_NAME] = patientId; | |
243 } | |
244 | |
245 void Apply(ParsedDicomFile& toModify) | |
246 { | |
247 // Check the request | |
248 assert(DicomRootLevel_Patient + 1 == DicomRootLevel_Study && | |
249 DicomRootLevel_Study + 1 == DicomRootLevel_Series && | |
250 DicomRootLevel_Series + 1 == DicomRootLevel_Instance); | |
251 | |
252 if (IsRemoved(DICOM_TAG_PATIENT_ID) || | |
253 IsRemoved(DICOM_TAG_STUDY_INSTANCE_UID) || | |
254 IsRemoved(DICOM_TAG_SERIES_INSTANCE_UID) || | |
255 IsRemoved(DICOM_TAG_SOP_INSTANCE_UID)) | |
256 { | |
257 throw OrthancException(ErrorCode_BadRequest); | |
258 } | |
259 | |
260 if (level_ == DicomRootLevel_Patient && !IsReplaced(DICOM_TAG_PATIENT_ID)) | |
261 { | |
262 throw OrthancException(ErrorCode_BadRequest); | |
263 } | |
264 | |
265 if (level_ > DicomRootLevel_Patient && IsReplaced(DICOM_TAG_PATIENT_ID)) | |
266 { | |
267 throw OrthancException(ErrorCode_BadRequest); | |
268 } | |
269 | |
270 if (level_ > DicomRootLevel_Study && IsReplaced(DICOM_TAG_STUDY_INSTANCE_UID)) | |
271 { | |
272 throw OrthancException(ErrorCode_BadRequest); | |
273 } | |
274 | |
275 if (level_ > DicomRootLevel_Series && IsReplaced(DICOM_TAG_SERIES_INSTANCE_UID)) | |
276 { | |
277 throw OrthancException(ErrorCode_BadRequest); | |
278 } | |
279 | |
280 // (1) Remove the private tags, if need be | |
281 if (removePrivateTags_) | |
282 { | |
283 toModify.RemovePrivateTags(); | |
284 } | |
285 | |
286 // (2) Remove the tags specified by the user | |
287 for (Removals::const_iterator it = removals_.begin(); | |
288 it != removals_.end(); ++it) | |
289 { | |
290 toModify.Remove(*it); | |
291 } | |
292 | |
293 // (3) Replace the tags | |
294 for (Replacements::const_iterator it = replacements_.begin(); | |
295 it != replacements_.end(); ++it) | |
296 { | |
297 toModify.Replace(it->first, it->second, DicomReplaceMode_InsertIfAbsent); | |
298 } | |
299 | |
300 // (4) Update the DICOM identifiers | |
301 if (level_ <= DicomRootLevel_Study) | |
302 { | |
303 MapDicomIdentifier(toModify, DicomRootLevel_Study); | |
304 } | |
305 | |
306 if (level_ <= DicomRootLevel_Series) | |
307 { | |
308 MapDicomIdentifier(toModify, DicomRootLevel_Series); | |
309 } | |
310 | |
311 if (level_ <= DicomRootLevel_Instance) // Always true | |
312 { | |
313 MapDicomIdentifier(toModify, DicomRootLevel_Instance); | |
314 } | |
315 } | |
316 }; | |
317 } | |
318 | |
319 | 26 |
320 | 27 |
321 TEST(DicomModification, Basic) | 28 TEST(DicomModification, Basic) |
322 { | 29 { |
323 DicomModification m; | 30 DicomModification m; |