Bug 184

Summary: DICOM private tag modifications are sometimes not being processed
Product: Orthanc Reporter: Nick Farrell <nick.farrell>
Component: Orthanc CoreAssignee: Sébastien Jodogne <s.jodogne>
Status: RESOLVED INVALID    
Severity: normal CC: am
Priority: ---    
Version: unspecified   
Hardware: PC   
OS: Linux   
URL: https://groups.google.com/forum/#!msg/orthanc-users/kdLXS_VCeQk/rkXCA6TlBgAJ
See Also: https://bugs.orthanc-server.com/show_bug.cgi?id=168
Attachments: dicom and scripts to reproduce
from storescu
from orthanc

Description Nick Farrell 2020-07-08 14:52:11 CEST
Created attachment 114 [details]
dicom and scripts to reproduce

The attachment, when executed using the 'start' script, creates two orthanc instances. If the included dicom file is uploaded into the 'mock-mim' instance (port 8032), and then transferred to the second 'anonymiser' one using the web UI, the lua script fails to write the modified dicom instance, resulting in corrupted tags - you will see the following on stdout:

anonymiser_1  |    "6161,1043" : {
anonymiser_1  |       "Name" : "Unknown Tag & Data",
anonymiser_1  |       "Type" : "Null",
anonymiser_1  |       "Value" : null
anonymiser_1  |    },
anonymiser_1  |    "6161,1044" : {
anonymiser_1  |       "Name" : "Unknown Tag & Data",
anonymiser_1  |       "Type" : "Null",
anonymiser_1  |       "Value" : null
anonymiser_1  |    },
anonymiser_1  |    "6161,1045" : {
anonymiser_1  |       "Name" : "Unknown Tag & Data",
anonymiser_1  |       "Type" : "Null",
anonymiser_1  |       "Value" : null
anonymiser_1  |    },

However, if the 'send' shell command is run, which simply uploads the dicom file using dcmsend, the tags are correctly modified (but the custom names are not displayed, despite orthanc.json knowing these names)


anonymiser_1  |    "6161,1043" : {
anonymiser_1  |       "Name" : "Unknown Tag & Data",
anonymiser_1  |       "Type" : "String",
anonymiser_1  |       "Value" : "172.28.0.1"
anonymiser_1  |    },
anonymiser_1  |    "6161,1044" : {
anonymiser_1  |       "Name" : "Unknown Tag & Data",
anonymiser_1  |       "Type" : "String",
anonymiser_1  |       "Value" : "DCMSEND"
anonymiser_1  |    },
anonymiser_1  |    "6161,1045" : {
anonymiser_1  |       "Name" : "Unknown Tag & Data",
anonymiser_1  |       "Type" : "String",
anonymiser_1  |       "Value" : "ANY-SCP"
anonymiser_1  |    },


I suspect, but cannot yet confirm, that this is related to an internal error message I can sometimes elicit.
Comment 1 Alain Mazy 2020-07-16 12:51:21 CEST
Created attachment 115 [details]
from storescu
Comment 2 Alain Mazy 2020-07-16 12:51:45 CEST
Created attachment 116 [details]
from orthanc
Comment 3 Alain Mazy 2020-07-16 12:56:31 CEST
After first analysis, the file from Orthanc is in implicit transfer syntax while the one from dcmsend/storescu is in explicit transfer syntax.

Orthanc JSON file handles the private tags correctly only with the explicit file.

Also note that Orthanc does not seem to add the "PrivateCreator" tag correctly at the beginning of the private group.

$ dcmdump storescu.dcm:
...
(0028,1054) LO [HU]                                     #   2, 1 RescaleType
... missing PrivateCreator tag here ...
(6161,1043) AE [127.22.0.1]                             #  10, 1 Unknown Tag & Data
(6161,1044) AE [STORESCU]                               #   8, 1 Unknown Tag & Data
(6161,1045) AE [ANY-SCP]                                #   8, 1 Unknown Tag & Data
Comment 4 Sébastien Jodogne 2020-08-05 18:47:02 CEST
Link to the original discussion:
https://groups.google.com/forum/#!msg/orthanc-users/kdLXS_VCeQk/rkXCA6TlBgAJ
Comment 5 Sébastien Jodogne 2020-08-06 13:09:30 CEST
Here is a much more direct way to reproduce, without having two instances of Orthanc and without using Docker.

(1) Take the files "lua/on_stored_instance.lua" and "3c5c2db6-b668-42bb-ab83-24eac780d805.dcm" out of the "reproduce.tar.gz" archive provided by Nick.

(2) Start Orthanc with the following adapted configuration file:

$ cat <<EOF > anonymizer.json
{
  "LuaScripts" : [ "on_stored_instance.lua" ],
  "OverwriteInstances" : true,
  "Dictionary": {
    "6161,1043" : [ "AE", "RemoteIp", 1, 1, "GenesisCare" ],
    "6161,1044" : [ "AE", "RemoteAet", 1, 1, "GenesisCare" ],
    "6161,1045" : [ "AE", "CalledAet", 1, 1, "GenesisCare" ],
    "6161,1046" : [ "LO", "Username", 1, 1, "GenesisCare" ]
  }
}
EOF
$ ./Orthanc anonymizer.json

(3) Use storescu from DCMTK to upload and anonymize the sample DICOM file, as follows:

$ storescu -xe localhost 4242 3c5c2db6-b668-42bb-ab83-24eac780d805.dcm 
$ storescu -xi localhost 4242 3c5c2db6-b668-42bb-ab83-24eac780d805.dcm

(4) Observations vs. expectations:

In the first flavor, Orthanc receives a file encoded using the "Little Endian Explicit" transfer syntax => the content of the tags is displayed.

In the second flavor, Orthanc receives a file encoded using the "Little Endian Implicit" transfer syntax => the tags are reported as "null".

In both flavors, the name of the tags are reported as "Unknown Tag & Data".


Now that I'm able to reproduce in a developer-friendly environment, I'll have a deeper look at the report.
Comment 6 Sébastien Jodogne 2020-08-06 13:24:37 CEST
It turns out that the problem is not in Orthanc, but in your Lua script. Indeed, the tag "6161,0010" is missing, but it must be present as it is used to determine the private creator for your tags.

Here is a fixed Orthanc configuration:

{
  "LuaScripts" : [ "on_stored_instance.lua" ],
  "OverwriteInstances" : true,
  "Dictionary": {
    "6161,0010" : [ "LO", "GenesisCare", 1, 1, "GenesisCare" ],  // <== NEW
    "6161,1043" : [ "AE", "RemoteIp", 1, 1, "GenesisCare" ],
    "6161,1044" : [ "AE", "RemoteAet", 1, 1, "GenesisCare" ],
    "6161,1045" : [ "AE", "CalledAet", 1, 1, "GenesisCare" ],
    "6161,1046" : [ "LO", "Username", 1, 1, "GenesisCare" ]
  }
}

Here is a fixed Lua script (excerpt):

      -- The tags to be replaced
      local replace = {}
      replace['6161,0010'] = 'GenesisCare'  -- <== NEW
      replace['CalledAet'] = trim(origin["CalledAet"])
      replace['RemoteAet'] = trim(origin["RemoteAet"])
      replace['RemoteIp'] = trim(origin["RemoteIp"])

And here is the result (both "storescu -xe" and "storescu -xi" lead to the same log):

   "6161,0010" : {
      "Name" : "PrivateCreator",
      "Type" : "String",
      "Value" : "GenesisCare"
   },
   "6161,1043" : {
      "Name" : "RemoteIp",
      "PrivateCreator" : "GenesisCare",
      "Type" : "String",
      "Value" : "127.0.0.1"
   },
   "6161,1044" : {
      "Name" : "RemoteAet",
      "PrivateCreator" : "GenesisCare",
      "Type" : "String",
      "Value" : "STORESCU"
   },
   "6161,1045" : {
      "Name" : "CalledAet",
      "PrivateCreator" : "GenesisCare",
      "Type" : "String",
      "Value" : "ANY-SCP"
   },