Charles A. Pelizzari Department of Radiation Oncology The University of Chicago c-pelizzari@uchicago.edu
Judging by the number of requests for further information, apparently a number of people have the same problem of how to get DICOM images onto a treatment planning computer. All my experience in this regard is with unix systems, so nothing I say may be relevant to PCs, probably not to VAX/VMS either without considerable effort.
Here is what we have done to accept DICOM-3 image transfers from scanners, specifically from GE CTi scanners, Picker PQ-5000 scanners and Picker AcQSim workstations, but should be applicable to others as well. In our environment the scanner supports image send but not query, so we cannot request that an image set be sent to us. The way it works is that the operator of the scanner selects a menu option "Send selected images to Rad Onc" or words to that effect, and DICOM images are transferred to our machine. Since someone from our department is always present when one of our patients is scanned, this does not seem to be a problem.
In order for this to work, two things have to be accomplished. First, the scanner has to be configured to have that menu item, and to send the selected data specifically to our machine when it is selected. This was done by the GE service engineer, who basically typed our IP number and a service port number into a configuration file, connected that configuration entry to the menu button and rebooted the system (I probably understate the effort involved, but I think that logically that is what was done). Second, our machine has to be capable of accepting the DICOM images. this is done by running a program which listens on the abovementioned service port number for DICOM image storage service requests, and services those requests by storing the images onto our disk. This program in our case is one we obtained from the Washington University Electronic Radiology Laboratory, called simple_storage. Simple_storage is part of an extensive software package called CTN (for Central Test Node), which started life as DICOM demo software written for the RSNA annual meeting. There are other DICOM packages, some of which may have more features or any number of other advantages. This one has the virtue of being known by us to work. The CTN package is available from Wash U. at
ftp.erl.wustl.edu/pub/dicom/software/ctn/
You will have to figure out how to compile it on your system, and at least in the version we got, there is not a simple way to compile only one program without building the whole package. But that's not a big deal, it's easy to delete what you don't need. You certainly don't need to go through any of the database configuration, and in fact if you can avoid building the database components at all you might as well (need to edit some makefiles to do this). Here are a few things I did, some of which may work for you:
Choose an "environment" from the provided ones, e.g. environments/solaris/solaris.2.4.msql.noopt.env. Edit that file as appropriate. I commented out the line beginning "setenv XILHOME" and substituted "cp" for "install" since "install" does not work right on SGI. Also there are pointers that need to be set to the top level directory of the source tree, and to the makefile that goes along with this environment file. Then also edit that corresponding makefile, e.g. make.solaris.2.4.msql.noopt.env. I removed all references to $(LIBS_DATABASE), $(LIBPATH_DATABASE), -lsq, -ltbl_msql, -lmsql. This did not seem to hurt anything. When finished, "source" the environment file.
Make the libraries: cd facilities; make install
Make the applications you want: cd apps/simple_storage; make application.
Make dcm_dump_file, dicom_echo, and send_image too, for testing and later use. At this point you are ready to roll: cd to your image storage directory and say "simple_storage port_number >& simple_storage.out &" (runs it in the background, sends stdout and stderr to the indicated file).
Once simple_storage is up and running, it listens for incoming storage requests and stores the images in the directory from which it was started (or a subdirectory named CT or MR, which it will create). There is a little quirk associated with the GE scanner's DICOM sender, or at least I was told by the service engineer this is true, that it wants to send images to port number 104 on the storage server. Now, port number 104 is what is called a "privileged" port, meaning only root can open it. This means simple_storage has to run with root privileges. On the other hand, the stored image files are owned by the user who runs the simple_storage program, which means if root runs it, root owns the files. That means the treatment planning user probably can't move or delete the files. So we made simple_storage setuid root (chown root simple_storage; chmod u+s simple_storage) so anyone can run it. Actually we start it up in the background at system boot time with uid "planning" which is what our planner account is called. That way no one has to remember to run it before going up to CT to get a scan.
Just to confuse matters a little, the person who manages the PACS network in our Radiology department has configured a GE MR scanner to send DICOM images to port 8000, which seems to imply that the requirement for using port 104 is not universal. In addition, since a number of people are using simple_storage here, Radiology asked us to change the "application entity title" of our simple_storage to something other than the compiled-in default which is "DICOM_STORAGE". (Now that I think of it, this AE title is something the scanner also has to be told about your system as well as the port and IP numbers). You can also change this if desired by editing an include file in the simple_storage directory.
You can test if your simple_storage is working by sending one of the provided test images to yourself:
send_image hostname portname filename [filename2 filename3...]
Assuming all goes well, after you or the scanner pushes some images your way, you are now left with image files in DICOM format with absurd long names like "1.2.840.113674.950809132212025.100". You can use some of the other CTN programs, like dcm_dump_file to print out the contents of the various data elements in the file so you can see if you have the right patient, etc. and dcm_x_disp to actually view an image. Clearly dcm_dump_file has all the makings of a parser/converter to whatever format of image and/or header files you might want. If you are fortunate, your planning system will just import the DICOM images and you are off to the races.
At this point we happen to unpack the DICOM format images using some utility programs that came with the planning system we are using (Plunc), which we have rather extensively modified, and which unfortunately we cannot offer you since Plunc belongs to UNC and not to us. But don't be dismayed, the DICOM file format is really quite simple. It's just a sequence of [numeric key, associated data item length, data]. The image data seems to come last in the file, so if you just get dcm_dump_file to tell you the height, width and pixel depth of the image data, you can find the size of the image data. Subtract that from the file size and skip over the indicated number of bytes to get to the image data.
In fact, here is a script which uses dcm_dump_file to extract some image-specific information, save that info in a directory file, and put the image from the DICOM file into another file:
#!/bin/csh
##############
# script to strip dicom header, save some header info in a text file.
# C. Pelizzari, Jan 1997
#
touch dicom_directory
set name = image
if ($#argv > 1) set name = $2
dcm_dump_file $1 | egrep "Image Number|IMG Rows|IMG Columns|IMG Bits Stored|
Slice Location|Pixel Spacing|Slice Thickness" > tmp
echo "---" >> dicom_directory
grep "Image Number" tmp | cut -f3- -d'/' | sed -e 's/ //g' >> dicom_directory
grep "Slice Location" tmp | cut -f3- -d'/' | sed -e 's/ //g' >> dicom_directory
grep "Pixel Spacing" tmp | cut -f3- -d'/' | sed -e 's/ //g' >> dicom_directory
grep "Slice Thickness" tmp | cut -f3- -d'/' | sed -e 's/ //g' >> dicom_directory
set number = `grep "Image Number" tmp | cut -f5 -d'/'`
set imgsiz = 1
@ imgsiz *= $rows
set cols = `grep Col tmp | awk '{print $NF}'`
@ imgsiz *= $cols
set bits = `grep Bit tmp | awk '{print $NF}'`
@ imgsiz *= $bits
echo "image size $rows x $cols" >> dicom_directory
@ header = $filesize - $imgsiz
echo "stripping" $header "of" $filesize "bytes"
dd if=$1 of=$name$number.ima bs=$header skip=1
rm tmp
##############
So! Now you know as much about this as I can tell you. In fact,
I've probably told you more than I really know.
Hopefully it will be enough to get you going and there is not too much misinfo to slow you down.
Enjoy!
-chuck