Monday, January 9, 2012

Introduction to DICOM - Chapter 6 - Transfer Syntax

Transfer syntax defines how DICOM objects are serialized. When holding an object in memory, the only thing that matters is that your application can use it. The internal representation of the object is your own business. However, when sharing objects with other applications, everyone should be able to use the same object. The common solution for such problems is serialization.

Serialization is the process of writing a data structure or object state to wire i.e in a format that can be stored in a file or memory buffer, or transmitted across a network so it can be read on the other side of the wire or later by the same or by another process.

There's no shared memory in DICOM but it can be easily made using the same mechanism that is utilized for networking and files alike i.e. serializing the object into memory according to the rules dictated by the standard i.e. using transfer syntax.

In this post I'll cover the following issues:
  • Present the term Transfer Syntax, 
  • Why Transfer Syntax is required 
  • What is Transfer Syntax used for 
  • How Transfer Syntax is set when using 
    • DICOM files 
    • DICOM network
So, as I said, the serialization in DICOM is governed by a term called Transfer Syntax.

Transfer Syntax is defined at the object level and is the syntax for serializing a DICOM object. We have seen transfer syntaxes already in chapter 5 when dealing with association negotiation but did not discuss them. In order for an application to read a DICOM object from a network wire, it has to know the rules that were used to write the object into the wire. In the association request the calling AE sends a list of abstract syntaxes with SOP Class UID's. For every SOP Class, the calling AE sends a list of transfer syntax UID's. In the association response the called AE selects one of the transfer syntax UID's for every SOP class it accepts.


Here's a short snippet from the last post on DICOM networking:

Presentation Contexts:
  Context ID:        1 (Proposed)
    Abstract Syntax: =VerificationSOPClass
    Proposed SCP/SCU Role: Default
    Accepted SCP/SCU Role: Default
    Proposed Transfer Syntax(es):
      =LittleEndianExplicit
      =BigEndianExplicit
      =LittleEndianImplicit

This is part of the association request and in read you see the three trasnfer syntaxes that the calling application is suggesting for the first presentation context. It suggests the three basic transfer syntaxes:
  • Little Endian Explicit which is defined be the UID: 1.2.840.10008.1.2.1
  • Big Endian Explicit which is defined be the UID: 1.2.840.10008.1.2.2 and 
  • Little Endian Implicit which is defined by the UID: 1.2.840.10008.1.2
The Transfer Syntax UID is a UID that identify the transfer syntax (that's lame, ha?). Like all the other UID's it can be found in chapter 6 of the standard.

Transfer syntax sets exactly three things that are required in order to parse the serialized DICOM object:
  1. If VR's are explicit, i.e. if the data type code of every element should be serialized or it will be implicitly deduced from the element tag (see the post on DICOM Elements
  2. The order that bytes of multi-byte data types are serialized. For example, if we have an element with unsigned short data type (the Value Representation, VR, is US) than which byte of the two is the first byte written to the buffer and which is the second. 
  3. If pixel data is compressed and what compression algorithm is used. Compressed pixel data transfer syntax are always explicit VR little Endian (so you can call JPEG baseline 1.2.840.10008.1.2.4.50 for example "explicit little endian jpeg baseline") .
Most DICOM toolkits, and RZDCX is not different, handle the first two items on this list for you and automatically change the byte order and inserts or removed the VR codes for you. But there are cases when this multitude of choises (and don't ask why do we need three serialization syntaxes, instead read the second post in this series) causes problems.

I'm going to leave compression for a later chapter but in short, DICOM defines many compressed transfer syntaxes, that are simply the compressed image stream encapsulated into the pixel data element of the DICOM object so one can actually open a DICOM file with a binary editor, locate the pixel data element (7FE0,0010), cut out the value, save it as a jpeg file, double click it and see it. Maybe we'll do it together when talking about compression.

Let's now do an example that shows some issues that you may be confusing. Let's say we have two images, both are CT but one we have compressed with the jpeg lossless compression and we would like to send it to an archive.

This negotiation is rather strange because one can for example negotiate two abstract syntaxes (1 and 3, remember?) in the following way:

1) CT Image storage, explicit little endian
3) CT Image storage, jpeg lossless
In this example the calling application requests to send a CT image and a compressed CT image.
The request could have been composed this way as well:
1) CT Image storage, (explicit little endian, jpeg lossless)
But this is different because the called AE will select one of the suggested transfer syntaxes and the calling AE will have to send all CT images according to the selected transfer syntax either encoding them all before sending or decoding them all, depending on what transfer syntax the called AE have selected. If your application can't do this compression on the fly, you may get calls from the field. Using the first negotiation however, the called AE will most likely accept both 1 and 3 and we can send the uncompressed images using context id 1 and the jpeg compressed DICOM images using context id 3. You don't mind that I say that RZDCX takes care of all this for you.

Most issues with transfer syntax are related to applications that don't support transfer syntaxes that others require. If you have one application that can read only big endian and another that is limited to little endian, they will never talk to one another. That's radical but there are many applications that don't support any compressed images or can only store them but not display them and if your application generates only jpeg's so you better rethink the design.
Transfer syntax issues sometimes cause images to look bad. I've seen applications that change the big-little endian (i.e. the byte order) of the pixel data without changing the transfer syntax properly causing the images to be unreadable. Such images usually feature jagged edges and bad contrast. I've also seen a very popular CD burner that if interfaced with implicit syntax causes many elements to become of Unknown VR even if these are well defined elements.

Now let's move to DICOM files. Just like in DICOM networking, DICOM files must be read by all applications so thats a serialization too. Here are the rules for DICOM files:

When writing a DICOM object to file, the application that creates the file writes it in the following way:
  1. The first 128 bytes are null (0x00) 
  2. Bytes 128 - 131 (zero based) are 'DICM' which is the DICOM magic number 
  3. Add to the object a file meta header - a group of elements of group 0002 that are the first elements in the object. 
  4. Group 0002 is written in Little Endian Explicit 
  5. Element (0002,0010) is the Transfer Syntax UID that is used for all the elements other than group 2.
So, when reading a DICOM file, a DICOM library should do this:

  1. Read 132 bytes (these 132 bytes are called the preamble) and see that 128-131 equal "DICM" 
  2. Start parsing using Little Endian Explicit all the group 0002 elements 
  3. Check the value of element (0002,0010) and use this transfer syntax for the rest of the file.
Important: always remove group 0002 before sending objects over the network. Group 0002 is strictly for DICOM files.


Before summarizing, here's a very detailed explanation of how a DICOM file actually looks like in the byte level. The screenshot bellow shows three DICOM files of exactly the same object opened in a binary editor. Each file was saved with a different transfer syntax using this simple code:


      string BEEfname(filename);
      BEEfname+=".bee.dcm";
      obj->TransferSyntax = TS_BEE;
      obj->saveFile(BEEfname.c_str());


      string LEEfname(filename);
      obj->TransferSyntax = TS_LEE;
      LEEfname+=".lee.dcm";
      obj->saveFile(LEEfname.c_str());

      string LEIfname(filename);
      obj->TransferSyntax = TS_LEI;
      LEIfname+=".lei.dcm";
      obj->saveFile(LEIfname.c_str());



Up to the highlighted part, the files are identical but for the value of the transfer syntax UID element in the file meta header. You can see the 128 0's and the DICM and then the elements of group 0002. In all three files this part is little endian explicit and you can see the VR codes UL and then OB just after the preamble.

The highlighted part is the first data element of the object itself, which is element (0008,0005). While in the Little Endian files (left and right) the bytes are ordered 08 00 05 00, in the big endian file (center) the order is 00 08 00 05.


Then, in the explicit VR files (left and center) the tag is followed by 'CS' which is the VR code of this element. CS stands for Code String and tells us the data type of this element. In the implict VR file this code is missing. It is implicitly specified by the tag. Tags always have the same type (this tag is called extended character set and it is the code string of the character set encoding for the strings in the file).

After that we have the data element length which is 0xA (meaning the value is 10 bytes long) and then the value itself 'ISO_IR 192' which means that the strings in this DICOM files are encoded using UTF-8. Note that in the explicit VR files the length is stored in a two bytes while in the implicit VR file the length is stored in four bytes (quiz: though not very important, can you guess why?).

Let's summarize:
  1. Serialization of DICOM objects is governed by Transfer Syntax 
  2. Transfer syntax sets: 
    • The byte order (little/big) 
    • If VR's are serialized (explicit/implicit) 
    • If pixel data is compressed or not (if compressed 1 is little and 2 is explicit) 
  3. In DICOM networking, the transfer syntax is selected per object type (SOP Class) at the negotiation phase 
  4. In DICOM files the transfer syntax is set in the File Meta Header (group 0002)
Recommendations as far as transfer syntax goes:
  1. Always support and propose all 3 basic transfer syntaxes: LEI, LEE and BEE
  2. If possible, always prefer LEE as your default.
With RZDCX you are dismissed from bothering about all these details. The transformations between transfer syntaxes are taken care of internally as well as the selection of transfer syntaxes during association negotiation and when reading and writing files. The toolkit takes care of all that. You can control it when saving files as shown in the detailed example above and also compress and decompress but unless there's a very specific requirement about that in your application, you will probably never have to deal with it.

One last comment. Many times I'm asked what transfer syntax is used by some application internally, i.e. when some application, e.g. a PACS writes files in it's internal storage, how they are stored. My answer to that is that I don't know and that you shouldn't care. Never assume anything about the internals of an application. The only thing that matters is their interfaces.

As always, comments and questions are most welcome

43 comments:

  1. Why recommended to propose or support BEE? Almost all systems accept LEE and LEI is required to be supported.
    It only makes testing more difficult.

    ReplyDelete
  2. Hello there,
    Nice series of posts about DICOM. Those are a "what every DICOM developper should know about DICOM", I like it.
    @Victor : I guess this recommandation goes in the way of backward compatibility: there are still a lot of old platforms in the field that are natively big endian speakers, as you may know.

    ReplyDelete
    Replies
    1. Thanks. Supporting all three LEI, LEE, BEE costs nothing. On the other hand omitting any of them may result with failures to communicate with some systems so why not support it?

      Delete
  3. so if i use the tag 7FE0,0010 i can have acess to my real image ??

    ReplyDelete
  4. so if i use the tag 7FE0,0010 i can have acess to my real image ??

    ReplyDelete
  5. Thank you for this helpful tutorial. Could you possibly explain this statement in more detail please: "Important: always remove group 0002 before sending objects over the network. Group 0002 is strictly for DICOM files". If this information is removed before transfer how is the file read in the future? Is it possible to determine the transfer syntax in another way if this information has been removed? Thanks.

    ReplyDelete
    Replies
    1. Group 0002 of the DICOM tags is part of the file meta header. The file meta header is created by the application that save the DICOM file on disk. When you send an object over a DICOM network connection using the C-STORE command, the object is saved by the receiving application application on the receiving application hard disk, so the receiving application adds the file meta header to the object and save it as a DICOM file.
      When reading a DICOM file from a CD for example, the transfer syntax is in tag (0002,0010) of the file meta header so you know how to read the object from the disk.
      Once the object is in memory and you want to send it over the network, the transfer syntax of that transfer is negotiated in the association negotiation phase. It can be different then the one that you used to read the file from disk.
      And when the receiving application finished reading the object from the network using the transfer syntax you agreed on for that, it can save the object to disk in whatever transfer syntax it feels like.
      So for example you can read a DICOM file encoded in LEE from a CD, send it over the network using LEI and then it might be stored in BEE. All these transfer syntax changes have no effect on the object content, it's just a syntax that is used for the transfer.
      One thing to remember, it should work as long as you keep in line with the encoding rules of DICOM. If for example, you will encode some tag with the wrong VR and use LEI, then the other party will not be able to read the data correctly because it will assume a different VR. This can be very problematic issue with UN (Unknown VR type).
      BTW, the file meta header should always be in LEE. The reader reads this part, then switch to the transfer syntax it found in tag (0002,0010) and continues.

      Delete
    2. This reply was very helpful indeed.

      Delete
  6. First of all, this tutorial helping me lot. Thanks Roni.

    can you please explain me that, why explicit VR files the length is stored in a two bytes while in implicit VR file the length is stored in four bytes ?

    ReplyDelete
    Replies
    1. Thank You! Great to hear that.
      Great question. Here's a hint:
      In explicit VR some VR's have 4 bytes length because they may have length longer then 65565 e.g. OB and OW.
      Now lets say there's a tag your parser doesn't know and the stream is implicit ... so we better have all the lengths the same size.
      What beats me is why in explicit VR the VR's with 4 bytes length has extra two empty bytes for the VR code. For this one I have no idea other then double word alignment considerations.

      Delete
    2. thanks for quick response, And about explicit VR's with 2 extra bytes I think your idea is close 99.99%.

      Delete
  7. This comment has been removed by the author.

    ReplyDelete
  8. Hi, great tutorial..I am new to Dicom...my question is how can I say...this is lee or bee transfer syntax? Is there any rule, so that I can say this is for little endian or big endian or explicit or implicite or compressed?

    ReplyDelete
  9. Htir article was really helpful. You said that :
    I'm going to leave compression for a later chapter but in short, DICOM defines many compressed transfer syntaxes, that are simply the compressed image stream encapsulated into the pixel data element of the DICOM object so one can actually open a DICOM file with a binary editor, locate the pixel data element (7FE0,0010), cut out the value, save it as a jpeg file, double click it and see it. Maybe we'll do it together when talking about compression.

    I know that jpeg is inside DICOM file so I copy pixel data object and I can't open it...

    ReplyDelete
  10. if the transfer syntax ID is
    1.2.840.10008.1.2.4.91 then what i do to decompress the image . ?

    ReplyDelete
  11. Nice explanation for beginners.

    ReplyDelete
  12. Thank you for your great articles!!!!

    ReplyDelete
  13. Nice Explanation of Transfer Syntax.

    ReplyDelete
  14. Thank you. You really helped me to understand why one should send more than one transfer abstract. Still this standard is... how can they not define allowed transfer syntax for an SOP class?! Makes no sense to me to be able to send a picture as video and the other way round.

    ReplyDelete
  15. This was a great article. Thank you for this.

    So, to wrap up, may I always assume, given a dicom element:

    1. the 4 first bytes are the tag, then
    2. (for explicit VR) the 2 next bytes represent the VR and
    3. a. (for explicit VR) the length is stored in a two bytes,
    b. (for implicit VR) stored in four bytes

    ReplyDelete
    Replies
    1. You got it, but! there are exceptions for some VR's when using Explicit Little Endian:"OB", "OW", "OF", "SQ", "UT" or "UN" have additional 2 0x00 bytes after them so the VR takes 4 bytes and their length field is always 4 bytes.

      Delete
    2. Thx.
      I have an additional question: Do you know any javascript libraries that are able to edit dicom data. So far, I only found libraries which could parse them.

      Delete
  16. Hi expert, DICOM client can get datas from server. However, when the VPN was enable in the network, connection timeout occurs:
    ERROR: Failed to establish association:
    java.net.SocketTimeoutException: connect timed out

    Network too slow cause timeout?? (confirmed no blacking in the network) Thank you.

    ReplyDelete
  17. First of all nice article. I ahev one doubt.
    You have mentioned that Value Length is mentioned after VR with value 0xA. But I can't find it in the images. Can you please point that to me.

    ReplyDelete
  18. Once an association is made, is it not possible to send DICOM instances over it with different TransferSyntaxUIDs (i.e. the pixel data has different compression schemes: for example some are lossy and some are lossless)?

    ReplyDelete
  19. Every presentation context has one combination of SOP Class and transfer syntax.
    If you want to be able to use different transfer syntaxes with the same SOP Class you have to negotiate one presentation context for every combination

    ReplyDelete
  20. If I say I support "ExplicitVRLittleEndian" will the association then be negotiated with a more specific UID representing the compression used for the DICOM instance?

    ReplyDelete
    Replies
    1. Alex, I'm not sure what you ask here. If you can present the whole picture. What are you trying to do. What software are you using and what is the problem, then I may be able to help.

      Delete
  21. I am trying to use pynetdicom to perform a C-FIND and then a C-MOVE against an Orthanc PACS server. Since Orthanc does not do any transcoding of the DICOM instances, the pixel data that is C-STORE back will be compressed with various methods.

    ReplyDelete
    Replies
    1. Yes, transcoding instances is tricky. Well, in this case you have to be able to accept all the SOP Classes and Transfer Syntaxes that Orthanc might ask for.
      Other than that, I suggest that you refer to their customer support.

      Delete
  22. So assuming that I can accept all Transfer Syntaxes that Orthanc might ask for, how can I tell which TransferSyntaxUID to set when saving the DICOM file (i.e. how can I tell how the Pixel Data has been encoded)?

    ReplyDelete
  23. Read chapter 5 in this tutorial.
    You know that from the transfer syntax selected for the prensetation context id that the file was stored with.
    http://dicomiseasy.blogspot.co.il/2011/12/introduction-to-dicom-chapter-5-solving.html

    ReplyDelete
  24. I read chapter 5. My question, does the association need to be closed between DICOM Stores if the TransferSytaxUID changes between different image compression options? Or is there a way to negotiate a more general syntax (i.e. ExplicitVRLittleEndian) and then for then to send the actual compression scheme used?

    ReplyDelete
  25. There is no way to negotiate 'more general' syntax.
    Explicit Little Endian is not more general, it is specifically uncompressed, little endian, explicit.
    There is no need to close the association either.
    What the C-MOVE SCP (your open source PACS in this case) should do is negotiate many presentation contexts for the same SOP class, each with a different Transfer Syntax. This way it can send over one association Instances of the same class using different transfer syntaxes.

    ReplyDelete
  26. You state: Compressed pixel data transfer syntax are always explicit VR little Endian

    What I don't understand:
    - how knowing wether pixel data is compressed or not compression could be relevant for the transfer of the data.

    Is knowing that your stream is explicit VR little Endian not enough for the receiver to correctly store the data?
    If you look at the scheme provided here
    http://3.bp.blogspot.com/-hAI3mF_ZL-I/TtQOMjy6LmI/AAAAAAAAKvE/SDqvwxaXnog/s1600/DICOM+Element.png

    the value field is just data no?

    ReplyDelete
    Replies
    1. Yes, You are right. Transfer syntax is a good name for something that describes the syntax used to transfer the data. Yes, it was overloaded with the encoding of the pixel data. Maybe its not very elegant but that's the way it is. Perfect is the worst enemy of very good.

      Delete
  27. Great Post and Thanks! I come across a problem with the Transfer syntax. Whenever my application try to send a color ultrasound dicom (with Transfer syntax Jpeg Baseline 1.2.840.10008.1.2.4.50), the receiving side always only use Explicit VR Little Endian (1.2.840.10008.1.2.1) and the image became green tinted. Is there anything I could do about this or any hints? Many Thanks!

    ReplyDelete
    Replies
    1. Sounds like a bug in one of the applications that you're using

      Delete
    2. I think so. Would it be any chance to verify it? Thanks again.

      Delete