Support for Metadata Working Group hierarchical keywords

Started by Mac2, February 23, 2012, 07:25:40 AM

Previous topic - Next topic

Mac2

Does ExifTool support already, or plans to support, the storage schema for keywords as outlined in the document

GUIDELINES FOR HANDLING IMAGE METADATA Version 2.0 from November 2010

This would create a fourth (sigh!) potential set of keywords an image file may contain, in addition to the classical IPTC keywords, XMP dc:subject, XMP and the proprietary lr:hierarchicalSubject keywords.

Phil Harvey

Yes.  ExifTool is up to date with the current MWG recommendations, and supports all of the new XMP MWG tags.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Mac2

Ah, very good. This was added since I last looked into the XMP/MWG tags... (Ehem  :-[ )

Are there any samples on how to set these tags from the command line / args file?
I could not find anything in the FAQ or here on the forum. A small example on how to set/retrieve something like


Animals
  |--Mammals*
     |--Cat*
     |--Dog


would be helpful (Mammals and Cat are assigned to the file).

My users often have very deep keyword hierarchies, especially for scientific and genealogical hierarchies. Maybe 6 to 20 levels deep.  I need to be able to map this into MWG keywords too. Also many of the "controlled vocabularies" you can purchase have more than four levels.
The ExifTool documentation states that it currently "unrolls keyword structures to an arbitrary depth of 4". What does this mean?

Currently I use Adobe's proprietary lr:hierarchicalKeywords tag and optionally flatten the keywords into IPTC:keywords and dc:subject using a configurable separator to retain the hierarchical information for non-XMP-aware applications and services. I would like to support the MWG keywords as a "super set" to cover all bases.

Phil Harvey

#3
Dealing with structures is fairly complex.  See the Writing section os the Structured Information documentation for some examples.  You can write using the structure syntax, or as individual flattened tags.

The arbitrary depth of 4 means that ExifTool will only write a 4-deep MWG keyword hierarchy.  It will read any depth, but beyond the 4th level the tag names will be automatically generated, and not very nice (like "KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenKeyword" for example).

A depth of 20 is fairly ridiculous.  The corresponding XMP would take 6 kB just to store a single keyword at this depth, and would look like this:

<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?>
<x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='Image::ExifTool 8.80'>
<rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>

<rdf:Description rdf:about=''
  xmlns:mwg-kw='http://www.metadataworkinggroup.com/schemas/keywords/'>
  <mwg-kw:Keywords rdf:parseType='Resource'>
   <mwg-kw:Hierarchy>
    <rdf:Bag>
     <rdf:li rdf:parseType='Resource'>
      <mwg-kw:Children>
       <rdf:Bag>
        <rdf:li rdf:parseType='Resource'>
         <mwg-kw:Children>
          <rdf:Bag>
           <rdf:li rdf:parseType='Resource'>
            <mwg-kw:Children>
             <rdf:Bag>
              <rdf:li rdf:parseType='Resource'>
               <mwg-kw:Children>
                <rdf:Bag>
                 <rdf:li rdf:parseType='Resource'>
                  <mwg-kw:Children>
                   <rdf:Bag>
                    <rdf:li rdf:parseType='Resource'>
                     <mwg-kw:Children>
                      <rdf:Bag>
                       <rdf:li rdf:parseType='Resource'>
                        <mwg-kw:Children>
                         <rdf:Bag>
                          <rdf:li rdf:parseType='Resource'>
                           <mwg-kw:Children>
                            <rdf:Bag>
                             <rdf:li rdf:parseType='Resource'>
                              <mwg-kw:Children>
                               <rdf:Bag>
                                <rdf:li rdf:parseType='Resource'>
                                 <mwg-kw:Children>
                                  <rdf:Bag>
                                   <rdf:li rdf:parseType='Resource'>
                                    <mwg-kw:Children>
                                     <rdf:Bag>
                                      <rdf:li rdf:parseType='Resource'>
                                       <mwg-kw:Children>
                                        <rdf:Bag>
                                         <rdf:li rdf:parseType='Resource'>
                                          <mwg-kw:Children>
                                           <rdf:Bag>
                                            <rdf:li rdf:parseType='Resource'>
                                             <mwg-kw:Children>
                                              <rdf:Bag>
                                               <rdf:li rdf:parseType='Resource'>
                                                <mwg-kw:Children>
                                                 <rdf:Bag>
                                                  <rdf:li rdf:parseType='Resource'>
                                                   <mwg-kw:Children>
                                                    <rdf:Bag>
                                                     <rdf:li rdf:parseType='Resource'>
                                                      <mwg-kw:Children>
                                                       <rdf:Bag>
                                                        <rdf:li rdf:parseType='Resource'>
                                                         <mwg-kw:Children>
                                                          <rdf:Bag>
                                                           <rdf:li rdf:parseType='Resource'>
                                                            <mwg-kw:Children>
                                                             <rdf:Bag>
                                                              <rdf:li rdf:parseType='Resource'>
                                                               <mwg-kw:Keyword>test</mwg-kw:Keyword>
                                                              </rdf:li>
                                                             </rdf:Bag>
                                                            </mwg-kw:Children>
                                                           </rdf:li>
                                                          </rdf:Bag>
                                                         </mwg-kw:Children>
                                                        </rdf:li>
                                                       </rdf:Bag>
                                                      </mwg-kw:Children>
                                                     </rdf:li>
                                                    </rdf:Bag>
                                                   </mwg-kw:Children>
                                                  </rdf:li>
                                                 </rdf:Bag>
                                                </mwg-kw:Children>
                                               </rdf:li>
                                              </rdf:Bag>
                                             </mwg-kw:Children>
                                            </rdf:li>
                                           </rdf:Bag>
                                          </mwg-kw:Children>
                                         </rdf:li>
                                        </rdf:Bag>
                                       </mwg-kw:Children>
                                      </rdf:li>
                                     </rdf:Bag>
                                    </mwg-kw:Children>
                                   </rdf:li>
                                  </rdf:Bag>
                                 </mwg-kw:Children>
                                </rdf:li>
                               </rdf:Bag>
                              </mwg-kw:Children>
                             </rdf:li>
                            </rdf:Bag>
                           </mwg-kw:Children>
                          </rdf:li>
                         </rdf:Bag>
                        </mwg-kw:Children>
                       </rdf:li>
                      </rdf:Bag>
                     </mwg-kw:Children>
                    </rdf:li>
                   </rdf:Bag>
                  </mwg-kw:Children>
                 </rdf:li>
                </rdf:Bag>
               </mwg-kw:Children>
              </rdf:li>
             </rdf:Bag>
            </mwg-kw:Children>
           </rdf:li>
          </rdf:Bag>
         </mwg-kw:Children>
        </rdf:li>
       </rdf:Bag>
      </mwg-kw:Children>
     </rdf:li>
    </rdf:Bag>
   </mwg-kw:Hierarchy>
  </mwg-kw:Keywords>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>
<?xpacket end='w'?>


But if you really want to do this, you may extend the ExifTool definition by overriding the MWG Keyword definition with a user-defined tag.  The config file would look like this:

my %sKeywordStruct20 = (
    STRUCT_NAME => 'KeywordStruct20',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
);
my %sKeywordStruct19 = (
    STRUCT_NAME => 'KeywordStruct19',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct20, List => 'Bag' },
);
my %sKeywordStruct18 = (
    STRUCT_NAME => 'KeywordStruct18',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct19, List => 'Bag' },
);
my %sKeywordStruct17 = (
    STRUCT_NAME => 'KeywordStruct17',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct18, List => 'Bag' },
);
my %sKeywordStruct16 = (
    STRUCT_NAME => 'KeywordStruct16',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct17, List => 'Bag' },
);
my %sKeywordStruct15 = (
    STRUCT_NAME => 'KeywordStruct15',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct16, List => 'Bag' },
);
my %sKeywordStruct14 = (
    STRUCT_NAME => 'KeywordStruct14',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct15, List => 'Bag' },
);
my %sKeywordStruct13 = (
    STRUCT_NAME => 'KeywordStruct13',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct14, List => 'Bag' },
);
my %sKeywordStruct12 = (
    STRUCT_NAME => 'KeywordStruct12',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct13, List => 'Bag' },
);
my %sKeywordStruct11 = (
    STRUCT_NAME => 'KeywordStruct11',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct12, List => 'Bag' },
);
my %sKeywordStruct10 = (
    STRUCT_NAME => 'KeywordStruct10',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct11, List => 'Bag' },
);
my %sKeywordStruct9 = (
    STRUCT_NAME => 'KeywordStruct9',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct10, List => 'Bag' },
);
my %sKeywordStruct8 = (
    STRUCT_NAME => 'KeywordStruct8',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct9, List => 'Bag' },
);
my %sKeywordStruct7 = (
    STRUCT_NAME => 'KeywordStruct7',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct8, List => 'Bag' },
);
my %sKeywordStruct6 = (
    STRUCT_NAME => 'KeywordStruct6',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct7, List => 'Bag' },
);
my %sKeywordStruct5 = (
    STRUCT_NAME => 'KeywordStruct5',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct6, List => 'Bag' },
);
my %sKeywordStruct4 = (
    STRUCT_NAME => 'KeywordStruct4',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct5, List => 'Bag' },
);
my %sKeywordStruct3 = (
    STRUCT_NAME => 'KeywordStruct3',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct4, List => 'Bag' },
);
my %sKeywordStruct2 = (
    STRUCT_NAME => 'KeywordStruct2',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct3, List => 'Bag' },
);
my %sKeywordStruct1 = (
    STRUCT_NAME => 'KeywordStruct1',
    NAMESPACE   => 'mwg-kw',
    Keyword   => { },
    Applied   => { Writable => 'boolean' },
    Children  => { Struct => \%sKeywordStruct2, List => 'Bag' },
);
%Image::ExifTool::UserDefined = (
    'Image::ExifTool::XMP::mwg_kw' => {
        Keywords => {
            Name => 'KeywordInfo',
            Struct => {
                STRUCT_NAME => 'KeywordInfo',
                NAMESPACE   => 'mwg-kw',
                Hierarchy => { Struct => \%sKeywordStruct1, List => 'Bag' },
            },
        },
        KeywordsHierarchy => { Name => 'HierarchicalKeywords', Flat => 1 },
        KeywordsHierarchyKeyword  => { Name => 'HierarchicalKeywords1', Flat => 1 },
        KeywordsHierarchyApplied  => { Name => 'HierarchicalKeywords1Applied', Flat => 1 },
        KeywordsHierarchyChildren => { Name => 'HierarchicalKeywords1Children', Flat => 1 },
        KeywordsHierarchyChildrenKeyword  => { Name => 'HierarchicalKeywords2', Flat => 1 },
        KeywordsHierarchyChildrenApplied  => { Name => 'HierarchicalKeywords2Applied', Flat => 1 },
        KeywordsHierarchyChildrenChildren => { Name => 'HierarchicalKeywords2Children', Flat => 1 },
        KeywordsHierarchyChildrenChildrenKeyword  => { Name => 'HierarchicalKeywords3', Flat => 1 },
        KeywordsHierarchyChildrenChildrenApplied  => { Name => 'HierarchicalKeywords3Applied', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildren => { Name => 'HierarchicalKeywords3Children', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenKeyword  => { Name => 'HierarchicalKeywords4', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenApplied  => { Name => 'HierarchicalKeywords4Applied', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildren => { Name => 'HierarchicalKeywords4Children', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenKeyword  => { Name => 'HierarchicalKeywords5', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenApplied  => { Name => 'HierarchicalKeywords5Applied', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildren => { Name => 'HierarchicalKeywords5Children', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenKeyword  => { Name => 'HierarchicalKeywords6', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenApplied  => { Name => 'HierarchicalKeywords6Applied', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildren => { Name => 'HierarchicalKeywords6Children', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenKeyword  => { Name => 'HierarchicalKeywords7', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenApplied  => { Name => 'HierarchicalKeywords7Applied', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildren => { Name => 'HierarchicalKeywords7Children', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenKeyword  => { Name => 'HierarchicalKeywords8', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenApplied  => { Name => 'HierarchicalKeywords8Applied', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildren => { Name => 'HierarchicalKeywords8Children', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenKeyword  => { Name => 'HierarchicalKeywords9', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenApplied  => { Name => 'HierarchicalKeywords9Applied', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildren => { Name => 'HierarchicalKeywords9Children', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenKeyword  => { Name => 'HierarchicalKeywords10', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenApplied  => { Name => 'HierarchicalKeywords10Applied', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildren => { Name => 'HierarchicalKeywords10Children', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenKeyword  => { Name => 'HierarchicalKeywords11', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenApplied  => { Name => 'HierarchicalKeywords11Applied', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildren => { Name => 'HierarchicalKeywords11Children', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenKeyword  => { Name => 'HierarchicalKeywords12', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenApplied  => { Name => 'HierarchicalKeywords12Applied', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildren => { Name => 'HierarchicalKeywords12Children', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenKeyword  => { Name => 'HierarchicalKeywords13', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenApplied  => { Name => 'HierarchicalKeywords13Applied', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildren => { Name => 'HierarchicalKeywords13Children', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenKeyword  => { Name => 'HierarchicalKeywords14', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenApplied  => { Name => 'HierarchicalKeywords14Applied', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildren => { Name => 'HierarchicalKeywords14Children', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenKeyword  => { Name => 'HierarchicalKeywords15', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenApplied  => { Name => 'HierarchicalKeywords15Applied', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildren => { Name => 'HierarchicalKeywords15Children', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenKeyword  => { Name => 'HierarchicalKeywords16', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenApplied  => { Name => 'HierarchicalKeywords16Applied', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildren => { Name => 'HierarchicalKeywords16Children', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenKeyword  => { Name => 'HierarchicalKeywords17', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenApplied  => { Name => 'HierarchicalKeywords17Applied', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildren => { Name => 'HierarchicalKeywords17Children', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenKeyword  => { Name => 'HierarchicalKeywords18', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenApplied  => { Name => 'HierarchicalKeywords18Applied', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildren => { Name => 'HierarchicalKeywords18Children', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenKeyword  => { Name => 'HierarchicalKeywords19', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenApplied  => { Name => 'HierarchicalKeywords19Applied', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildren => { Name => 'HierarchicalKeywords19Children', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenKeyword  => { Name => 'HierarchicalKeywords20', Flat => 1 },
        KeywordsHierarchyChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenChildrenApplied  => { Name => 'HierarchicalKeywords20Applied', Flat => 1 },
    },
);
1;  #end


However, to avoid infinite recursion, ExifTool has a sanity check on the maximum length of the tag ID for a structured tag.  The current limit is 150 characters, which is exceeded by the definition above.  I will increase this to 250 characters in the next release (ExifTool 8.80), and with this version the above config file will allow you to add ridiculously deep MWG keyword hierarchies.

- Phil

Edit: Added some missing flattened "Children" tags
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Mac2

Thank you very much, Phil!  :)
I will chew this through over the weekend and see if I can get it to work.

The format chosen by the MWG for keyword storage is, ah, not very compact. As we experience quite often, people are getting carried away with XML(P) and do not consider practical issues like storage efficiency or "outside of the imaging industry" real world problems. Given the member list of MWG this is only natural. But images are also used by non-photographers, who have very different requirements  ::)  Simple tags like "Family", "Uncle John" or "Country | United States" just won't do.

Taxonomies as used by scientists or company users are often (necessarily) very deep. Many of my users have hierarchies of 3,000 to 10,000 "keywords" with 6, 10 or more levels. Image get between 5 and 30 keywords assigned. Storing that in the MWG format will make the XMP record just explode. Sigh.

Even if the hierarchy is not that deep, images prepared for stock photo web sites often contain 20 to 50 keywords to make them search-engine friendly. Even if these keywords have only two or three levels, the MWG keyword structure will become really large. And most of it will be redundant structural information...

All this is of course neither your responsibility nor fault. Just wanted to rant a bit about the metadata madness we have to deal with these days. Four (!) sets of keywords in files, and all have to be somehow kept in synch. And then such a    loquacious MWG keyword storage schemata.

As before, thank you for ExifTool. Without it, the battle would already be lost  :)

sgbotsford

I can see two solutions to this:
1.  For standard hierarchies -- eg. a museum collection, a scientific discipline, you have a recognized controlled vocabulary that is done by a hierarchy.  In this collection the keywords are an index in the form of the name of a CV, and an index number.  Some organization, such as the MWG, or possibly the Dublin Core group becomes the registrar of the names of controlled vocabularies.

Thus instead of keywording by using Sports->Soccer->Playoffs->1974->Manchester United
it would keyworded as SWA:11973  meaning that the user used the Sports WRiters of America set of keywords, entry 11973.

If the program isn't familiar with SWA it has to look up at the registration where the canonical repository for the SWA keyword catalog is, download that, and do the lookups.

2.  For non-standard hierarchies, the entire path is stored, along with a defined separator.  Non-standard hierarchies are unlikely to be more than a few levels deep although 4 seems a bit shallow.  6 seems reasonable.