DICOM Filename Structure and Customization

Hi,

I have a weird use case for Orthanc and I was hoping to find out if it will work for me.

I have a working Orthanc install where xrays are now being automatically sent to it over my network from my xray capture PC after accepting each scan.

I don't want to use any web viewer or management features from Orthanc, I just wanted to use it to get the dicom files onto the filesystem where they can be manipulated freely. Mainly chose Orthanc because it was so easy to install and configure.

I have a script created using a program called AdvancedRenamer that will move and rename DICOM files based on their metadata (ie patient ID, etc) which I need to allow some automatic imports into other network software. My plan is to point it at the Orthanc storage directory and let it manipulate the files from there. I'm aware moving the dicom files straight from the Orthanc storage directory will probably make the software unhappy, but again I don't need an Orthanc features other than get the files to the filesystem from an automatic dicom send.

The problem I've run into is that while the script can filter which files it targets based on filename mask and regex, Orthanc stores other small files mixed in with the larger DICOM files (even after sqlite index directory moved), and uses filenames that are not only random strings (as far as I can tell), but don't even include a DCM extension.

Any of the following would solve my problem if at all possible:
1. Isolate storage directories so the small (11KB) files are stored in a separate folder structure from the large dicom files.
2. Give DICOM files a DCM extension so that I can filter them out.
3. Some other way to differentiate by file name, ie all DICOM files starting with a certain string, etc.

I knkw this is an unusual use case but any help would be appreciated in case I've missed a possible solution in the config file or anything else.

One thing I forgot to mention. Another solution (that would more likely respect the way the software is intended to work) would be the ability to automatically export DCM files to a given directory automatically after they've been imported as storage space is not an issue. Though again would need to not have all the extra little files present like live in the storage folder.

Hello,

I think the following Python sample that is part of the official Orthanc distribution does exactly what you want:
https://bitbucket.org/sjodogne/orthanc/src/default/Resources/Samples/Python/AutoClassify.py

The script will copy any DICOM file Orthanc receives to a given directory, then remove the file from Orthanc.

Sébastien-

Or you can use the easier plug and play lua script

https://bitbucket.org/sjodogne/orthanc/src/b97aa579e85bc57652d5ac8a2e45ce77a1e203d7/Resources/Samples/Lua/WriteToDisk.lua?at=default&fileviewer=file-view-default

This looks really interesting. I glossed over the lua stuff earlier when I was looking at documentation because I didn’t realise there were already samples written that do what I’m asking for. If I understand it correctly I may be able to totally replace the other program in my workflow and use this script to do the specific naming scheme I require and save the files to the required location. So I would save the lua script somewhere, modify as needed, and simply reference that file in the orthanc config?

In this instance what I’m talking about doing is the following:

  1. Xray is captured and sent to the orthanc server (this is already working)
  2. When files are imported successfully, Lua script automatically executes
  3. Files are exported to a directory (e.g. C:\OrthancExport) with the naming scheme “H[PatientID]-[SeriesNumber].DCM” (where the square brackets are obviously replaced with metadata) - the “-[SeriesNumber]” is really just to avoid file conflicts if that part is more difficult.

If this kind of workflow is possible within the context of the scripting, is anyone able to help point out what I need to modify? I don’t know Lua (or Python for that matter) and from my initial observations I’m having trouble understanding the syntax.

Try this modified example;
I modified it to work on windows and export the files in structured folders

TARGET = 'C:\\OrthancExport'

function ToAscii(s)
   -- http://www.lua.org/manual/5.1/manual.html#pdf-string.gsub
   return s
end

function OnStableSeries(seriesId, tags, metadata)
   print('This series is now stable, writing its instances on the disk: ' .. seriesId)

   local instances = ParseJson(RestApiGet('/series/' .. seriesId)) ['Instances']
   local patient = ParseJson(RestApiGet('/series/' .. seriesId .. '/patient')) ['MainDicomTags']
   local study = ParseJson(RestApiGet('/series/' .. seriesId .. '/study')) ['MainDicomTags']
   local series = ParseJson(RestApiGet('/series/' .. seriesId)) ['MainDicomTags']

   for i, instance in pairs(instances) do
      local path = ToAscii(TARGET .. '\\' ..
                              patient['PatientID'] .. ' - ' .. patient['PatientName'] .. '\\' ..
                              study['StudyDate'] .. ' - ' .. study['StudyDescription'] .. '\\' ..
                              series['SeriesDescription'])

      -- Retrieve the DICOM file from Orthanc
      local dicom = RestApiGet('/instances/' .. instance .. '/file')

      -- Create the subdirectory (CAUTION: For Linux demo only, this is insecure!)
      -- http://stackoverflow.com/a/16029744/881731
      os.execute('mkdir "' .. path .. '"')

      -- Write to the file
      local target = assert(io.open(path .. '\\' .. instance .. '.dcm', 'wb'))
      target:write(dicom)
      target:close()
   end
end

Sorry for the delay, this is very helpful. I saved your modified script as WriteToDisk.lua and put it in C:\Orthanc, specifying the file name in the config file and it seems to be working well. Now when a set of dicom files are added they go to a folder structure. Looking at the script I see lines that are controlling what the folders are, but I’m wondering if using this script I can change what the filename itself is, based on some of the same information, such as patient ID.

Specifically in my instance I’d be trying to export everything to a flat folder with the following syntax:

H[PatientID]-[SeriesNumber].DCM

This would allow me to specify exports to the watched auto-import folder of the other software I’m trying to integrate with. If it isn’t possible to modify filenames and use a flat folder (instead of the hierarchy) in this way I can still make my workflow possible by using a script that moves/renames files based on dicom metadata, but this would cut out another software layer if posible.

Thanks.

Have you tried adapting the script?

Replace the following line with whatever you want:

local path = ToAscii(TARGET … ‘\’ …
patient[‘PatientID’] … ’ - ’ … patient[‘PatientName’] … ‘\’ …
study[‘StudyDate’] … ’ - ’ … study[‘StudyDescription’] … ‘\’ …
series[‘SeriesDescription’])

If you want a flat structure, remove the “\” and remove the system call to mkdir.

Unfortunately after removing the \ from the local path line and removing the mkdir line the exports aren’t working anymore, should I delete the following 3 lines (patient, study, series)? Also would the mkdir line make a difference considering I’m running on Windows?

Is it possible with this script to change the filename with the dicom metadata such as PatientID, instead of just the folders?

Please clarify your syntax:

H[PatientID]-[SeriesNumber].DCM

H ?

where is the instance number?

Essentially I have a program on my network (veterinary database software) that can automatically import DICOM files, but it does so the same way it does so for other files(e.g. PDF), that being through the file-name being in this format (begin with ‘H’, followed with patient ID) and being put in a specific watched folder (which is why I want flat folder export), it doesn’t act as a DICOM server/client.

H[PatientID]-[SeriesNumber].DCM

H (as in the letter H, short for history in this case)
[PatientID] (replaced with the patient id number from the dicom metadata)

  • (to seperate the two numbers)
    [SeriesNumber] (not required by the software, but in instances where a few xrays are taken of the same patient, need something to avoid filename conflicts in the minute or so before the software takes the files and moves them elsewhere, taking the series number from the dicom metadata makes sense for this because each scan will be given an incremented number)

I’m assuming the instance number is the unique identifier Orthanc has in the existing filenames and I don’t want that at all if possible.

Thanks for you help.

Here your code with the naming scheme "H[PatientID]-[SeriesNumber].[Inastance Number].dcm

`
TARGET = ‘C:\OrthancExport’

function ToAscii(s)
http://www.lua.org/manual/5.1/manual.html#pdf-string.gsub
return s
end

function OnStableSeries(seriesId, tags, metadata)
print('This series is now stable, writing its instances on the disk: ’ … seriesId)

local instances = ParseJson(RestApiGet(‘/series/’ … seriesId)) [‘Instances’]
local patient = ParseJson(RestApiGet(‘/series/’ … seriesId … ‘/patient’)) [‘MainDicomTags’]
local study = ParseJson(RestApiGet(‘/series/’ … seriesId … ‘/study’)) [‘MainDicomTags’]
local series = ParseJson(RestApiGet(‘/series/’ … seriesId)) [‘MainDicomTags’]

for i, instance in pairs(instances) do
local path = ToAscii(TARGET)

– Retrieve the DICOM file from Orthanc
local dicom = RestApiGet(‘/instances/’ … instance … ‘/file’)

– Write to the file
local target = assert(io.open(path … ‘\’ … ‘H’ … patient[‘PatientID’] … ‘-’ … series[‘SeriesNumber’] … ‘-’ … i … ‘.dcm’, ‘wb’))
target:write(dicom)
target:close()
end
end
`

3.lua (1.02 KB)

Thanks for the code, it’s working great.

I did remove the part of the “write to the file” section that appends the hyphen and instance number to it, and it works well still, do you think there’s an issue with me doing that? Since within a minute of being written to the folder, the files are all going to be picked up and moved elsewhere by other software on the network I don’t expect conflicts from future scans of the same patient.

Thanks for the help everyone, I think it’s time for me to set it up in my testing environment to see how stable it is with more frequent scans.