LaurelBridge.DCF.Examples.CustomTagDictionary Namespace

DICOM Connectivity Framework V3.4
The CustomTagDictionary example demonstates how to use DCF to customize the DICOM and DICONDE tag dictionary.

Public classProgram
This example demonstrates how to customize the default tag dictionary.

When DCF needs to look up a value representation (VR) in the implicit transfer syntax, it uses the TagLookup singleton. If no dictionary is configured for the application, the default dictionary is loaded from a resource in the libary.

The default dictionary may also be extended via the use of the custom_tags file, where users may automatically load custom tag extensions upon construction of the default DataDictionary. This facility is used to support the tags and naming conventions for DICONDE.

Extending the data dictionary is useful in several scenarios:

  • Adding to tags in the standard dictionary for items newly added by the DICOM standard.
  • Adding definitions of private tags for vendor specific private tags.
  • Poorly encoded elements may be able to be better processed by changing the VR of a tag in the dictionary.
  • Overriding a tag allows you to change the name or description of an element in the log output.

CustomTagDictionary Sample Code
public class Program
    /// <summary>
    /// Main entry point for CustomTagDictionary.
    /// </summary>
    public static void Main()
            File.Copy("dicom/custom_tags.txt", "dicom/custom_tags");
            File.Copy("dicom/ext_data_dictionary.txt", "dicom/ext_data_dictionary");

            Console.WriteLine("Default tag lookup prior to dictionary extension:");
            // We do this to prevent the custom_tags dictionary from automatically loading in order to show the before and after
            // when loading the custom tags dictionary
            DataDictionary.TagLookup = TagLookup.CreateDefault();
            Console.WriteLine("(0010,0010) Name: ({0})", DataDictionary.GetNameForTag(Tags.PatientName));
            Console.WriteLine("(0010,0020) Name: ({0})", DataDictionary.GetNameForTag(Tags.PatientID));
            Console.WriteLine("(0008,0090) Name: ({0})", DataDictionary.GetNameForTag(Tags.ReferringPhysicianName));

            Console.WriteLine("{0}Private tag lookup prior to dictionary extension:", Environment.NewLine);
            Console.WriteLine("(0019,1000) Name: ({0})", DataDictionary.GetNameForTag(PrivateCreatorTags.MyCount, "PRIVATE CREATOR"));
            Console.WriteLine("(0019,1001) Name: ({0})", DataDictionary.GetNameForTag(PrivateCreatorTags.MyOtherID, "PRIVATE CREATOR"));
            Console.WriteLine("(0019,1002) Name: ({0})", DataDictionary.GetNameForTag(PrivateCreatorTags.MyDate, "PRIVATE CREATOR"));
            Console.WriteLine("(0019,1003) Name: ({0})", DataDictionary.GetNameForTag(PrivateCreatorTags.MyPersonName, "PRIVATE CREATOR"));

            // Override and extend the default tag dictionary
            DataDictionary.TagLookup = CreateExtendedProvider();

            Console.WriteLine("{0}Tag lookup after dictionary extension and override:", Environment.NewLine);
            Console.WriteLine("(0010,0010) Name: ({0})", DataDictionary.GetNameForTag(NDETags.ComponentName));
            Console.WriteLine("(0010,0020) Name: ({0})", DataDictionary.GetNameForTag(NDETags.ComponentIDNumber));
            Console.WriteLine("(0008,0090) Name: ({0})", DataDictionary.GetNameForTag(NDETags.ComponentOwnerName));

            // Note the ordering of the override and private dictionaries as they were returned by the CreateExtendedProvider method.
            // The override dictionary comes first in the list, which means the override dict will be searched first, and the values returned.
            // For instance, for the private tag (0019,1003), indicates the name 'My Person Name' in the private dictionary and the name
            // 'My Overridden Person Name' in the override dict. The expected value returned from the lookup will be the overriden value.
            Console.WriteLine("{0}Private tag lookup after dictionary extension and override:", Environment.NewLine);
            Console.WriteLine("(0019,1000) Name: ({0})", DataDictionary.GetNameForTag(PrivateCreatorTags.MyCount, "PRIVATE CREATOR"));
            Console.WriteLine("(0019,1001) Name: ({0})", DataDictionary.GetNameForTag(PrivateCreatorTags.MyOtherID, "PRIVATE CREATOR"));
            Console.WriteLine("(0019,1002) Name: ({0})", DataDictionary.GetNameForTag(PrivateCreatorTags.MyDate, "PRIVATE CREATOR"));
            Console.WriteLine("(0019,1003) Name: ({0})", DataDictionary.GetNameForTag(PrivateCreatorTags.MyPersonName, "PRIVATE CREATOR"));

            // Causes the recreation of the default tags dictionary including the custom_tags overrides from disk
            DataDictionary.TagLookup = null;
            Console.WriteLine("{0}Custom tags dictionary extention read from disk:{0}{1}{0}", Environment.NewLine, ReadCustomTagsDictFromFile());
            Console.WriteLine("Dictionary entry after custom_tags extension:");
            Console.WriteLine("(0010,0010) Name: ({0})", DataDictionary.GetNameForTag(Tags.PatientName));

            Console.WriteLine("{0}Name returned when the private creator id is not specified for the private tag lookup:", Environment.NewLine);
            Console.WriteLine("(0019,1000) Name: ({0})", DataDictionary.GetNameForTag(PrivateCreatorTags.MyCount));

            // Load a new dictionary from disk that uses the legacy CFGGroup format
            Console.WriteLine("{0}Legacy Dictionary Expansion:", Environment.NewLine);
            DataDictionary.TagLookup = LegacyExtendTagDictionary();
            Console.WriteLine("(0029,1234) Name: ({0})", DataDictionary.GetNameForTag(0x00291234));
            Console.WriteLine("(0039,1234) Name: ({0})", DataDictionary.GetNameForTag(0x00391234));

            // Demonstrate session based tag dictionary extensions
            Console.WriteLine("{0}Session based custom tags dictionary extension:", Environment.NewLine);
            DicomSessionSettings ss = new DicomSessionSettings()
                // The explicit transfer syntax codecs will perform a lookup for standard (non-private) tags.
                // This allows overriding the VR for explicit transfer syntaxes and should be used with caution.
                LookupStandardVrInExplicit = true
            string sessionSettingsPNOverride = "(0010,0010)|Patient's Name|PatientName|CS|1|";
            Console.WriteLine("{0}", sessionSettingsPNOverride);
            ss.TagLookup = TagLookup.CreateFromStream("CSPatientName", new MemoryStream(Encoding.UTF8.GetBytes(
                sessionSettingsPNOverride)), true);
            string datasetPath = "mr-knee.dcm";
            DicomDataSet ds = ReadDatasetFromFile(datasetPath, ss);
            DicomElement e = ds.FindElement(Tags.PatientName);
            Console.WriteLine("{0}Dicom PN element after dictionary extension using session settings:{0}{1}", Environment.NewLine, e);

            // Demonstrate how to provide a custom prefix for UID generation
            Console.WriteLine("{0}Provide a custom UID prefix for UID generation:", Environment.NewLine);
            string customOrgPrefix = "";
            string customSysPrefix = "";
            Console.WriteLine("Custom Org Prefix: {0}", customOrgPrefix);
            Console.WriteLine("Custom Sys Prefix: {0}", customSysPrefix);
            DataDictionary.UidFactory = new UidFactory(customOrgPrefix, customSysPrefix);
            Console.WriteLine("New Generated UID: {0}", DataDictionary.CreateUid());
            DataDictionary.UidFactory = null;
            Console.WriteLine("Generated UID after restoring default uid factory: {0}", DataDictionary.CreateUid());
        catch (Exception e)
            Console.WriteLine("Exception caught during execution: {0}", e);
            // Remove the custom tags and extended data dictionary files to prevent other examples from automatically loading them

        if (System.Diagnostics.Debugger.IsAttached)
            Console.Write("Press any key to continue . . . ");

    /// <summary>
    /// Create an ITagEntryProvider with 3 dictionaries, the override, the private and the standard dictionary.
    /// </summary>
    /// <returns>the ITagEntryProvider</returns>
    private static ITagEntryProvider CreateExtendedProvider()
        // typically, these dictionaries will be in a file you can load using CreateFromFile.  We use CreateFromStream here:

        // the private dictionary defines elements for any private group with creator id 'PRIVATE CREATOR'
        TagLookup privateDict = TagLookup.CreateFromStream("private dictionary", new MemoryStream(Encoding.UTF8.GetBytes(
            "(0019,xx00,PRIVATE CREATOR)|My Count|MyCount|US|1|\n" +
            "(0019,xx01,PRIVATE CREATOR)|My Other ID|MyOtherID|SH|1|\n" +
            "(0019,xx02,PRIVATE CREATOR)|My Date and Time|MyDate|TM|1|\n" +
            "(0019,xx03,PRIVATE CREATOR)|My Person Name|MyPersonName|PN|1|\n")), true);

        // this override dictionary overrides some tags that are used by DICONDE, as well as adding another private creator tag
        TagLookup overrideDict = TagLookup.CreateFromStream("override dictionary", new MemoryStream(Encoding.UTF8.GetBytes(
            "(0010,0010)|Component Name|ComponentName|PN|1|\n" +
            "(0010,0020)|Component ID Number|ComponentIDNumber|LO|1|\n" +
            "(0008,0090)|Component Owner Name|ComponentOwnerName|PN|1|\n" +
            "(0019,xx03,PRIVATE CREATOR)|My Overridden Person Name|MyPersonName|PN|1|\n")), true);

        // create the TagEntryProvider with the override, private and standard dictionaries, in that search order.
        return new TagEntryProvider(true, overrideDict, privateDict);

    /// <summary>
    /// Reads the text from the custom_tags dictionary extensions from the file 'dicom\\custom_tags'.
    /// </summary>
    /// <returns>The custom tags dictionary text read from file.</returns>
    private static string ReadCustomTagsDictFromFile()
        string customTagsDictPath = Path.Combine(Framework.ConfigurationPath, "dicom\\custom_tags");
        return File.ReadAllText(customTagsDictPath);

    /// <summary>
    /// Extend the default tag dictionary using the legacy CFGGroup format.
    /// </summary>
    /// <returns>The default tag dictionary extended to include the private tag entries from the indicated CFGGroup.</returns>
    private static ITagEntryProvider LegacyExtendTagDictionary()
        // load the legacy extended dictionary using the configuration file deployed by the visual studio project
        const string cfgPath = "dicom\\ext_data_dictionary";
        CFGGroup cfg = CFGDB.loadGroupFromFile(cfgPath);
        Console.WriteLine("CFGGroup: {0}{1}", Environment.NewLine, cfg);
        TagLookup tagLookup = TagLookup.CreateFromCfgGroup(cfg);
        return new TagEntryProvider(true, tagLookup);

    /// <summary>
    /// Reads the Dicom dataset header only from the given input file path.
    /// </summary>
    /// <param name="inputPath">The path of the Dicom dataset on disk.</param>
    /// <param name="sessionSettings">Session settings to use when reading the dataset.</param>
    /// <returns>The Dicom dataset read from file.</returns>
    private static DicomDataSet ReadDatasetFromFile(string inputPath, DicomSessionSettings sessionSettings)
        using (DicomFileInput dfi = new DicomFileInput(inputPath, sessionSettings))
            return dfi.ReadDataSetNoPixels();

    /// <summary>
    /// Helper class to manage DICONDE override tag constants.
    /// </summary>
    static class NDETags
        public const int ComponentName = Tags.PatientName;
        public const int ComponentIDNumber = Tags.PatientID;
        public const int ComponentOwnerName = Tags.ReferringPhysicianName;

    /// <summary>
    /// Helper class to manage private tag constants.
    /// </summary>
    static class PrivateCreatorTags
        public const int MyCount = 0x00191000;
        public const int MyOtherID = 0x00191001;
        public const int MyDate = 0x00191002;
        public const int MyPersonName = 0x00191003;