# What's *DcmDict* **DcmDict** is a Ruby gem [(*dcm\_dict*)][12] to handle in a simple way the *Data Dictionary* defined within the DICOM® Standard [1] such as DICOM Data Elements or DICOM Unique Identifiers. # Why *DcmDict* With the new releases from year 2014 DICOM documents are available in different format including one particularly interesting, the *DocBook* XML version. As confirmed in the DICOM® Standard [web site][10] where each part is published in [multiple formats][11] and XML format is defined by: >"... XML – good for machine readability, e.g. self-updating validators ..." This is great news for anyone involved in the study of the standard ! Starting from this thing was born the idea of trying to automate the extraction of data from XML documents to make available the information in a simple way that simplifies the study of the DICOM standard, in the hope this idea will be useful and inspiration. # *DcmDict* features The library involve *String*, *Array* and *Symbol* class as *refinements* to access to DICOM information. In this way is possible to *play* with the 'Patient's Birth Date' tag defined as (0010,0030) by: ```ruby > "(0010,0030)".tag_name => "Patient's Birth Date" > 'PatientBirthDate'.tag_vr => [:DA] > "Patient's Birth Date".tag_vm => ["1"] > :patient_birth_date.tag => [16, 48] > [16,48].tag_key => "PatientBirthDate" > [16, 48].tag_group => 16 > '00100030'.tag_element => 48 > :patient_birth_date.tag_group => 16 > "Patient's Birth Date".tag_element => 48 ``` as well for UIDs ```ruby > '1.2.840.10008.1.2'.uid_name => "Implicit VR Little Endian: Default Transfer Syntax for DICOM" > '1.2.840.10008.1.2'.uid_type => :transfer_syntax > "Implicit VR Little Endian: Default Transfer Syntax for DICOM".uid_value => "1.2.840.10008.1.2" > "Verification SOP Class".uid_value => "1.2.840.10008.1.1" > "1.2.840.10008.1.1".uid_type => :sop_class > "Verification".uid => "1.2.840.10008.1.1" > :verification.uid => "1.2.840.10008.1.1" ``` let see all features in detail: ## Library usage The library involve **Ruby Refinements** so use it is simple, it is necessary to include the specific *using directive*: ```ruby using DcmDict::Refine::StringRefine ``` for the String object, ```ruby using DcmDict::Refine::ArrayRefine ``` for the Array object and ```ruby using DcmDict::Refine::SymbolRefine ``` for the Symbol object. That's it. **Note**: It is possible to use also the the *monkey patching* way, this is a *deprecated* utilization but possible, and, may be, useful technique in certain contexts or for some particular test. For the *monkey patching* is possible to include some specific *common modules* in the class you want to use as "keyword": there are two base modules *DcmDict::Refine::DataElementRefine* and *DcmDict::Refine::UidRefine*. For example in the String class will have the code: ```ruby class String include DcmDict::Refine::DataElementRefine include DcmDict::Refine::UidRefine end ``` *The main way remains **Ruby Refinements***. ### Rubies compatibility **MRI Ruby**: from version _**v0.51.0**_ gemspec are changed: the minimum required version is _**3.0**_ in order to manage the refinements changes that occurred in version _**3.2**_: - The include/extend in refinements never worked (ref [Bug #17429][13]) - import_methods is limited in functionality (ref [class Refinement][14]) **Other Rubies**: the other considered rubies is only [Rubinius][4] checked at version _**v2.2.10**_. **Test Note**: In order to try to ensure a minimum compatibility there is a file under *lib/dcm_dict/rubies/* called *rb_ext.rb* able to *mask*/*simulates* the refinements by [refine gem][5] and add the required *bit_length* method to *Fixnum* class by [backports gem][6]. This sort of extension is not loaded by default but only into *spec files* for the *'rbx' Ruby Engine*. See *spec_helper.rb* and *rb_ext.rb* for details. ## Data Element data in detail Any Data Element feature can be accessed from a *String*, *Array* or *Symbol* objects. For these objects is possible to access to Tag (group/element), Name, Keyword, Value Representation (VR) and Value Multiplicity (VM) for any single Data Element. The main methods supported are: | Method | Note | Type | | | :---------------- | :-------------------------------: | :-----------------: | :--------------------------------: | | **tag** | tag as Array | Array of two Fixnum | (alias of *tag_ary*) | | **tag_name** | name of tag | String | | | **tag_keyword** | keyword | String | | | **tag_key** | keyword | String | | | **tag_vr** | Value Representation | Array of Symbol | | | **tag_vm** | Value Multiplicity | Array of Symbol | | | **tag_ps** | tag as string (as standard doc) | String | | | **tag_ary** | tag as Array | Array of two Fixnum | | | **tag_sym** | tag as Symbol | Symbol | | | **tag_ndm** | tag as in Native DICOM Model | String | | | **tag_str** | tag as string (similar to tag_ps) | String | | | **tag_note** | tag note | String | | | **tag_multiple?** | is a multiple tag ? | boolean | (useful for 'multi tag' attribute) | each data element is indexed by these fields: | Field | Type | | :----------- | :----: | | **tag_key** | String | | **tag_ps** | String | | **tag_ary** | Array | | **tag_sym** | Symbol | | **tag_ndm** | String | | **tag_str** | String | | **tag_name** | String | By these fields is possible to access to data element informations. Consider the case of the tag (0010,1005) (*Patient's Birth Name*), the primary fields are: **'(0010,1005)'**, **"Patient's Birth Name"**, **'PatientBirthName'**, **:patient_birth_name**, **'00101005'** and **[0x0010,0x1005]** - As String key you may use the standard tag format **'(0010,1005)'**, the tag name **'Patient's Birth Name'**, the tag keyword **'PatientBirthName'** or the Native DICOM Format tag **'00101005'** - As Array key you may use the numeric array **[0x0010,0x1005]** - As Symbol key you may use **:patient_birth_name** ### example: Consider the previous case of tag (0010,1005), the code: ```ruby puts "Patient's Birth Name".tag_ps.inspect puts [0x0010,0x1005].tag_name.inspect puts '(0010,1005)'.tag_key.inspect puts :patient_birth_name.tag_vr.inspect puts 'PatientBirthName'.tag_vm.inspect puts '00101005'.tag.inspect puts '(0010,1005)'.tag_ary.inspect puts '00101005'.tag_sym.inspect puts 'PatientBirthName'.tag_ndm.inspect puts "Patient's Birth Name".tag_str.inspect puts [0x0010,0x1005].tag_note.inspect puts [0x0010,0x1005].tag_group.inspect puts :patient_birth_name.tag_element.inspect ``` will produce ```ruby "(0010,1005)" "Patient's Birth Name" "PatientBirthName" [:PN] ["1"] [16, 4101] [16, 4101] :patient_birth_name "00101005" "(0010,1005)" "" 16 4101 ``` ### Multiple Tag The library also manage 'multiple tags' such as *(0020,31XX)* (*Source Image IDs*). As an example for this data element the default tag in array form is [0x0020,0x3122] and all fields are expressed as: | Method/field | Value | | :---------------- | :----------------: | | **tag** | [0x0020,0x3122] | | **tag_name** | "Source Image IDs" | | **tag_keyword** | 'SourceImageIDs' | | **tag_key** | 'SourceImageIDs' | | **tag_vr** | [:CS] | | **tag_vm** | ["1-n"] | | **tag_ps** | '(0020,31XX)' | | **tag_ary** | [0x0020,0x3122] | | **tag_sym** | :source_image_ids | | **tag_ndm** | '00203122' | | **tag_str** | '(0020,3122)' | | **tag_note** | 'RET' | | **tag_multiple?** | true | [0x0020,0x3122] is the default value for group/element pair (*[32, 12578]*), but any valid pair of numbers for this tag will be refers that information: ```ruby > '(0020,31XX)'.tag_name => "Source Image IDs" > '(0020,31XX)'.tag_ndm => "00203122" > "SourceImageIDs".tag_ary => [32, 12578] > '(0020,3178)'.tag_name => "Source Image IDs" [0x0020,0x3146].tag_sym => :source_image_ids > '00203120'.tag_key => "SourceImageIDs" > [0x0020,0x3138].tag_vr => [:CS] > '(0020,3144)'.tag_note => "RET" ``` and so on. ## UID data in detail Any UID features can be accessed from a *String* or a *Symbol* object. ### UID Values For these objects is possible to access to value, name and type values for any single uid. Main methods supported are: | Method | Note | Type | | | :------------ | :----------------------: | :----: | :--------------------: | | **uid** | value of uid | String | (alias of *uid_value*) | | **uid_value** | value of uid | String | | | **uid_name** | name of uid | String | | | **uid_key** | keyword of uid | String | | | **uid_sym** | keyword as symbol of uid | Symbol | | | **uid_type** | type of uid | Symbol | | each uid is indexed by: | Field | Type | | :------------ | :----- | | **uid_value** | String | | **uid_name** | String | | **uid_key** | String | | **uid_sym** | Symbol | From these fields is possible to access to all uid informations. Consider the case of the uid *1.2.840.10008.1.1* (*Verification SOP Class*), the primary fields are the strings **'1.2.840.10008.1.1'**, **"Verification SOP Class"**, **"Verification"**. - As key you may use the standard value **'1.2.840.10008.1.1'**, the uid name **"Verification SOP Class"**, the uid keyword **"Verification"** and the symbol **:verification** For this uid all fields are expressed as: | Method/field | Value | | :------------ | :----------------------: | | **uid** | '1.2.840.10008.1.1' | | **uid_value** | '1.2.840.10008.1.1' | | **uid_name** | "Verification SOP Class" | | **uid_key** | "Verification" | | **uid_sym** | :verification | | **uid_type** | :sop_class | ### example: ```ruby > "Verification SOP Class".uid_value => "1.2.840.10008.1.1" > "1.2.840.10008.1.1".uid_type => :sop_class > "1.2.840.10008.1.1".uid_name => "Verification SOP Class" > "Verification SOP Class".uid => "1.2.840.10008.1.1" > "Verification".uid => "1.2.840.10008.1.1" > :verification.uid => "1.2.840.10008.1.1" ``` ## How data is extracted The library also contains a script (*dcm_dict_converter.rb* in the *bin* folder) able to download the xml documents and extracts the source data in a *Ruby compatible format*. The script produces the conversion in the *stdout* so for example is possible to extract the Tag Values by: ```ruby dcm_dict_converter.rb tag > /tmp/tag-dict.rb ``` Note: the script use the [Nokogiri][3] as XML parser if installed as gem, otherwise the standard *REXML*. Check the file for other details. ## Note Current library version is aligned to *DICOM Base Standard* **2024a** ## Install $ gem install dcm_dict *Note for Windows users*: may be that it is installed a previous version of Ruby 2.1, if [RubyInstaller][7] does not provide the official release for the supported version it's still possible to make it manually by clone/download the ['git' version of RubyInstaller][8] and execute the rake command ```rake ruby21``` or equivalent (see RubyInstaller doc for details). ## What's next The main idea is that *dictionary* should include only fixed data defined in the DICOM standard to encode and then make it available in easy way. There are many possible *candidates* for inclusion in the *dictionary*, here are some *"ideas"*: - Directory Record type data - Well-known Frames of Reference UID Values - Context Group UID Values - Standard Color Palettes UID Values - IOD definition data - ... ## Contributing TBD ## License Copyright (C) 2014-2024 Enrico Rivarola. See the `LICENSE` and `COPYING` files for license details. ## Ruby Gem Ruby gem **dcm\_dict** on [RubyGems][9] at [rubygems.org/gems/dcm_dict](https://rubygems.org/gems/dcm_dict) ## Source Code Main source code repository on github at [henrythebuilder/dcm_dict](https://github.com/henrythebuilder/dcm_dict)

  1. DICOM® is the registered trademark of the National Electrical Manufacturers Association for its standards publications relating to digital communications of medical information. ↩︎

[1]: #fn1-dcm-trademark [dcm1]: http://medical.nema.org/dicom/ "DICOM Homepage" [2]: http://www.dclunie.com/dicom-status/status.html "DICOM Standard Status" [3]: http://nokogiri.org/ "Nokogiri" [4]: http://rubini.us/ "Rubinius" [5]: https://rubygems.org/gems/refine "Refine gem at RubyGems.org" [6]: https://rubygems.org/gems/backports "Backports gem at RubyGems.org" [7]: http://rubyinstaller.org/ "The easy way to install Ruby on Windows" [8]: https://github.com/oneclick/rubyinstaller "RubyInstaller for Windows" [9]: https://rubygems.org/ [10]: http://www.dicomstandard.org/ [11]: http://www.dicomstandard.org/current/ [12]: https://rubygems.org/gems/dcm_dict [13]: https://bugs.ruby-lang.org/issues/17429 [14]: https://docs.ruby-lang.org/en/3.2/Refinement.html