comparison OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp @ 751:5197fd35333c

refactoring of OrthancRestApi
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 14 Apr 2014 11:28:35 +0200
parents
children aebf0071020e
comparison
equal deleted inserted replaced
750:4afad8cb94fd 751:5197fd35333c
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege,
4 * Belgium
5 *
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * In addition, as a special exception, the copyright holders of this
12 * program give permission to link the code of its release with the
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it
14 * that use the same license as the "OpenSSL" library), and distribute
15 * the linked executables. You must obey the GNU General Public License
16 * in all respects for all of the code used other than "OpenSSL". If you
17 * modify file(s) with this exception, you may extend this exception to
18 * your version of the file(s), but you are not obligated to do so. If
19 * you do not wish to do so, delete this exception statement from your
20 * version. If you delete this exception statement from all source files
21 * in the program, then also delete it here.
22 *
23 * This program is distributed in the hope that it will be useful, but
24 * WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 * General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30 **/
31
32
33 #include "OrthancRestApi.h"
34
35 #include <glog/logging.h>
36
37 namespace Orthanc
38 {
39 // TODO IMPROVE MULTITHREADING
40 // Every call to "ParsedDicomFile" must lock this mutex!!!
41 static boost::mutex cacheMutex_;
42
43
44 // Raw access to the DICOM tags of an instance ------------------------------
45
46 static void GetRawContent(RestApi::GetCall& call)
47 {
48 boost::mutex::scoped_lock lock(cacheMutex_);
49
50 ServerContext& context = OrthancRestApi::GetContext(call);
51
52 std::string id = call.GetUriComponent("id", "");
53 ParsedDicomFile& dicom = context.GetDicomFile(id);
54 dicom.SendPathValue(call.GetOutput(), call.GetTrailingUri());
55 }
56
57
58
59 // Modification of DICOM instances ------------------------------------------
60
61 namespace
62 {
63 typedef std::set<DicomTag> Removals;
64 typedef std::map<DicomTag, std::string> Replacements;
65 typedef std::map< std::pair<DicomRootLevel, std::string>, std::string> UidMap;
66 }
67
68 static void ReplaceInstanceInternal(ParsedDicomFile& toModify,
69 const Removals& removals,
70 const Replacements& replacements,
71 DicomReplaceMode mode,
72 bool removePrivateTags)
73 {
74 if (removePrivateTags)
75 {
76 toModify.RemovePrivateTags();
77 }
78
79 for (Removals::const_iterator it = removals.begin();
80 it != removals.end(); ++it)
81 {
82 toModify.Remove(*it);
83 }
84
85 for (Replacements::const_iterator it = replacements.begin();
86 it != replacements.end(); ++it)
87 {
88 toModify.Replace(it->first, it->second, mode);
89 }
90
91 // A new SOP instance UID is automatically generated
92 std::string instanceUid = FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Instance);
93 toModify.Replace(DICOM_TAG_SOP_INSTANCE_UID, instanceUid, DicomReplaceMode_InsertIfAbsent);
94 }
95
96
97 static void ParseRemovals(Removals& target,
98 const Json::Value& removals)
99 {
100 if (!removals.isArray())
101 {
102 throw OrthancException(ErrorCode_BadRequest);
103 }
104
105 for (Json::Value::ArrayIndex i = 0; i < removals.size(); i++)
106 {
107 std::string name = removals[i].asString();
108 DicomTag tag = FromDcmtkBridge::ParseTag(name);
109 target.insert(tag);
110
111 VLOG(1) << "Removal: " << name << " " << tag << std::endl;
112 }
113 }
114
115
116 static void ParseReplacements(Replacements& target,
117 const Json::Value& replacements)
118 {
119 if (!replacements.isObject())
120 {
121 throw OrthancException(ErrorCode_BadRequest);
122 }
123
124 Json::Value::Members members = replacements.getMemberNames();
125 for (size_t i = 0; i < members.size(); i++)
126 {
127 const std::string& name = members[i];
128 std::string value = replacements[name].asString();
129
130 DicomTag tag = FromDcmtkBridge::ParseTag(name);
131 target[tag] = value;
132
133 VLOG(1) << "Replacement: " << name << " " << tag << " == " << value << std::endl;
134 }
135 }
136
137
138 static std::string GeneratePatientName(ServerContext& context)
139 {
140 uint64_t seq = context.GetIndex().IncrementGlobalSequence(GlobalProperty_AnonymizationSequence);
141 return "Anonymized" + boost::lexical_cast<std::string>(seq);
142 }
143
144
145 static void SetupAnonymization(Removals& removals,
146 Replacements& replacements)
147 {
148 // This is Table E.1-1 from PS 3.15-2008 - DICOM Part 15: Security and System Management Profiles
149 removals.insert(DicomTag(0x0008, 0x0014)); // Instance Creator UID
150 //removals.insert(DicomTag(0x0008, 0x0018)); // SOP Instance UID => set by ReplaceInstanceInternal()
151 removals.insert(DicomTag(0x0008, 0x0050)); // Accession Number
152 removals.insert(DicomTag(0x0008, 0x0080)); // Institution Name
153 removals.insert(DicomTag(0x0008, 0x0081)); // Institution Address
154 removals.insert(DicomTag(0x0008, 0x0090)); // Referring Physician's Name
155 removals.insert(DicomTag(0x0008, 0x0092)); // Referring Physician's Address
156 removals.insert(DicomTag(0x0008, 0x0094)); // Referring Physician's Telephone Numbers
157 removals.insert(DicomTag(0x0008, 0x1010)); // Station Name
158 removals.insert(DicomTag(0x0008, 0x1030)); // Study Description
159 removals.insert(DicomTag(0x0008, 0x103e)); // Series Description
160 removals.insert(DicomTag(0x0008, 0x1040)); // Institutional Department Name
161 removals.insert(DicomTag(0x0008, 0x1048)); // Physician(s) of Record
162 removals.insert(DicomTag(0x0008, 0x1050)); // Performing Physicians' Name
163 removals.insert(DicomTag(0x0008, 0x1060)); // Name of Physician(s) Reading Study
164 removals.insert(DicomTag(0x0008, 0x1070)); // Operators' Name
165 removals.insert(DicomTag(0x0008, 0x1080)); // Admitting Diagnoses Description
166 removals.insert(DicomTag(0x0008, 0x1155)); // Referenced SOP Instance UID
167 removals.insert(DicomTag(0x0008, 0x2111)); // Derivation Description
168 removals.insert(DicomTag(0x0010, 0x0010)); // Patient's Name
169 //removals.insert(DicomTag(0x0010, 0x0020)); // Patient ID => cf. below (*)
170 removals.insert(DicomTag(0x0010, 0x0030)); // Patient's Birth Date
171 removals.insert(DicomTag(0x0010, 0x0032)); // Patient's Birth Time
172 removals.insert(DicomTag(0x0010, 0x0040)); // Patient's Sex
173 removals.insert(DicomTag(0x0010, 0x1000)); // Other Patient Ids
174 removals.insert(DicomTag(0x0010, 0x1001)); // Other Patient Names
175 removals.insert(DicomTag(0x0010, 0x1010)); // Patient's Age
176 removals.insert(DicomTag(0x0010, 0x1020)); // Patient's Size
177 removals.insert(DicomTag(0x0010, 0x1030)); // Patient's Weight
178 removals.insert(DicomTag(0x0010, 0x1090)); // Medical Record Locator
179 removals.insert(DicomTag(0x0010, 0x2160)); // Ethnic Group
180 removals.insert(DicomTag(0x0010, 0x2180)); // Occupation
181 removals.insert(DicomTag(0x0010, 0x21b0)); // Additional Patient's History
182 removals.insert(DicomTag(0x0010, 0x4000)); // Patient Comments
183 removals.insert(DicomTag(0x0018, 0x1000)); // Device Serial Number
184 removals.insert(DicomTag(0x0018, 0x1030)); // Protocol Name
185 //removals.insert(DicomTag(0x0020, 0x000d)); // Study Instance UID => cf. below (*)
186 //removals.insert(DicomTag(0x0020, 0x000e)); // Series Instance UID => cf. below (*)
187 removals.insert(DicomTag(0x0020, 0x0010)); // Study ID
188 removals.insert(DicomTag(0x0020, 0x0052)); // Frame of Reference UID
189 removals.insert(DicomTag(0x0020, 0x0200)); // Synchronization Frame of Reference UID
190 removals.insert(DicomTag(0x0020, 0x4000)); // Image Comments
191 removals.insert(DicomTag(0x0040, 0x0275)); // Request Attributes Sequence
192 removals.insert(DicomTag(0x0040, 0xa124)); // UID
193 removals.insert(DicomTag(0x0040, 0xa730)); // Content Sequence
194 removals.insert(DicomTag(0x0088, 0x0140)); // Storage Media File-set UID
195 removals.insert(DicomTag(0x3006, 0x0024)); // Referenced Frame of Reference UID
196 removals.insert(DicomTag(0x3006, 0x00c2)); // Related Frame of Reference UID
197
198 /**
199 * (*) Patient ID, Study Instance UID and Series Instance UID
200 * are modified by "AnonymizeInstance()" if anonymizing a single
201 * instance, or by "RetrieveMappedUid()" if anonymizing a
202 * patient/study/series.
203 **/
204
205
206 // Some more removals (from the experience of DICOM files at the CHU of Liege)
207 removals.insert(DicomTag(0x0010, 0x1040)); // Patient's Address
208 removals.insert(DicomTag(0x0032, 0x1032)); // Requesting Physician
209 removals.insert(DicomTag(0x0010, 0x2154)); // PatientTelephoneNumbers
210 removals.insert(DicomTag(0x0010, 0x2000)); // Medical Alerts
211
212 // Set the DeidentificationMethod tag
213 replacements.insert(std::make_pair(DicomTag(0x0012, 0x0063), "Orthanc " ORTHANC_VERSION " - PS 3.15-2008 Table E.1-1"));
214
215 // Set the PatientIdentityRemoved tag
216 replacements.insert(std::make_pair(DicomTag(0x0012, 0x0062), "YES"));
217 }
218
219
220 static bool ParseModifyRequest(Removals& removals,
221 Replacements& replacements,
222 bool& removePrivateTags,
223 const RestApi::PostCall& call)
224 {
225 removePrivateTags = false;
226 Json::Value request;
227 if (call.ParseJsonRequest(request) &&
228 request.isObject())
229 {
230 Json::Value removalsPart = Json::arrayValue;
231 Json::Value replacementsPart = Json::objectValue;
232
233 if (request.isMember("Remove"))
234 {
235 removalsPart = request["Remove"];
236 }
237
238 if (request.isMember("Replace"))
239 {
240 replacementsPart = request["Replace"];
241 }
242
243 if (request.isMember("RemovePrivateTags"))
244 {
245 removePrivateTags = true;
246 }
247
248 ParseRemovals(removals, removalsPart);
249 ParseReplacements(replacements, replacementsPart);
250
251 return true;
252 }
253 else
254 {
255 return false;
256 }
257 }
258
259
260 static bool ParseAnonymizationRequest(Removals& removals,
261 Replacements& replacements,
262 bool& removePrivateTags,
263 bool& keepPatientId,
264 RestApi::PostCall& call)
265 {
266 ServerContext& context = OrthancRestApi::GetContext(call);
267
268 removePrivateTags = true;
269 keepPatientId = false;
270
271 Json::Value request;
272 if (call.ParseJsonRequest(request) &&
273 request.isObject())
274 {
275 Json::Value keepPart = Json::arrayValue;
276 Json::Value removalsPart = Json::arrayValue;
277 Json::Value replacementsPart = Json::objectValue;
278
279 if (request.isMember("Keep"))
280 {
281 keepPart = request["Keep"];
282 }
283
284 if (request.isMember("KeepPrivateTags"))
285 {
286 removePrivateTags = false;
287 }
288
289 if (request.isMember("Replace"))
290 {
291 replacementsPart = request["Replace"];
292 }
293
294 Removals toKeep;
295 ParseRemovals(toKeep, keepPart);
296
297 SetupAnonymization(removals, replacements);
298
299 for (Removals::iterator it = toKeep.begin(); it != toKeep.end(); ++it)
300 {
301 if (*it == DICOM_TAG_PATIENT_ID)
302 {
303 keepPatientId = true;
304 }
305
306 removals.erase(*it);
307 }
308
309 Removals additionalRemovals;
310 ParseRemovals(additionalRemovals, removalsPart);
311
312 for (Removals::iterator it = additionalRemovals.begin();
313 it != additionalRemovals.end(); ++it)
314 {
315 removals.insert(*it);
316 }
317
318 ParseReplacements(replacements, replacementsPart);
319
320 // Generate random Patient's Name if none is specified
321 if (toKeep.find(DICOM_TAG_PATIENT_NAME) == toKeep.end() &&
322 replacements.find(DICOM_TAG_PATIENT_NAME) == replacements.end())
323 {
324 replacements.insert(std::make_pair(DICOM_TAG_PATIENT_NAME, GeneratePatientName(context)));
325 }
326
327 return true;
328 }
329 else
330 {
331 return false;
332 }
333 }
334
335
336 static void AnonymizeOrModifyInstance(Removals& removals,
337 Replacements& replacements,
338 bool removePrivateTags,
339 RestApi::PostCall& call)
340 {
341 boost::mutex::scoped_lock lock(cacheMutex_);
342 ServerContext& context = OrthancRestApi::GetContext(call);
343
344 std::string id = call.GetUriComponent("id", "");
345 ParsedDicomFile& dicom = context.GetDicomFile(id);
346
347 std::auto_ptr<ParsedDicomFile> modified(dicom.Clone());
348 ReplaceInstanceInternal(*modified, removals, replacements, DicomReplaceMode_InsertIfAbsent, removePrivateTags);
349 modified->Answer(call.GetOutput());
350 }
351
352
353 static bool RetrieveMappedUid(ParsedDicomFile& dicom,
354 DicomRootLevel level,
355 Replacements& replacements,
356 UidMap& uidMap)
357 {
358 std::auto_ptr<DicomTag> tag;
359
360 switch (level)
361 {
362 case DicomRootLevel_Series:
363 tag.reset(new DicomTag(DICOM_TAG_SERIES_INSTANCE_UID));
364 break;
365
366 case DicomRootLevel_Study:
367 tag.reset(new DicomTag(DICOM_TAG_STUDY_INSTANCE_UID));
368 break;
369
370 case DicomRootLevel_Patient:
371 tag.reset(new DicomTag(DICOM_TAG_PATIENT_ID));
372 break;
373
374 default:
375 throw OrthancException(ErrorCode_InternalError);
376 }
377
378 std::string original;
379 if (!dicom.GetTagValue(original, *tag))
380 {
381 throw OrthancException(ErrorCode_InternalError);
382 }
383
384 std::string mapped;
385 bool isNew;
386
387 UidMap::const_iterator previous = uidMap.find(std::make_pair(level, original));
388 if (previous == uidMap.end())
389 {
390 mapped = FromDcmtkBridge::GenerateUniqueIdentifier(level);
391 uidMap.insert(std::make_pair(std::make_pair(level, original), mapped));
392 isNew = true;
393 }
394 else
395 {
396 mapped = previous->second;
397 isNew = false;
398 }
399
400 replacements[*tag] = mapped;
401 return isNew;
402 }
403
404
405 static void AnonymizeOrModifyResource(Removals& removals,
406 Replacements& replacements,
407 bool removePrivateTags,
408 bool keepPatientId,
409 MetadataType metadataType,
410 ChangeType changeType,
411 ResourceType resourceType,
412 RestApi::PostCall& call)
413 {
414 typedef std::list<std::string> Instances;
415
416 bool isFirst = true;
417 Json::Value result(Json::objectValue);
418
419 boost::mutex::scoped_lock lock(cacheMutex_);
420 ServerContext& context = OrthancRestApi::GetContext(call);
421
422 Instances instances;
423 std::string id = call.GetUriComponent("id", "");
424 context.GetIndex().GetChildInstances(instances, id);
425
426 if (instances.empty())
427 {
428 return;
429 }
430
431 /**
432 * Loop over all the instances of the resource.
433 **/
434
435 UidMap uidMap;
436 for (Instances::const_iterator it = instances.begin();
437 it != instances.end(); ++it)
438 {
439 LOG(INFO) << "Modifying instance " << *it;
440 ParsedDicomFile& original = context.GetDicomFile(*it);
441
442 DicomInstanceHasher originalHasher = original.GetHasher();
443
444 if (isFirst && keepPatientId)
445 {
446 std::string patientId = originalHasher.GetPatientId();
447 uidMap[std::make_pair(DicomRootLevel_Patient, patientId)] = patientId;
448 }
449
450 bool isNewSeries = RetrieveMappedUid(original, DicomRootLevel_Series, replacements, uidMap);
451 bool isNewStudy = RetrieveMappedUid(original, DicomRootLevel_Study, replacements, uidMap);
452 bool isNewPatient = RetrieveMappedUid(original, DicomRootLevel_Patient, replacements, uidMap);
453
454
455 /**
456 * Compute the resulting DICOM instance and store it into the Orthanc store.
457 **/
458
459 std::auto_ptr<ParsedDicomFile> modified(original.Clone());
460 ReplaceInstanceInternal(*modified, removals, replacements, DicomReplaceMode_InsertIfAbsent, removePrivateTags);
461
462 std::string modifiedInstance;
463 if (context.Store(modifiedInstance, modified->GetDicom()) != StoreStatus_Success)
464 {
465 LOG(ERROR) << "Error while storing a modified instance " << *it;
466 return;
467 }
468
469
470 /**
471 * Record metadata information (AnonymizedFrom/ModifiedFrom).
472 **/
473
474 DicomInstanceHasher modifiedHasher = modified->GetHasher();
475
476 if (isNewSeries)
477 {
478 context.GetIndex().SetMetadata(modifiedHasher.HashSeries(),
479 metadataType, originalHasher.HashSeries());
480 }
481
482 if (isNewStudy)
483 {
484 context.GetIndex().SetMetadata(modifiedHasher.HashStudy(),
485 metadataType, originalHasher.HashStudy());
486 }
487
488 if (isNewPatient)
489 {
490 context.GetIndex().SetMetadata(modifiedHasher.HashPatient(),
491 metadataType, originalHasher.HashPatient());
492 }
493
494 assert(*it == originalHasher.HashInstance());
495 assert(modifiedInstance == modifiedHasher.HashInstance());
496 context.GetIndex().SetMetadata(modifiedInstance, metadataType, *it);
497
498
499 /**
500 * Compute the JSON object that is returned by the REST call.
501 **/
502
503 if (isFirst)
504 {
505 std::string newId;
506
507 switch (resourceType)
508 {
509 case ResourceType_Series:
510 newId = modifiedHasher.HashSeries();
511 break;
512
513 case ResourceType_Study:
514 newId = modifiedHasher.HashStudy();
515 break;
516
517 case ResourceType_Patient:
518 newId = modifiedHasher.HashPatient();
519 break;
520
521 default:
522 throw OrthancException(ErrorCode_InternalError);
523 }
524
525 result["Type"] = EnumerationToString(resourceType);
526 result["ID"] = newId;
527 result["Path"] = GetBasePath(resourceType, newId);
528 result["PatientID"] = modifiedHasher.HashPatient();
529 isFirst = false;
530 }
531 }
532
533 call.GetOutput().AnswerJson(result);
534 }
535
536
537
538 static void ModifyInstance(RestApi::PostCall& call)
539 {
540 Removals removals;
541 Replacements replacements;
542 bool removePrivateTags;
543
544 if (ParseModifyRequest(removals, replacements, removePrivateTags, call))
545 {
546 AnonymizeOrModifyInstance(removals, replacements, removePrivateTags, call);
547 }
548 }
549
550
551 static void AnonymizeInstance(RestApi::PostCall& call)
552 {
553 Removals removals;
554 Replacements replacements;
555 bool removePrivateTags, keepPatientId;
556
557 if (ParseAnonymizationRequest(removals, replacements, removePrivateTags, keepPatientId, call))
558 {
559 // TODO Handle "keepPatientId"
560
561 // Generate random patient ID if not specified
562 if (replacements.find(DICOM_TAG_PATIENT_ID) == replacements.end())
563 {
564 replacements.insert(std::make_pair(DICOM_TAG_PATIENT_ID,
565 FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Patient)));
566 }
567
568 // Generate random study UID if not specified
569 if (replacements.find(DICOM_TAG_STUDY_INSTANCE_UID) == replacements.end())
570 {
571 replacements.insert(std::make_pair(DICOM_TAG_STUDY_INSTANCE_UID,
572 FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Study)));
573 }
574
575 // Generate random series UID if not specified
576 if (replacements.find(DICOM_TAG_SERIES_INSTANCE_UID) == replacements.end())
577 {
578 replacements.insert(std::make_pair(DICOM_TAG_SERIES_INSTANCE_UID,
579 FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Series)));
580 }
581
582 AnonymizeOrModifyInstance(removals, replacements, removePrivateTags, call);
583 }
584 }
585
586
587 static void ModifySeriesInplace(RestApi::PostCall& call)
588 {
589 Removals removals;
590 Replacements replacements;
591 bool removePrivateTags;
592
593 if (ParseModifyRequest(removals, replacements, removePrivateTags, call))
594 {
595 AnonymizeOrModifyResource(removals, replacements, removePrivateTags, true /*keepPatientId*/,
596 MetadataType_ModifiedFrom, ChangeType_ModifiedSeries,
597 ResourceType_Series, call);
598 }
599 }
600
601
602 static void AnonymizeSeriesInplace(RestApi::PostCall& call)
603 {
604 Removals removals;
605 Replacements replacements;
606 bool removePrivateTags, keepPatientId;
607
608 if (ParseAnonymizationRequest(removals, replacements, removePrivateTags, keepPatientId, call))
609 {
610 AnonymizeOrModifyResource(removals, replacements, removePrivateTags, keepPatientId,
611 MetadataType_AnonymizedFrom, ChangeType_AnonymizedSeries,
612 ResourceType_Series, call);
613 }
614 }
615
616
617 static void ModifyStudyInplace(RestApi::PostCall& call)
618 {
619 Removals removals;
620 Replacements replacements;
621 bool removePrivateTags;
622
623 if (ParseModifyRequest(removals, replacements, removePrivateTags, call))
624 {
625 AnonymizeOrModifyResource(removals, replacements, removePrivateTags, true /*keepPatientId*/,
626 MetadataType_ModifiedFrom, ChangeType_ModifiedStudy,
627 ResourceType_Study, call);
628 }
629 }
630
631
632 static void AnonymizeStudyInplace(RestApi::PostCall& call)
633 {
634 Removals removals;
635 Replacements replacements;
636 bool removePrivateTags, keepPatientId;
637
638 if (ParseAnonymizationRequest(removals, replacements, removePrivateTags, keepPatientId, call))
639 {
640 AnonymizeOrModifyResource(removals, replacements, removePrivateTags, keepPatientId,
641 MetadataType_AnonymizedFrom, ChangeType_AnonymizedStudy,
642 ResourceType_Study, call);
643 }
644 }
645
646
647 /*static void ModifyPatientInplace(RestApi::PostCall& call)
648 {
649 Removals removals;
650 Replacements replacements;
651 bool removePrivateTags;
652
653 if (ParseModifyRequest(removals, replacements, removePrivateTags, call))
654 {
655 AnonymizeOrModifyResource(false, removals, replacements, removePrivateTags,
656 MetadataType_ModifiedFrom, ChangeType_ModifiedPatient,
657 ResourceType_Patient, call);
658 }
659 }*/
660
661
662 static void AnonymizePatientInplace(RestApi::PostCall& call)
663 {
664 Removals removals;
665 Replacements replacements;
666 bool removePrivateTags, keepPatientId;
667
668 if (ParseAnonymizationRequest(removals, replacements, removePrivateTags, keepPatientId, call))
669 {
670 AnonymizeOrModifyResource(removals, replacements, removePrivateTags, keepPatientId,
671 MetadataType_AnonymizedFrom, ChangeType_AnonymizedPatient,
672 ResourceType_Patient, call);
673 }
674 }
675
676
677
678 void OrthancRestApi::RegisterAnonymizeModify()
679 {
680 Register("/instances/{id}/content/*", GetRawContent);
681
682 Register("/instances/{id}/modify", ModifyInstance);
683 Register("/series/{id}/modify", ModifySeriesInplace);
684 Register("/studies/{id}/modify", ModifyStudyInplace);
685 //Register("/patients/{id}/modify", ModifyPatientInplace);
686
687 Register("/instances/{id}/anonymize", AnonymizeInstance);
688 Register("/series/{id}/anonymize", AnonymizeSeriesInplace);
689 Register("/studies/{id}/anonymize", AnonymizeStudyInplace);
690 Register("/patients/{id}/anonymize", AnonymizePatientInplace);
691 }
692 }