This blog post was inspired by a question in the Varian Developer Forum:
From File > Export > DVH in Tabular format, one can export DVH for selected structures into a single text file. I have been using the R package RadOnc to load these DVH for analysis. I have a single dummy patient containing 40 plans and I wish to write a script to export the DVH in this way for all structures in the 40 plans. I cannot find a way to do this via scripting? Any advice appreciated
I've never used this feature in Eclipse, so I tried it out with a few structures. First, I had to make sure that the DVHs were already in the display. To do this, I had to click on the Planning menu item, then on Show Dose Volume Histogram View.
The output file, however, is not formatted in a way that's easily parseable. It starts with an overall header, then for each structure, contains a header including some dose statistics, followed by the DVH itself.
I would prefer a simpler file format, like CSV, where each line would contain the patient ID, course ID, plan ID, structure ID, DVH dose, and DVH volume—nothing else. This format would be easily readable by many programs, including R and Excel, although the R package that the question mentions, RadOnc, is able to read Eclipse's format.
Here I describe a Python script that goes through every plan of a given patient, and outputs the DVH of every structure in that plan to a CSV file. For simple, data extraction scripts like these, I prefer to use Python because I feel like the code is cleaner and to the point. Also, I'm easily able to use the command-line to run the script in batch with multiple patients, if necessary. Of course, I could do this with a C# program as well, but the coding/running cycle is shorter with Python. If you haven't used IronPython to run ESAPI scripts before, I suggest you first read my blog post ESAPI Programming with Python.
I've put the script on GitHub as export-dvh.
The script starts by storing the patient ID and the output file name that are given as command-line arguments. These are obtained using sys.argv, a list where the first item is the name of the script file name, and the rest are the command-line arguments.
Next, an Eclipse application object is created, and the patient is opened. I don't check whether the returned patient is None (Python's equivalent of null in C#), which happens when the patient can't be opened (like if it doesn't exist). This script, in general, doesn't do much error checking, but it would be wise to do so.
The output file is then opened for writing and a CSV writer object is created. This script uses the csv Python package.
To get to every structure in every plan of the patient, we need to loop through every Course, every PlanSetup, and every Structure object in the patient. Inside of this triple loop, we call the GetDVHCumulativeData method, specifying absolute units and a bin width of 0.01:
for course in patient.Courses: for plan in course.PlanSetups: for structure in plan.StructureSet.Structures: # Get the cumulative DVH in absolute units dvhData = plan.GetDVHCumulativeData(structure, DoseValuePresentation.Absolute, VolumePresentation.AbsoluteCm3, 0.01)
According to the ESAPI documentation, this method may return null (None in Python) if the DVH cannot be calculated. For example, the plan may not have any dose. If it's not None, we output the result using the CSV writer; otherwise, we print out an error on the standard error stream, typically the screen (see standard streams):
# dvhData is None if the DVH could not be calculated if dvhData != None: # Write every DVH point in CSV format for dvhPoint in dvhData.CurveData: dvhWriter.writerow([patientId, plan.Id, structure.Id, \ dvhPoint.DoseValue.Dose, dvhPoint.Volume]) else: # Print to the error stream (normally the screen) print >> sys.stderr, 'Cannot get DVH for structure', \ structure.Id, 'in plan', plan.Id
Finally, we dispose of the application object to free any unmanaged resourced.
To run this script, use the command-line and pass in the patient ID and the output file name:
ipy64.exe export-dvh.py 12345 output.csv
Any errors will be output to the screen, but the actual data will be output to a file. If you have a lot of plans or structures to process, you can print out some kind of progress to the screen.
If you have a lot of patients to process, you can repeatedly run this script with a different patient ID, perhaps in a shell script that reads a list of patient IDs. Or, you can edit the script to read the patient IDs from a file.