3D Coordinates in DicomObjects
All 3D calculations in DicomObjects are almost entirely in “real world” coordinated (patient X,Y,Z in mm). We have implemented a few dedicated methods to help you manipulate your 3D object or the ones that are displayed. This includes the DicomImage.ImagePlane() (DicomImage.Projection.Plane in COM) which returns an Image’s plane in Plane3D form that can be used for other calculation (in real world units again), say the intersection of two Image planes, or to offset one of them by the slab thickness and to calculate that intersection, etc.
ActionUpdate only fires when Pending responses are received
The ActionUpdate event fires when “pending” responses are received during C-Move operations.
The event is therefore triggered by a response on the primary connection indicating how many images:
Have been sent successfully Have Been sent with a warning response Have failed Are still to be sent Such pending responses are however optional and the server may choose not to send any.
If this is the case you will find that ActionUpdate does not fire.
Adding Sequence Items
A sequence in DICOM is represented by DicomDataSets Object in DicomObjects and a sequence item is represented by a DicomDataSet Object.
Adding additional attributes when using AddToDirectory
Only the mandatory elements (and one or two others we’ve been talked into adding) are put into the DICOMDIR by the AddToDirectory method, but you can add others yourself very easily.
The AddToDirectory method returns a 4 item DicomDataSets collection(COM) or DicomDataSetCollection (.NET) , and the 4 items are references to the 4 datasets referred to (existing or created as necessary) by that method call. They represent (in numerical order)
Adjusting WindowWidth And Level using ExplicitVOILUT property
.NET version Demo code to show the usage of the DicomImage property (ExplicitVOILUT) by generating and applying a LookupTable that has been calculated based on the standard DICOM windowing formula for calculating a Lookup table.
This example code assumes min and max as the range of values you need to handle in input data (anything beyond that is mapped to the first/last value) and for screen display it defaults to 8 bit output.
Anonymisation of DICOM Objects
DicomObjects is commonly and successfully used for Anonymisation and all the required data changes are easy using DicomObjects - the real problem is working out what needs changing! This subject is a real “Pandora’s box”, and whilst changing IDs, names etc. is easy, the more you look into it, the more “Hidden” patient information you find
dates and times of examinations and accession numbers buried within UIDs private data [who knows what’s in there?
Appropriate Compression For DICOM Files
As disk space usage has always been a priority when designing/implementing DICOM applications, “What is the best practice for writing DICOM files?” is becoming a very common question asked by our developers.
A sensible idea is to write uncompressed images in lossless-compressed format in order to reduce disk usage, and leave compressed images “as is”.
So check against the “original” transfer syntax of the image (either read from disk or received over network), if it’s one of those uncompressed formats then lossless compress it when you write on disk.
Avoiding Accepting Private SOP classes
Many pieces of equipment will try to negotiate both a Private SOP class and an official DICOM SOP Class for the images they send to a PACS, on the basis that only a PACS from the same manufacturer will accept the private one, with everyone else accepting only the DICOM one. It is important that this behavior is maintained at the PACS in order to avoid accidentally accepting the private class(es).
Background Operations in DicomObjects
DicomObjects.NET version All functions in .NET (e.g SendImages) are synchronous methods and do not return until the operation has completed. So if for example you wish to send asynchronously, then it is necessary to create your own thread using the native .NET threading methods. Whilst this might seem a “move backwards” from the COM version, remember that the COM threading in DicomObjects was only to allow you to do these things despite the lack of native threading support in VB6 etc.
Catch non-zero final status with DicomAssociation
In DicomObjects, we don’t throw exceptions when non-zero final status is received during the DICOM associations. The non-zero final status get passed by us and it should be the developers’ responsibility to check and, if needed, alert the user for any warnings or failures.
To catch the final status and also the related warning/error fields, you can take a look at the following code (VB6 for COM version and vb.
A config file may be used to over-ride some aspects of DicomObjects.NET behaviour without the need to recompile. This is broadly equivalent to the registry as used in the COM version, but has the advantage of being application-specific, avoiding any side-effects for other applications.
The config file is a simple text file called DicomObjects.Config in the same directory as the copy of the DicomObjects dll being used (which is normally the same as the application using it).
Conversion from the COM version of DicomObjects to the .NET version
Conversion of a DicomObjects ActiveX/COM project to .NET looks initially daunting, but in fact is quite simple, and for those using any of the complicated ActionComplete/ActionUpdate mechanisms and asynchronous DicomConnection objects will find that the resulting code is lot simpler and easier to maintain after conversion. As ever, we are happy to help with any conversion issues.
Threading Model The .NET version is free-threaded so all the events fire on their own threads, rather than being marshalled back onto the main thread.
Coronal, Sagittal & Transverse position calculation
To calculate frame position in terms of Sagittal, Coronal and Transverse position please see the attached code.
/* get info from file */ // (0028,0030) float pixel_space = new float; string pixel_space_p = Viewer.CurrentImage.DataSet[0x0028, 0x0030].Value as string; pixel_space = float.Parse(pixel_space_p); //Pixel size (row to row) pixel_space = float.Parse(pixel_space_p); //Pixel size (colums to columns) // (0028,0010) & (0028,0011) ushort rows = (ushort)(Viewer.CurrentImage.DataSet[0x0028, 0x0010].Value); // number of rows ushort colums = (ushort)(Viewer.
Counting Studies, Series and Instances
There are many times when users need to know how many objects there of a given type, without needing to enumerate each one. The good news is:
DICOM does allow such queries Such queries are possible using DicomObjects The less good news is:
The feature is optional (though strongly recommended) so many PACS do not support it It is not a default feature of DicomObjects queries (due to the load placed on the server), so a little work is required to enable it.
DicomObjects does not have direct, simple ways to create DICOM overlays. This is because most people regard them as obsolete, having been replaced since 1998 or so by DICOM Presentation states, which we do fully support, using CurrentToPresentationState method.
However, there are equivalent ways of burning the images into the pixel data using “PrinterImage” method (though that requires the pixel data to be re-imported into the image as a PrinterImage does not have any demographics)