File: dcmedit.cpp

package info (click to toggle)
mrtrix3 3.0.8-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 15,300 kB
  • sloc: cpp: 130,470; python: 9,603; sh: 597; makefile: 62; xml: 47
file content (144 lines) | stat: -rw-r--r-- 4,446 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/* Copyright (c) 2008-2025 the MRtrix3 contributors.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Covered Software is provided under this License on an "as is"
 * basis, without warranty of any kind, either expressed, implied, or
 * statutory, including, without limitation, warranties that the
 * Covered Software is free of defects, merchantable, fit for a
 * particular purpose or non-infringing.
 * See the Mozilla Public License v. 2.0 for more details.
 *
 * For more details, see http://www.mrtrix.org/.
 */

#include "command.h"
#include "debug.h"
#include "file/path.h"
#include "file/dicom/element.h"
#include "file/dicom/quick_scan.h"
#include "file/dicom/definitions.h"


using namespace MR;
using namespace App;

void usage ()
{
  AUTHOR = "J-Donald Tournier (jdtournier@gmail.com)";

  SYNOPSIS = "Edit DICOM file in-place";

  DESCRIPTION
  + "Note that this command simply replaces the existing "
    "values without modifying the DICOM structure in any way. Replacement text "
    "will be truncated if it is too long to fit inside the existing tag."

  + "WARNING: this command will modify existing data! It is recommended to run "
    "this command on a copy of the original data set to avoid loss of data.";


  ARGUMENTS
  + Argument ("file", "the DICOM file to be edited.").type_file_in();

  OPTIONS
  + Option ("anonymise", "remove any identifiable information, by replacing the following tags:\n"
      "- any tag with Value Representation PN will be replaced with 'anonymous'\n"
      "- tag (0010,0030) PatientBirthDate will be replaced with an empty string\n"
      "WARNING: there is no guarantee that this command will remove all identiable "
      "information, since such information may be contained in any number "
      "of private vendor-specific tags. You will need to double-check the "
      "results independently if you " "need to ensure anonymity.")

  + Option ("id", "replace all ID tags with string supplied. This consists of tags "
      "(0010, 0020) PatientID and (0010, 1000) OtherPatientIDs")
  +   Argument ("text").type_text()

  + Option ("tag", "replace specific tag.").allow_multiple()
  +   Argument ("group")
  +   Argument ("element")
  +   Argument ("newvalue");
}


class Tag { NOMEMALIGN
  public:
    Tag (uint16_t group, uint16_t element, const std::string& newvalue) :
      group (group), element (element), newvalue (newvalue) { }
    uint16_t group, element;
    std::string newvalue;
};




inline std::string hex (uint16_t value)
{
  std::ostringstream hex;
  hex << std::hex << value;
  return hex.str();
}

inline uint16_t read_hex (const std::string& m)
{
  uint16_t value;
  std::istringstream hex (m);
  hex >> std::hex >> value;
  return value;
}




void run ()
{
  vector<Tag> tags;
  vector<uint16_t> VRs;

  auto opt = get_options ("anonymise");
  if (opt.size()) {
    tags.push_back (Tag (0x0010U, 0x0030U, "")); // PatientBirthDate
    VRs.push_back (VR_PN);
  }


  opt = get_options ("tag");
  if (opt.size())
    for (size_t n = 0; n < opt.size(); ++n)
      tags.push_back (Tag (read_hex (opt[n][0]), read_hex (opt[n][1]), opt[n][2]));

  opt = get_options ("id");
  if (opt.size()) {
    std::string newid = opt[0][0];
    tags.push_back (Tag (0x0010U, 0x0020U, newid)); // PatientID
    tags.push_back (Tag (0x0010U, 0x1000U, newid)); // OtherPatientIDs
  }

  for (size_t n = 0; n < VRs.size(); ++n) {
    union __VR { uint16_t i; char c[2]; } VR;
    VR.i = VRs[n];
    INFO (std::string ("clearing entries with VR \"") + VR.c[1] + VR.c[0] + "\"");
  }
  for (size_t n = 0; n < tags.size(); ++n)
    INFO ("replacing tag (" + hex(tags[n].group) + "," + hex(tags[n].element) + ") with value \"" + tags[n].newvalue + "\"");

  File::Dicom::Element item;
  item.set (argument[0], true, true);
  while (item.read()) {
    for (size_t n = 0; n < VRs.size(); ++n) {
      if (item.VR == VRs[n]) {
        memset (item.data, 32, item.size);
        memcpy (item.data, "anonymous", std::min<int> (item.size, 9));
      }
    }
    for (size_t n = 0; n < tags.size(); ++n) {
      if (item.is (tags[n].group, tags[n].element)) {
        memset (item.data, 32, item.size);
        memcpy (item.data, tags[n].newvalue.c_str(), std::min<int> (item.size, tags[n].newvalue.size()));
      }
    }
  }
}