Mercurial > hg > orthanc
changeset 787:ac18946afa74
refactoring of anonymization/modification
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 05 May 2014 15:52:14 +0200 |
parents | b6d6b65142e8 |
children | 7ebe4bf87196 |
files | OrthancServer/DicomModification.cpp OrthancServer/DicomModification.h OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp |
diffstat | 3 files changed, 142 insertions(+), 405 deletions(-) [+] |
line wrap: on
line diff
--- a/OrthancServer/DicomModification.cpp Mon May 05 13:07:10 2014 +0200 +++ b/OrthancServer/DicomModification.cpp Mon May 05 15:52:14 2014 +0200 @@ -87,7 +87,7 @@ level_ = DicomRootLevel_Instance; } - void DicomModification::Reset(const DicomTag& tag) + void DicomModification::Keep(const DicomTag& tag) { removals_.erase(tag); replacements_.erase(tag);
--- a/OrthancServer/DicomModification.h Mon May 05 13:07:10 2014 +0200 +++ b/OrthancServer/DicomModification.h Mon May 05 15:52:14 2014 +0200 @@ -62,7 +62,7 @@ public: DicomModification(); - void Reset(const DicomTag& tag); + void Keep(const DicomTag& tag); void Remove(const DicomTag& tag);
--- a/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp Mon May 05 13:07:10 2014 +0200 +++ b/OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp Mon May 05 15:52:14 2014 +0200 @@ -32,67 +32,55 @@ #include "OrthancRestApi.h" +#include "../DicomModification.h" + #include <glog/logging.h> namespace Orthanc { // Modification of DICOM instances ------------------------------------------ - namespace + enum TagOperation { - typedef std::set<DicomTag> Removals; - typedef std::map<DicomTag, std::string> Replacements; - typedef std::map< std::pair<DicomRootLevel, std::string>, std::string> UidMap; - } - - static void ReplaceInstanceInternal(ParsedDicomFile& toModify, - const Removals& removals, - const Replacements& replacements, - bool removePrivateTags) - { - if (removePrivateTags) - { - toModify.RemovePrivateTags(); - } + TagOperation_Keep, + TagOperation_Remove + }; - for (Removals::const_iterator it = removals.begin(); - it != removals.end(); ++it) - { - toModify.Remove(*it); - } - - for (Replacements::const_iterator it = replacements.begin(); - it != replacements.end(); ++it) - { - toModify.Replace(it->first, it->second, DicomReplaceMode_InsertIfAbsent); - } - - // A new SOP instance UID is automatically generated - std::string instanceUid = FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Instance); - toModify.Replace(DICOM_TAG_SOP_INSTANCE_UID, instanceUid, DicomReplaceMode_InsertIfAbsent); - } - - - static void ParseRemovals(Removals& target, - const Json::Value& removals) + static void ParseListOfTags(DicomModification& target, + const Json::Value& query, + TagOperation operation) { - if (!removals.isArray()) + if (!query.isArray()) { throw OrthancException(ErrorCode_BadRequest); } - for (Json::Value::ArrayIndex i = 0; i < removals.size(); i++) + for (Json::Value::ArrayIndex i = 0; i < query.size(); i++) { - std::string name = removals[i].asString(); + std::string name = query[i].asString(); + DicomTag tag = FromDcmtkBridge::ParseTag(name); - target.insert(tag); + + switch (operation) + { + case TagOperation_Keep: + target.Keep(tag); + VLOG(1) << "Keep: " << name << " " << tag << std::endl; + break; - VLOG(1) << "Removal: " << name << " " << tag << std::endl; + case TagOperation_Remove: + target.Remove(tag); + VLOG(1) << "Remove: " << name << " " << tag << std::endl; + break; + + default: + throw OrthancException(ErrorCode_InternalError); + } } } - static void ParseReplacements(Replacements& target, + static void ParseReplacements(DicomModification& target, const Json::Value& replacements) { if (!replacements.isObject()) @@ -107,9 +95,9 @@ std::string value = replacements[name].asString(); DicomTag tag = FromDcmtkBridge::ParseTag(name); - target[tag] = value; + target.Replace(tag, value); - VLOG(1) << "Replacement: " << name << " " << tag << " == " << value << std::endl; + VLOG(1) << "Replace: " << name << " " << tag << " == " << value << std::endl; } } @@ -121,186 +109,27 @@ } - static void SetupAnonymization(Removals& removals, - Replacements& replacements) - { - // This is Table E.1-1 from PS 3.15-2008 - DICOM Part 15: Security and System Management Profiles - removals.insert(DicomTag(0x0008, 0x0014)); // Instance Creator UID - //removals.insert(DicomTag(0x0008, 0x0018)); // SOP Instance UID => set by ReplaceInstanceInternal() - removals.insert(DicomTag(0x0008, 0x0050)); // Accession Number - removals.insert(DicomTag(0x0008, 0x0080)); // Institution Name - removals.insert(DicomTag(0x0008, 0x0081)); // Institution Address - 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, 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, 0x1050)); // Performing Physicians' Name - removals.insert(DicomTag(0x0008, 0x1060)); // Name of Physician(s) Reading Study - removals.insert(DicomTag(0x0008, 0x1070)); // Operators' Name - removals.insert(DicomTag(0x0008, 0x1080)); // Admitting Diagnoses Description - removals.insert(DicomTag(0x0008, 0x1155)); // Referenced SOP Instance UID - removals.insert(DicomTag(0x0008, 0x2111)); // Derivation Description - removals.insert(DicomTag(0x0010, 0x0010)); // Patient's Name - //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 - removals.insert(DicomTag(0x0010, 0x0040)); // Patient's Sex - removals.insert(DicomTag(0x0010, 0x1000)); // Other Patient Ids - removals.insert(DicomTag(0x0010, 0x1001)); // Other Patient Names - 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, 0x1090)); // Medical Record Locator - removals.insert(DicomTag(0x0010, 0x2160)); // Ethnic Group - removals.insert(DicomTag(0x0010, 0x2180)); // Occupation - removals.insert(DicomTag(0x0010, 0x21b0)); // Additional Patient's History - removals.insert(DicomTag(0x0010, 0x4000)); // Patient Comments - removals.insert(DicomTag(0x0018, 0x1000)); // Device Serial Number - removals.insert(DicomTag(0x0018, 0x1030)); // Protocol Name - //removals.insert(DicomTag(0x0020, 0x000d)); // Study Instance UID => cf. below (*) - //removals.insert(DicomTag(0x0020, 0x000e)); // Series Instance UID => cf. below (*) - 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, 0x4000)); // Image Comments - removals.insert(DicomTag(0x0040, 0x0275)); // Request Attributes Sequence - removals.insert(DicomTag(0x0040, 0xa124)); // UID - removals.insert(DicomTag(0x0040, 0xa730)); // Content Sequence - removals.insert(DicomTag(0x0088, 0x0140)); // Storage Media File-set UID - removals.insert(DicomTag(0x3006, 0x0024)); // Referenced Frame of Reference UID - removals.insert(DicomTag(0x3006, 0x00c2)); // Related Frame of Reference UID - - /** - * (*) Patient ID, Study Instance UID and Series Instance UID - * are modified by "AnonymizeInstance()" if anonymizing a single - * instance, or by "RetrieveMappedUid()" if anonymizing a - * patient/study/series. - **/ - - - // Some more removals (from the experience of DICOM files at the CHU of Liege) - removals.insert(DicomTag(0x0010, 0x1040)); // Patient's Address - removals.insert(DicomTag(0x0032, 0x1032)); // Requesting Physician - removals.insert(DicomTag(0x0010, 0x2154)); // PatientTelephoneNumbers - removals.insert(DicomTag(0x0010, 0x2000)); // Medical Alerts - - // Set the DeidentificationMethod tag - replacements.insert(std::make_pair(DicomTag(0x0012, 0x0063), "Orthanc " ORTHANC_VERSION " - PS 3.15-2008 Table E.1-1")); - - // Set the PatientIdentityRemoved tag - replacements.insert(std::make_pair(DicomTag(0x0012, 0x0062), "YES")); - } - - - static bool ParseModifyRequest(Removals& removals, - Replacements& replacements, - bool& removePrivateTags, + static bool ParseModifyRequest(DicomModification& target, const RestApi::PostCall& call) { - removePrivateTags = false; + // curl http://localhost:8042/series/95a6e2bf-9296e2cc-bf614e2f-22b391ee-16e010e0/modify -X POST -d '{"Replace":{"InstitutionName":"My own clinic"}}' + Json::Value request; - if (call.ParseJsonRequest(request) && - request.isObject()) + if (call.ParseJsonRequest(request) && request.isObject()) { - Json::Value removalsPart = Json::arrayValue; - Json::Value replacementsPart = Json::objectValue; + if (request.isMember("RemovePrivateTags")) + { + target.SetRemovePrivateTags(true); + } if (request.isMember("Remove")) { - removalsPart = request["Remove"]; + ParseListOfTags(target, request["Remove"], TagOperation_Remove); } if (request.isMember("Replace")) { - replacementsPart = request["Replace"]; - } - - if (request.isMember("RemovePrivateTags")) - { - removePrivateTags = true; - } - - ParseRemovals(removals, removalsPart); - ParseReplacements(replacements, replacementsPart); - - return true; - } - else - { - return false; - } - } - - - static bool ParseAnonymizationRequest(Removals& removals, - Replacements& replacements, - bool& removePrivateTags, - bool& keepPatientId, - RestApi::PostCall& call) - { - ServerContext& context = OrthancRestApi::GetContext(call); - - removePrivateTags = true; - keepPatientId = false; - - Json::Value request; - if (call.ParseJsonRequest(request) && - request.isObject()) - { - Json::Value keepPart = Json::arrayValue; - Json::Value removalsPart = Json::arrayValue; - Json::Value replacementsPart = Json::objectValue; - - if (request.isMember("Keep")) - { - keepPart = request["Keep"]; - } - - if (request.isMember("KeepPrivateTags")) - { - removePrivateTags = false; - } - - if (request.isMember("Replace")) - { - replacementsPart = request["Replace"]; - } - - Removals toKeep; - ParseRemovals(toKeep, keepPart); - - SetupAnonymization(removals, replacements); - - for (Removals::iterator it = toKeep.begin(); it != toKeep.end(); ++it) - { - if (*it == DICOM_TAG_PATIENT_ID) - { - keepPatientId = true; - } - - removals.erase(*it); - } - - Removals additionalRemovals; - ParseRemovals(additionalRemovals, removalsPart); - - for (Removals::iterator it = additionalRemovals.begin(); - it != additionalRemovals.end(); ++it) - { - removals.insert(*it); - } - - ParseReplacements(replacements, replacementsPart); - - // Generate random Patient's Name if none is specified - if (toKeep.find(DICOM_TAG_PATIENT_NAME) == toKeep.end() && - replacements.find(DICOM_TAG_PATIENT_NAME) == replacements.end()) - { - replacements.insert(std::make_pair(DICOM_TAG_PATIENT_NAME, GeneratePatientName(context))); + ParseReplacements(target, request["Replace"]); } return true; @@ -312,9 +141,54 @@ } - static void AnonymizeOrModifyInstance(Removals& removals, - Replacements& replacements, - bool removePrivateTags, + static bool ParseAnonymizationRequest(DicomModification& target, + RestApi::PostCall& call) + { + // 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.GetReplacement(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.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))); + } + + return true; + } + else + { + return false; + } + } + + + static void AnonymizeOrModifyInstance(DicomModification& modification, RestApi::PostCall& call) { std::string id = call.GetUriComponent("id", ""); @@ -322,79 +196,23 @@ ServerContext::DicomCacheLocker locker(OrthancRestApi::GetContext(call), id); std::auto_ptr<ParsedDicomFile> modified(locker.GetDicom().Clone()); - ReplaceInstanceInternal(*modified, removals, replacements, removePrivateTags); + modification.Apply(*modified); modified->Answer(call.GetOutput()); } - static bool RetrieveMappedUid(ParsedDicomFile& dicom, - DicomRootLevel level, - Replacements& replacements, - UidMap& uidMap) - { - std::auto_ptr<DicomTag> tag; - - switch (level) - { - case DicomRootLevel_Series: - tag.reset(new DicomTag(DICOM_TAG_SERIES_INSTANCE_UID)); - break; - - case DicomRootLevel_Study: - tag.reset(new DicomTag(DICOM_TAG_STUDY_INSTANCE_UID)); - break; - - case DicomRootLevel_Patient: - tag.reset(new DicomTag(DICOM_TAG_PATIENT_ID)); - break; - - default: - throw OrthancException(ErrorCode_InternalError); - } - - std::string original; - if (!dicom.GetTagValue(original, *tag)) - { - throw OrthancException(ErrorCode_InternalError); - } - - std::string mapped; - bool isNew; - - UidMap::const_iterator previous = uidMap.find(std::make_pair(level, original)); - if (previous == uidMap.end()) - { - mapped = FromDcmtkBridge::GenerateUniqueIdentifier(level); - uidMap.insert(std::make_pair(std::make_pair(level, original), mapped)); - isNew = true; - } - else - { - mapped = previous->second; - isNew = false; - } - - replacements[*tag] = mapped; - return isNew; - } - - - static void AnonymizeOrModifyResource(Removals& removals, - Replacements& replacements, - bool removePrivateTags, - bool keepPatientId, + static void AnonymizeOrModifyResource(DicomModification& modification, MetadataType metadataType, ChangeType changeType, ResourceType resourceType, RestApi::PostCall& call) { - typedef std::list<std::string> Instances; - bool isFirst = true; Json::Value result(Json::objectValue); ServerContext& context = OrthancRestApi::GetContext(call); + typedef std::list<std::string> Instances; Instances instances; std::string id = call.GetUriComponent("id", ""); context.GetIndex().GetChildInstances(instances, id); @@ -404,11 +222,11 @@ return; } + /** * Loop over all the instances of the resource. **/ - UidMap uidMap; for (Instances::const_iterator it = instances.begin(); it != instances.end(); ++it) { @@ -427,26 +245,15 @@ } ParsedDicomFile& original = locker->GetDicom(); - DicomInstanceHasher originalHasher = original.GetHasher(); - if (isFirst && keepPatientId) - { - std::string patientId = originalHasher.GetPatientId(); - uidMap[std::make_pair(DicomRootLevel_Patient, patientId)] = patientId; - } - - bool isNewSeries = RetrieveMappedUid(original, DicomRootLevel_Series, replacements, uidMap); - bool isNewStudy = RetrieveMappedUid(original, DicomRootLevel_Study, replacements, uidMap); - bool isNewPatient = RetrieveMappedUid(original, DicomRootLevel_Patient, replacements, uidMap); - /** * Compute the resulting DICOM instance and store it into the Orthanc store. **/ std::auto_ptr<ParsedDicomFile> modified(original.Clone()); - ReplaceInstanceInternal(*modified, removals, replacements, removePrivateTags); + modification.Apply(*modified); std::string modifiedInstance; if (context.Store(modifiedInstance, modified->GetDicom()) != StoreStatus_Success) @@ -462,19 +269,19 @@ DicomInstanceHasher modifiedHasher = modified->GetHasher(); - if (isNewSeries) + if (originalHasher.HashSeries() != modifiedHasher.HashSeries()) { context.GetIndex().SetMetadata(modifiedHasher.HashSeries(), metadataType, originalHasher.HashSeries()); } - if (isNewStudy) + if (originalHasher.HashStudy() != modifiedHasher.HashStudy()) { context.GetIndex().SetMetadata(modifiedHasher.HashStudy(), metadataType, originalHasher.HashStudy()); } - if (isNewPatient) + if (originalHasher.HashPatient() != modifiedHasher.HashPatient()) { context.GetIndex().SetMetadata(modifiedHasher.HashPatient(), metadataType, originalHasher.HashPatient()); @@ -526,154 +333,84 @@ static void ModifyInstance(RestApi::PostCall& call) { - Removals removals; - Replacements replacements; - bool removePrivateTags; + DicomModification modification; + + // TODO : modification.SetLevel(DicomRootLevel_Series); ????? - if (ParseModifyRequest(removals, replacements, removePrivateTags, call)) + if (ParseModifyRequest(modification, call)) { - AnonymizeOrModifyInstance(removals, replacements, removePrivateTags, call); + AnonymizeOrModifyInstance(modification, call); } } static void AnonymizeInstance(RestApi::PostCall& call) { - Removals removals; - Replacements replacements; - bool removePrivateTags, keepPatientId; - - if (ParseAnonymizationRequest(removals, replacements, removePrivateTags, keepPatientId, call)) - { - // TODO Handle "keepPatientId" - - // Generate random patient ID if not specified - if (replacements.find(DICOM_TAG_PATIENT_ID) == replacements.end()) - { - replacements.insert(std::make_pair(DICOM_TAG_PATIENT_ID, - FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Patient))); - } - - // Generate random study UID if not specified - if (replacements.find(DICOM_TAG_STUDY_INSTANCE_UID) == replacements.end()) - { - replacements.insert(std::make_pair(DICOM_TAG_STUDY_INSTANCE_UID, - FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Study))); - } + DicomModification modification; - // Generate random series UID if not specified - if (replacements.find(DICOM_TAG_SERIES_INSTANCE_UID) == replacements.end()) - { - replacements.insert(std::make_pair(DICOM_TAG_SERIES_INSTANCE_UID, - FromDcmtkBridge::GenerateUniqueIdentifier(DicomRootLevel_Series))); - } - - AnonymizeOrModifyInstance(removals, replacements, removePrivateTags, call); - } - } - - - static void ModifySeriesInplace(RestApi::PostCall& call) - { - Removals removals; - Replacements replacements; - bool removePrivateTags; - - if (ParseModifyRequest(removals, replacements, removePrivateTags, call)) + if (ParseAnonymizationRequest(modification, call)) { - AnonymizeOrModifyResource(removals, replacements, removePrivateTags, true /*keepPatientId*/, - MetadataType_ModifiedFrom, ChangeType_ModifiedSeries, - ResourceType_Series, call); + AnonymizeOrModifyInstance(modification, call); } } - static void AnonymizeSeriesInplace(RestApi::PostCall& call) + template <enum ChangeType changeType, + enum ResourceType resourceType> + static void ModifyResource(RestApi::PostCall& call) { - Removals removals; - Replacements replacements; - bool removePrivateTags, keepPatientId; + DicomModification modification; - if (ParseAnonymizationRequest(removals, replacements, removePrivateTags, keepPatientId, call)) + switch (resourceType) { - AnonymizeOrModifyResource(removals, replacements, removePrivateTags, keepPatientId, - MetadataType_AnonymizedFrom, ChangeType_AnonymizedSeries, - ResourceType_Series, call); + case ResourceType_Series: + modification.SetLevel(DicomRootLevel_Series); + break; + + case ResourceType_Study: + modification.SetLevel(DicomRootLevel_Study); + break; + + case ResourceType_Patient: + modification.SetLevel(DicomRootLevel_Patient); + break; + + default: + throw OrthancException(ErrorCode_InternalError); } - } - - static void ModifyStudyInplace(RestApi::PostCall& call) - { - Removals removals; - Replacements replacements; - bool removePrivateTags; - - if (ParseModifyRequest(removals, replacements, removePrivateTags, call)) + if (ParseModifyRequest(modification, call)) { - AnonymizeOrModifyResource(removals, replacements, removePrivateTags, true /*keepPatientId*/, - MetadataType_ModifiedFrom, ChangeType_ModifiedStudy, - ResourceType_Study, call); + AnonymizeOrModifyResource(modification, MetadataType_ModifiedFrom, + changeType, resourceType, call); } } - static void AnonymizeStudyInplace(RestApi::PostCall& call) + template <enum ChangeType changeType, + enum ResourceType resourceType> + static void AnonymizeResource(RestApi::PostCall& call) { - Removals removals; - Replacements replacements; - bool removePrivateTags, keepPatientId; + DicomModification modification; - if (ParseAnonymizationRequest(removals, replacements, removePrivateTags, keepPatientId, call)) + if (ParseAnonymizationRequest(modification, call)) { - AnonymizeOrModifyResource(removals, replacements, removePrivateTags, keepPatientId, - MetadataType_AnonymizedFrom, ChangeType_AnonymizedStudy, - ResourceType_Study, call); + AnonymizeOrModifyResource(modification, MetadataType_AnonymizedFrom, + changeType, resourceType, call); } } - /*static void ModifyPatientInplace(RestApi::PostCall& call) - { - Removals removals; - Replacements replacements; - bool removePrivateTags; - - if (ParseModifyRequest(removals, replacements, removePrivateTags, call)) - { - AnonymizeOrModifyResource(false, removals, replacements, removePrivateTags, - MetadataType_ModifiedFrom, ChangeType_ModifiedPatient, - ResourceType_Patient, call); - } - }*/ - - - static void AnonymizePatientInplace(RestApi::PostCall& call) - { - Removals removals; - Replacements replacements; - bool removePrivateTags, keepPatientId; - - if (ParseAnonymizationRequest(removals, replacements, removePrivateTags, keepPatientId, call)) - { - AnonymizeOrModifyResource(removals, replacements, removePrivateTags, keepPatientId, - MetadataType_AnonymizedFrom, ChangeType_AnonymizedPatient, - ResourceType_Patient, call); - } - } - - - void OrthancRestApi::RegisterAnonymizeModify() { Register("/instances/{id}/modify", ModifyInstance); - Register("/series/{id}/modify", ModifySeriesInplace); - Register("/studies/{id}/modify", ModifyStudyInplace); - //Register("/patients/{id}/modify", ModifyPatientInplace); + Register("/series/{id}/modify", ModifyResource<ChangeType_ModifiedSeries, ResourceType_Series>); + Register("/studies/{id}/modify", ModifyResource<ChangeType_ModifiedStudy, ResourceType_Study>); + //Register("/patients/{id}/modify", ModifyResource<ChangeType_ModifiedPatient, ResourceType_Patient>); Register("/instances/{id}/anonymize", AnonymizeInstance); - Register("/series/{id}/anonymize", AnonymizeSeriesInplace); - Register("/studies/{id}/anonymize", AnonymizeStudyInplace); - Register("/patients/{id}/anonymize", AnonymizePatientInplace); + Register("/series/{id}/anonymize", AnonymizeResource<ChangeType_ModifiedSeries, ResourceType_Series>); + Register("/studies/{id}/anonymize", AnonymizeResource<ChangeType_ModifiedStudy, ResourceType_Study>); + Register("/patients/{id}/anonymize", AnonymizeResource<ChangeType_ModifiedPatient, ResourceType_Patient>); } }