Click here to Skip to main content
15,879,326 members
Articles / Desktop Programming / WPF

Medical image visualization using WPF

Rate me:
Please Sign up or sign in to vote.
4.96/5 (68 votes)
27 Sep 2012CPOL6 min read 107.4K   7.7K   121  
The article demonstrates the visualization of medical images (DICOM) using WPF.
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace DICOMViewer.Parsing
{
    enum VREncodingMode { Undefined, ExplicitVR, ImplicitVR };
    enum EndianEncodingMode { Undefined, LittleEndian, BigEndian };

    public class DICOMParser
    {
        private string mFileName = null;
        private BinaryReader mBinaryReader = null;
        private string mTransferSyntax = "???";
        private bool mFileMetaInfoReadingOngoingFlag = true;

        public DICOMParser(string theFileName)
        {
            mFileName = theFileName;
        }

        // The method converts the binary encoded DICOM file into a XDocument tree.
        // The XDocument can be queried afterwars via LINQ to access the detailed information for individual DICOM attributes.
        //
        // In order to understand the implementation details, please refer to 'DCIOM Standard 2009, Part 5: Data Structures and Encoding'

        public XDocument GetXDocument()
        {
            mBinaryReader = new BinaryReader(File.Open(mFileName, FileMode.Open, FileAccess.Read, FileShare.Read));

            XDocument aXDocument = new XDocument(
                new XDeclaration("1.0", System.Text.Encoding.Default.EncodingName, "yes"),
                new XElement("DicomFile"));

            XElement aRootNode = new XElement("DataSet");
            aXDocument.Root.Add(aRootNode);

            // Check, if file is following the DICOM standard (DICOM Specification Part 10: Media Storage and File Format for Media Interchange)
            //
            // In this case, the file will contain the following:
            //  a) A fixed file preamble (128 bytes) not to be used
            //  b) DICOM Prefix (four bytes containing string "DICM")
            //  c) File Meta Information (sequence of File Meta Attributes)
            //     All File Meta Attributes are of type (0002,xxxx) and shall be encoded using the 'Explicit VR Little Endian' Transfer Syntax

            // Skip file preable (128 bytes)
            mBinaryReader.BaseStream.Position = 128;

            // Read in the "DICM" string
            byte[] aDICMString = new byte[4];
            mBinaryReader.Read(aDICMString, 0, 4);
            Encoding myStringEncoder = System.Text.Encoding.Default;
            string DICMString = myStringEncoder.GetString(aDICMString).Trim();

            if (DICMString.Equals("DICM"))
            {
                // DICOM file, start parsing after preamble (128 byte) and DICM string (4 bytes)
                mBinaryReader.BaseStream.Position = 128 + 4;
            }
            else
            {
                // No standard DICOM file, set offset to '0'
                mBinaryReader.BaseStream.Position = 0;
            }

            ParseDataSet(mBinaryReader.BaseStream.Length, aRootNode);

            mBinaryReader.Close();
            
            return aXDocument;
        }

        private void ParseDataSet(long StreamPositionEnd, XElement theParentNode)
        {
            // Each Sequence does have it's own 'Private Code Dictionary'
            PrivateCodeDictionary aPrivateCodeDictionary = new PrivateCodeDictionary();

            while (mBinaryReader.BaseStream.Position < StreamPositionEnd)
            {
                DICOMDataElement aTag = GetNextTag(aPrivateCodeDictionary);

                if (aTag != null)
                {
                    // Check for 'Transfer Syntax UID (0002,0010)'
                    // ===========================================
                    if (aTag.Tag.Equals("(0002,0010)"))
                        mTransferSyntax = aTag.ValueField;

                    // Private Creator Code Handling
                    // =============================
                    // Reset Private Creator Code List if current Tag is 'Item'
                    if (aTag.Tag.Equals("(FFFE,E000)"))
                        aPrivateCodeDictionary.ClearPrivateCreatorCode();

                    // Add new Private Creator Code
                    if ((aTag.GroupNumber % 2) == 1 && (aTag.ElementNumber <= 0xFF))
                    {
                        string aGroupNumberString = aTag.GroupNumber.ToString("X4");
                        string aElementNumberString = aTag.ElementNumber.ToString("X2");
                        aPrivateCodeDictionary.LoadPrivateCreatorCode(aGroupNumberString, aElementNumberString, aTag.ValueField.Trim());
                    }

                    // Add new Tag to XDocument
                    // ========================
                    XElement newXDataElement = new XElement("DataElement");
                    theParentNode.Add(newXDataElement);

                    newXDataElement.Add(new XAttribute("Tag", aTag.Tag));
                    newXDataElement.Add(new XAttribute("TagName", aTag.TagName));
                    newXDataElement.Add(new XAttribute("VR", aTag.VR));
                    newXDataElement.Add(new XAttribute("VM", aTag.VM));
                    newXDataElement.Add(new XAttribute("Data", aTag.ValueField));
                    newXDataElement.Add(new XAttribute("Length", aTag.ValueLength.ToString()));
                    newXDataElement.Add(new XAttribute("StreamPosition", aTag.StreamPosition.ToString()));

                    // Call 'ParseDataSet' recursively for nested datasets
                    // ===================================================
                    if (aTag.VR.Equals("SQ"))
                    {
                        if (aTag.ValueLength == -1)
                            ParseDataSet(mBinaryReader.BaseStream.Length, newXDataElement);
                        else
                            ParseDataSet(mBinaryReader.BaseStream.Position + aTag.ValueLength, newXDataElement);
                    }

                    // End criteria for sequence reached?
                    // ==================================
                    // Break if current Tag is 'Sequence Delimitation Item'
                    if (aTag.Tag.Equals("(FFFE,E0DD)"))
                        break;
                }
            }
        }

        private DICOMDataElement GetNextTag(PrivateCodeDictionary thePrivateCodeDictionary)
        {
            DICOMDataElement aTag = new DICOMDataElement();

            // Check, if we still read the 'File Meta Information' (attributes of type '(0002,xxxx)')
            // For this purpose, we do a look-up of the GroupNumber with the default 'Little Endian' encoding mode
            // If the GroupNumer is different to 0x0002, we have left the 'File Meta Information' section
            // Don't forget to adjust the Stream Position afterwards (decrement by '2')
            // Afterwars, the GroupNumber is read again (this time with the correct encoding mode)
            ushort aGroupNumberLookup = GetUnsignedShort_16Bit(EndianEncodingMode.LittleEndian);
            if (aGroupNumberLookup != 0x0002)
                mFileMetaInfoReadingOngoingFlag = false;

            mBinaryReader.BaseStream.Position -= 2;

            // Set the Encoding Mode for this tag
            // ==================================
            // All 'File Meta Information Attributes (0002,xxxx)' shall be encoded as 'VR explicit, little endian'
            // All other Attributes have to be encoded according to the transfer syntax value
            VREncodingMode aTagVREncodingMode = VREncodingMode.Undefined;
            EndianEncodingMode aTagEndianEncodingMode = EndianEncodingMode.Undefined;

            if (mFileMetaInfoReadingOngoingFlag)
            {
                aTagVREncodingMode = VREncodingMode.ExplicitVR;
                aTagEndianEncodingMode = EndianEncodingMode.LittleEndian;
            }
            else
            {
                switch (mTransferSyntax)
                {
                    // Explicit VR Encoding, little endian
                    case "1.2.840.10008.1.2.1":
                        aTagVREncodingMode = VREncodingMode.ExplicitVR;
                        aTagEndianEncodingMode = EndianEncodingMode.LittleEndian;
                        break;

                    // Implicit VR Encoding, little endian
                    case "1.2.840.10008.1.2":
                        aTagVREncodingMode = VREncodingMode.ImplicitVR;
                        aTagEndianEncodingMode = EndianEncodingMode.LittleEndian;
                        break;

                    // Explicit VR Encoding, big endian
                    case "1.2.840.10008.1.2.2":
                        aTagVREncodingMode = VREncodingMode.ExplicitVR;
                        aTagEndianEncodingMode = EndianEncodingMode.BigEndian;
                        break;

                    // For every other Transfer Syntax (e.g. JPEG encoding), 'Exlicit VR, little endian' shall be used
                    default:
                        aTagVREncodingMode = VREncodingMode.ExplicitVR;
                        aTagEndianEncodingMode = EndianEncodingMode.LittleEndian;
                        break;
                }
            }

            // Read GroupNumber
            aTag.GroupNumber = GetUnsignedShort_16Bit(aTagEndianEncodingMode);

            // Read ElementNumber
            aTag.ElementNumber = GetUnsignedShort_16Bit(aTagEndianEncodingMode);

            // Format the Tag string
            aTag.Tag = string.Format("({0},{1})", aTag.GroupNumber.ToString("X4"), aTag.ElementNumber.ToString("X4"));

            // Get VR value
            aTag.VR = GetVR(aTag.GroupNumber, aTag.ElementNumber, aTag.Tag, thePrivateCodeDictionary, aTagVREncodingMode);

            // Get DICOM attribute name
            aTag.TagName = GetTagName(aTag.GroupNumber, aTag.ElementNumber, aTag.Tag, thePrivateCodeDictionary);
            
            switch (aTag.VR)
            {
                case "OB":  // Other Byte String
                case "OF":  // Other Float String
                case "OW":  // Other Word String
                case "UN":  // Unknown content
                    {
                        // Reference: DCIOM Standard 2009, PS 3.5: Data Structures and Encoding
                        // Table 7.1-1 
                        //  -------------------------------------
                        // | Reserved | ValueLength | ValueField |
                        //  -------------------------------------
                        // | 2 Bytes  | 4 Bytes     | n Bytes    |
                        //  -------------------------------------

                        if (aTagVREncodingMode == VREncodingMode.ExplicitVR)
                        {
                            // Skip the two Reserved bytes
                            byte ReservedByte0 = mBinaryReader.ReadByte();
                            byte ReservedByte1 = mBinaryReader.ReadByte();
                        }

                        aTag.ValueLength = GetLength_32Bit(aTagEndianEncodingMode);
                        aTag.StreamPosition = mBinaryReader.BaseStream.Position;

                        // Parse out the data
                        if (aTag.ValueLength > 10)
                        {
                            for (int i = 0; i < 10; i++)
                            {
                                byte b = mBinaryReader.ReadByte();
                                aTag.ValueField += string.Format("{0} ", b.ToString("X2"));
                            }
                            aTag.ValueField += "....";
                            mBinaryReader.BaseStream.Position += aTag.ValueLength - 10;
                        }
                        else
                        {
                            for (int i = 0; i < aTag.ValueLength; i++)
                            {
                                byte b = mBinaryReader.ReadByte();
                                aTag.ValueField += string.Format("{0} ", b.ToString("X2"));
                            }
                        }

                        aTag.ValueField = aTag.ValueField.Trim();
                        aTag.VM = 1;

                        break;
                    }

                case "UT":  // Unlimited Text
                    {
                        // Reference: DCIOM Standard 2009, PS 3.5: Data Structures and Encoding
                        // Table 7.1-1 
                        //  -------------------------------------
                        // | Reserved | ValueLength | ValueField |
                        //  -------------------------------------
                        // | 2 Bytes  | 4 Bytes     | n Bytes    |
                        //  -------------------------------------

                        if (aTagVREncodingMode == VREncodingMode.ExplicitVR)
                        {
                            // Skip the two Reserved bytes
                            byte ReservedByte0 = mBinaryReader.ReadByte();
                            byte ReservedByte1 = mBinaryReader.ReadByte();
                        }

                        aTag.ValueLength = GetLength_32Bit(aTagEndianEncodingMode);
                        aTag.StreamPosition = mBinaryReader.BaseStream.Position;

                        // Parse out the Value string using the Default Encoder
                        byte[] aBuffer = new byte[aTag.ValueLength];
                        mBinaryReader.Read(aBuffer, 0, (int)aTag.ValueLength);
                        
                        Encoding myStringEncoder = System.Text.Encoding.Default;
                        aTag.ValueField = myStringEncoder.GetString(aBuffer).Trim();

                        aTag.ValueField = aTag.ValueField.Replace(Environment.NewLine, " ");
                        aTag.ValueField = aTag.ValueField.Replace(Convert.ToChar(0x00).ToString(), "");

                        aTag.VM = 1;

                        break;
                    }

                case "SQ":  // Sequence of Items 
                    {
                        // Reference: DCIOM Standard 2009, PS 3.5: Data Structures and Encoding
                        // Table 7.1-1 
                        //  ------------------------
                        // | Reserved | ValueLength |
                        // | 2 Bytes  | 4 Bytes     |
                        //  ------------------------

                        if (aTagVREncodingMode == VREncodingMode.ExplicitVR)
                        {
                            // Skip the two Reserved bytes
                            byte ReservedByte0 = mBinaryReader.ReadByte();
                            byte ReservedByte1 = mBinaryReader.ReadByte();
                        }

                        aTag.ValueLength = GetLength_32Bit(aTagEndianEncodingMode);
                        aTag.StreamPosition = mBinaryReader.BaseStream.Position;

                        aTag.VM = 1;

                        break;
                    }

                case "AE":  // Application Entity
                case "AS":  // Age String
                case "CS":  // Code String
                case "DA":  // Date
                case "DT":  // Date Time
                case "LT":  // Long Text
                case "PN":  // Person Name
                case "SH":  // Short String
                case "ST":  // Short Text
                case "TM":  // Time
                    {
                        // Reference: DCIOM Standard 2009, PS 3.5: Data Structures and Encoding
                        // Table 7.1-2 
                        //  --------------------------
                        // | ValueLength | ValueField |
                        //  --------------------------
                        // | 2 Bytes     | n Bytes    |
                        //  --------------------------

                        if (aTagVREncodingMode == VREncodingMode.ExplicitVR)
                            aTag.ValueLength = GetLength_16Bit(aTagEndianEncodingMode);
                        else
                            aTag.ValueLength = GetLength_32Bit(aTagEndianEncodingMode);

                        aTag.StreamPosition = mBinaryReader.BaseStream.Position;
                        
                        // Parse out the Value string using the Default Encoder
                        byte[] aBuffer = new byte[aTag.ValueLength];
                        mBinaryReader.Read(aBuffer, 0, (int)aTag.ValueLength);

                        Encoding myStringEncoder = System.Text.Encoding.Default;
                        aTag.ValueField = myStringEncoder.GetString(aBuffer).Trim();

                        aTag.ValueField = aTag.ValueField.Replace(Environment.NewLine, " ");
                        aTag.ValueField = aTag.ValueField.Replace(Convert.ToChar(0x00).ToString(), "");

                        aTag.VM = 1;

                        break;
                    }

                case "DS":  // Decimal String
                case "IS":  // Integer String
                case "LO":  // Long String
                    {
                        // Reference: DCIOM Standard 2009, PS 3.5: Data Structures and Encoding
                        // Table 7.1-2 
                        //  --------------------------
                        // | ValueLength | ValueField |
                        //  --------------------------
                        // | 2 Bytes     | n Bytes    |
                        //  --------------------------

                        if (aTagVREncodingMode == VREncodingMode.ExplicitVR)
                            aTag.ValueLength = GetLength_16Bit(aTagEndianEncodingMode);
                        else
                            aTag.ValueLength = GetLength_32Bit(aTagEndianEncodingMode);

                        aTag.StreamPosition = mBinaryReader.BaseStream.Position;

                        // Parse out the Value string using the Default Encoder
                        byte[] aBuffer = new byte[aTag.ValueLength];
                        mBinaryReader.Read(aBuffer, 0, (int)aTag.ValueLength);

                        Encoding myStringEncoder = System.Text.Encoding.Default;
                        aTag.ValueField = myStringEncoder.GetString(aBuffer).Trim();

                        aTag.ValueField = aTag.ValueField.Replace(Environment.NewLine, " ");
                        aTag.ValueField = aTag.ValueField.Replace(Convert.ToChar(0x00).ToString(), "");

                        string[] split = aTag.ValueField.Split(new Char[] { '\\' });

                        aTag.VM = split.Count();

                        break;
                    }

                case "UI":  // Unique Identifier (UID)
                    {
                        // Reference: DCIOM Standard 2009, PS 3.5: Data Structures and Encoding
                        // Table 7.1-2 
                        //  --------------------------
                        // | ValueLength | ValueField |
                        //  --------------------------
                        // | 2 Bytes     | n Bytes    |
                        //  --------------------------

                        if (aTagVREncodingMode == VREncodingMode.ExplicitVR)
                            aTag.ValueLength = GetLength_16Bit(aTagEndianEncodingMode);
                        else
                            aTag.ValueLength = GetLength_32Bit(aTagEndianEncodingMode);

                        aTag.StreamPosition = mBinaryReader.BaseStream.Position;

                        // Parse out the Value string using the Default Encoder
                        byte[] aBuffer = new byte[aTag.ValueLength];
                        mBinaryReader.Read(aBuffer, 0, (int)aTag.ValueLength);
                        
                        Encoding myStringEncoder = System.Text.Encoding.Default;
                        aTag.ValueField = myStringEncoder.GetString(aBuffer).Trim();

                        aTag.ValueField = aTag.ValueField.Replace(Environment.NewLine, " ");
                        aTag.ValueField = aTag.ValueField.Replace(Convert.ToChar(0x00).ToString(), "");

                        aTag.VM = 1;

                        break;
                    }

                case "AT":  // Attribute Tag
                    {
                        // Reference: DCIOM Standard 2009, PS 3.5: Data Structures and Encoding
                        // Table 7.1-2 
                        //  -----------------------------
                        // | ValueLength | ValueField    |
                        //  -----------------------------
                        // | 2 Bytes     | 4 Bytes fixed |
                        //  -----------------------------

                        if (aTagVREncodingMode == VREncodingMode.ExplicitVR)
                            aTag.ValueLength = GetLength_16Bit(aTagEndianEncodingMode);
                        else
                            aTag.ValueLength = GetLength_32Bit(aTagEndianEncodingMode);

                        aTag.StreamPosition = mBinaryReader.BaseStream.Position;

                        ushort aGroupNumber = GetUnsignedShort_16Bit(aTagEndianEncodingMode);
                        ushort aElementNumber = GetUnsignedShort_16Bit(aTagEndianEncodingMode);

                        aTag.Tag = string.Format("({0}),({1})", aGroupNumber.ToString("X4"), aElementNumber.ToString("X4"));

                        aTag.VM = 1;

                        break;
                    }

                case "UL":  // Unsigned Long (32 Bit, 4 Bytes)
                    {
                        // Reference: DCIOM Standard 2009, PS 3.5: Data Structures and Encoding
                        // Table 7.1-2 
                        //  ----------------------------------------
                        // | ValueLength | ValueField               |
                        //  ----------------------------------------
                        // | 2 Bytes     | ValueLength x 4 Bytes    |
                        //  ----------------------------------------

                        if (aTagVREncodingMode == VREncodingMode.ExplicitVR)
                            aTag.ValueLength = GetLength_16Bit(aTagEndianEncodingMode);
                        else
                            aTag.ValueLength = GetLength_32Bit(aTagEndianEncodingMode);

                        aTag.StreamPosition = mBinaryReader.BaseStream.Position;

                        for (int i = 0; i < aTag.ValueLength; i += 4)
                        {
                            ulong Value = GetUnsignedInt_32Bit(aTagEndianEncodingMode);
                            aTag.ValueField += Value.ToString() + " ";
                            aTag.VM++;
                        }
                        break;
                    }

                case "US":  // Unsigned Short
                    {
                        // Reference: DCIOM Standard 2009, PS 3.5: Data Structures and Encoding
                        // Table 7.1-2 
                        //  -------------------------------------
                        // | ValueLength | ValueField            |
                        //  -------------------------------------
                        // | 2 Bytes     | ValueLength x 2 Bytes |
                        //  -------------------------------------

                        if (aTagVREncodingMode == VREncodingMode.ExplicitVR)
                            aTag.ValueLength = GetLength_16Bit(aTagEndianEncodingMode);
                        else
                            aTag.ValueLength = GetLength_32Bit(aTagEndianEncodingMode);

                        aTag.StreamPosition = mBinaryReader.BaseStream.Position;

                        for (int i = 0; i < aTag.ValueLength; i += 2)
                        {
                            ushort Value = GetUnsignedShort_16Bit(aTagEndianEncodingMode);
                            aTag.ValueField += Value.ToString() + " ";
                            aTag.VM++;
                        }

                        break;
                    }

                case "SL":  // Signed long (32 Bit, 4 Bytes)
                    {
                        // Reference: DCIOM Standard 2009, PS 3.5: Data Structures and Encoding
                        // Table 7.1-2 
                        //  -------------------------------------
                        // | ValueLength | ValueField            |
                        //  -------------------------------------
                        // | 2 Bytes     | ValueLength x 4 Bytes |
                        //  -------------------------------------

                        if (aTagVREncodingMode == VREncodingMode.ExplicitVR)
                            aTag.ValueLength = GetLength_16Bit(aTagEndianEncodingMode);
                        else
                            aTag.ValueLength = GetLength_32Bit(aTagEndianEncodingMode);

                        aTag.StreamPosition = mBinaryReader.BaseStream.Position;

                        for (int i = 0; i < aTag.ValueLength; i += 4)
                        {
                            long Value = GetSignedInt_32Bit(aTagEndianEncodingMode);
                            aTag.ValueField += Value.ToString() + " ";
                            aTag.VM++;
                        }
                        break;
                    }

                case "SS":  // Signed short (16 Bit, 2 Bytes)
                    {
                        // Reference: DCIOM Standard 2009, PS 3.5: Data Structures and Encoding
                        // Table 7.1-2 
                        //  -------------------------------------
                        // | ValueLength | ValueField            |
                        //  -------------------------------------
                        // | 2 Bytes     | ValueLength x 2 Bytes |
                        //  -------------------------------------

                        if (aTagVREncodingMode == VREncodingMode.ExplicitVR)
                            aTag.ValueLength = GetLength_16Bit(aTagEndianEncodingMode);
                        else
                            aTag.ValueLength = GetLength_32Bit(aTagEndianEncodingMode);

                        aTag.StreamPosition = mBinaryReader.BaseStream.Position;

                        for (int i = 0; i < aTag.ValueLength; i += 2)
                        {
                            short Value = GetSignedShort_16Bit(aTagEndianEncodingMode);
                            aTag.ValueField += Value.ToString() + " ";
                            aTag.VM++;
                        }
                        break;
                    }

                case "FL":  // Floating Point Single (32 Bit, 4 Byte)
                    {
                        // Reference: DCIOM Standard 2009, PS 3.5: Data Structures and Encoding
                        // Table 7.1-2 
                        //  -------------------------------------
                        // | ValueLength | ValueField            |
                        //  -------------------------------------
                        // | 2 Bytes     | ValueLength x 4 Bytes |
                        //  -------------------------------------

                        if (aTagVREncodingMode == VREncodingMode.ExplicitVR)
                            aTag.ValueLength = GetLength_16Bit(aTagEndianEncodingMode);
                        else
                            aTag.ValueLength = GetLength_32Bit(aTagEndianEncodingMode);

                        aTag.StreamPosition = mBinaryReader.BaseStream.Position;

                        for (int i = 0; i < aTag.ValueLength; i += 4)
                        {
                            float Value = GetFloatingPointSingle_32Bit(aTagEndianEncodingMode);
                            aTag.ValueField += Value.ToString() + " ";
                            aTag.VM++;
                        }
                        break;
                    }

                case "FD":  // Floating Point Double (64 Bit, 8 Byte)
                    {
                        // Reference: DCIOM Standard 2009, PS 3.5: Data Structures and Encoding
                        // Table 7.1-2 
                        //  -------------------------------------
                        // | ValueLength | ValueField            |
                        //  -------------------------------------
                        // | 2 Bytes     | ValueLength x 8 Bytes |
                        //  -------------------------------------

                        if (aTagVREncodingMode == VREncodingMode.ExplicitVR)
                            aTag.ValueLength = GetLength_16Bit(aTagEndianEncodingMode);
                        else
                            aTag.ValueLength = GetLength_32Bit(aTagEndianEncodingMode);

                        aTag.StreamPosition = mBinaryReader.BaseStream.Position;

                        for (int i = 0; i < aTag.ValueLength; i += 8)
                        {
                            double Value = GetFloatingPointDouble_64Bit(aTagEndianEncodingMode);
                            aTag.ValueField += Value.ToString() + " ";
                            aTag.VM++;
                        }
                        break;
                    }

                case "DL":  // Special SQ related Data Elements Items:
                            //   - (FFFE,E000) Item
                            //   - (FFFE,E00D) Item Delimitation Item
                            //   - (FFFE,E0DD) Sequence Delimitation Item

                    aTag.ValueLength = GetLength_32Bit(aTagEndianEncodingMode);
                    aTag.StreamPosition = mBinaryReader.BaseStream.Position;
                    aTag.VM = 1;
                    break;
 
                default:

                    aTag.VR = "UN";
                    aTag.ValueLength = GetLength_32Bit(aTagEndianEncodingMode);
                    aTag.StreamPosition = mBinaryReader.BaseStream.Position;

                    aTag.ValueField = "???";
                    aTag.VM = 1;
                    mBinaryReader.BaseStream.Position += aTag.ValueLength;
                    break;
            }

            return aTag;
        }

        private string GetVR(UInt16 theGroupNumber, UInt16 theElementNumber, string theTag, PrivateCodeDictionary thePrivateCodeDictionary, VREncodingMode theTagVREncodingMode)
        {
            // For Tag 'Item (FFFE,E000)' return "Delimination"
            if (theTag.Equals("(FFFE,E000)"))
                return "DL";
            
            // For Tag 'Item Delimitation Item (FFFE,E00D)' return "Delimination"
            if (theTag.Equals("(FFFE,E00D)"))
                return "DL";

            // For Tag 'Sequence Delimitation Item (FFFE,E0DD)' return "Delimination"
            if (theTag.Equals("(FFFE,E0DD)"))
                return "DL";

            if (theTagVREncodingMode == VREncodingMode.ExplicitVR)
            {
                // Explicit VR Encoding (value representation type is contained in stream)
                // =======================================================================
                byte b4 = mBinaryReader.ReadByte();
                byte b5 = mBinaryReader.ReadByte();

                return string.Format("{0}{1}", (char)b4, (char)b5);
            }
            else
            {
                // Implicit VR Encoding (value representation type must be retrieved from dictionary)
                // ==================================================================================

                // For Tag 'Private Creator Code' return "Long String"
                if ((theGroupNumber % 2) == 1 && (theElementNumber <= 0xFF))
                    return "LO";

                // For 'Even Group Number' and 'Implicit VR Encoding', VR information has to come from public dictionary
                if ((theGroupNumber % 2) == 0)
                    return PublicDICOMDictionary.GetVR(theTag);

                // For 'Odd Group Number' and 'Implicit VR Encoding', VR information has to come from private dictionary
                if ((theGroupNumber % 2) == 1)
                    return thePrivateCodeDictionary.GetVR(theTag);
            }

            // Should never be reached...
            return "UN";
        }

        private string GetTagName(UInt16 theGroupNumber, UInt16 theElementNumber, string theTag, PrivateCodeDictionary thePrivateCodeDictionary)
        {
            // Tag 'Item (FFFE,E000)'
            if (theTag.Equals("(FFFE,E000)"))
                return "Item";

            // Tag 'Item Delimitation Item (FFFE,E00D)'
            if (theTag.Equals("(FFFE,E00D)"))
                return "Item Delimitation Item";

            // Tag 'Sequence Delimitation Item (FFFE,E0DD)'
            if (theTag.Equals("(FFFE,E0DD)"))
                return "Sequence Delimitation Item";

            // For 'Even Group Number', DICOM attribute name has to come from public dictionary
            if ((theGroupNumber % 2) == 0)
                return PublicDICOMDictionary.GetTagName(theTag);

            // For Tag 'Private Creator Code' return "Private Creator"
            if ((theGroupNumber % 2) == 1 && (theElementNumber <= 0xFF))
                return "Private Creator";

            // For 'Odd Group Number', DICOM attribute name has to come from private dictionary
            if ((theGroupNumber % 2) == 1)
                return thePrivateCodeDictionary.GetTagName(theTag);

            // Should never be reached...
            return "???";
        }

        private UInt16 GetLength_16Bit(EndianEncodingMode theTagEndianEncodingMode)
        {
            byte[] aBuffer = new byte[2];
            mBinaryReader.Read(aBuffer, 0, 2);

            if (theTagEndianEncodingMode == EndianEncodingMode.BigEndian)
                aBuffer = aBuffer.Reverse().ToArray();
            
            return BitConverter.ToUInt16(aBuffer, 0);
        }

        private UInt32 GetLength_32Bit(EndianEncodingMode theTagEndianEncodingMode)
        {
            byte[] aBuffer = new byte[4];
            mBinaryReader.Read(aBuffer, 0, 4);

            if (theTagEndianEncodingMode == EndianEncodingMode.BigEndian)
                aBuffer = aBuffer.Reverse().ToArray();

            return BitConverter.ToUInt32(aBuffer, 0);
        }

        private UInt16 GetUnsignedShort_16Bit(EndianEncodingMode theTagEndianEncodingMode)
        {
            byte[] aBuffer = new byte[2];
            mBinaryReader.Read(aBuffer, 0, 2);

            if (theTagEndianEncodingMode == EndianEncodingMode.BigEndian)
                aBuffer = aBuffer.Reverse().ToArray();

            return BitConverter.ToUInt16(aBuffer, 0);
        }

        private Int16 GetSignedShort_16Bit(EndianEncodingMode theTagEndianEncodingMode)
        {
            byte[] aBuffer = new byte[2];
            mBinaryReader.Read(aBuffer, 0, 2);

            if (theTagEndianEncodingMode == EndianEncodingMode.BigEndian)
                aBuffer = aBuffer.Reverse().ToArray();

            return BitConverter.ToInt16(aBuffer, 0);
        }

        private UInt32 GetUnsignedInt_32Bit(EndianEncodingMode theTagEndianEncodingMode)
        {
            byte[] aBuffer = new byte[4];
            mBinaryReader.Read(aBuffer, 0, 4);

            if (theTagEndianEncodingMode == EndianEncodingMode.BigEndian)
                aBuffer = aBuffer.Reverse().ToArray();

            return BitConverter.ToUInt32(aBuffer, 0);
        }

        private Int32 GetSignedInt_32Bit(EndianEncodingMode theTagEndianEncodingMode)
        {
            byte[] aBuffer = new byte[4];
            mBinaryReader.Read(aBuffer, 0, 4);

            if (theTagEndianEncodingMode == EndianEncodingMode.BigEndian)
                aBuffer = aBuffer.Reverse().ToArray();
            
            return BitConverter.ToInt32(aBuffer, 0);
        }

        private float GetFloatingPointSingle_32Bit(EndianEncodingMode theTagEndianEncodingMode)
        {
            byte[] aBuffer = new byte[4];
            mBinaryReader.Read(aBuffer, 0, 4);

            if (theTagEndianEncodingMode == EndianEncodingMode.BigEndian)
                aBuffer = aBuffer.Reverse().ToArray();

            return (float)BitConverter.ToSingle(aBuffer, 0);
        }

        private double GetFloatingPointDouble_64Bit(EndianEncodingMode theTagEndianEncodingMode)
        {
            byte[] aBuffer = new byte[8];
            mBinaryReader.Read(aBuffer, 0, 8);

            if (theTagEndianEncodingMode == EndianEncodingMode.BigEndian)
                aBuffer = aBuffer.Reverse().ToArray();
            
            return (float)BitConverter.ToDouble(aBuffer, 0);
        }
    }

    public class DICOMDataElement
    {
        public UInt16 GroupNumber { get; set; }
        public UInt16 ElementNumber { get; set; }
        public string Tag { get; set; }
        public string TagName { get; set; }
        public string VR { get; set; }
        public Int64  VM { get; set; }
        public Int64  ValueLength { get; set; }
        public string ValueField { get; set; }
        public Int64  StreamPosition { get; set; }

        public DICOMDataElement()
        {
            GroupNumber = 0;
            ElementNumber = 0;
            Tag = "";
            TagName = "";
            VR = "";
            VM = 0;
            ValueLength = -1;
            ValueField = "";
            StreamPosition = 0;
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Siemens Healthcare
Germany Germany
Currently working as a Requirement Engineer for Siemens Healthcare in Germany.

Comments and Discussions