Project

General

Profile

Adding new XMP namespaces

Added by Benjamin H. over 7 years ago

Hi,

I want to use exiv2 (via pyexiv2) to read and write currently not supported XMP namespaces. Can you give me some hints, where I have to extend exiv2 or how I can help you to extend the missing namespaces in the library? I want to use the Metadata Working Group Region Schema (see http://www.metadataworkinggroup.org/pdf/mwg_guidance.pdf - Image Region Metadata) and the Microsoft Photo Region Information (see http://msdn.microsoft.com/en-us/library/ee719905%28VS.85%29.aspx or http://metability.editme.com/mmf-scavenger-xmp-schemaex-MSPeopleTagging.
I guess it also has to be done some work at the python wrapper, but extending the c++ lib would be the first thing to do.
How can I help to implement these?


Replies (26)

RE: Adding new XMP namespaces - Added by Andreas Huggel over 7 years ago

Hi Benjamin,

Thanks for your offer!

You can actually use exiv2 (and pyexiv2, I presume) also for XMP namespaces that it doesn't know of. Just make sure you register the namespace before use.

Adding properties of a new namespace to the library requires just a few changes. (I don't know if it also requires changes in pyexiv2.) The easiest way to go about it is to use an existing namespace for guidance, e.g., look for occurrences of "kipi" and "Kipi" and make similar changes:

In src/properties.cpp:
- add a XmpPropertyInfo array with the reference data for the new properties
- add an entry for the namespace to xmpNsInfo[]

In src/xmp.cpp:
- register the new namespace with the XMP-SDK

For the documentation, make changes similar to these:
http://dev.exiv2.org/projects/exiv2/repository/diff?rev=2248&rev_to=2247

Test (try to set a new property without registering the namespace, e.g., using the exiv2 command line tool) and when you're happy with the change send me your patch and I'll check it in.

Andreas

RE: Adding new XMP namespaces - Added by Benjamin H. over 7 years ago

Ok, I will try on that (the face tagging only at first). For Microsoft Photo 1.2 RegionInfo this might be simply. I will have to look for the MWG Regions Schema, beacause it is less documented and more nested Tags.
I have to add namespaces and entries to exiv2, not only registring a namespace via exvi2, because pyexiv2 determines the tag types by asking the exiv2 lib. Otherwise one only can insert simple XmpText values.
What the different entries in the XmpPropertyInfo and in the entry in xmpNsInfo stand for? For some values I am not sure how to define for example bags which contain other complex/struct elements.
I will try and post my results here.

For later tests/comparison I now include some XMP examples:

MP:RegionInfo by WLPG

  <MP:RegionInfo rdf:parseType="Resource">
    <MPRI:Regions>
     <rdf:Bag>
      <rdf:li
       MPReg:Rectangle="0.661608, 0.244310, 0.130501, 0.261002" 
       MPReg:PersonDisplayName="Baby Tux"/>
      <rdf:li
       MPReg:Rectangle="0.205615, 0.226100, 0.130501, 0.261002" 
       MPReg:PersonDisplayName="Baby Gnu"/>
     </rdf:Bag>
    </MPRI:Regions>
   </MP:RegionInfo>

MP:RegionInfo as described at MSDN

  <MP:RegionInfo>
    <rdf:Description xmlns:MPRI="http://ns.microsoft.com/photo/1.2/t/RegionInfo#">
      <MPRI:Regions>
        <rdf:Bag>
          <rdf:li>
            <rdf:Description xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/RegionInfo#>
              <MPReg:Rectangle>0.790650, 0.441734, 0.209350, 0.279133</MPReg:Rectangle>
              <MPReg:PersonDisplayName>John Doe</MPReg:PersonDisplayName>
              <MPReg:PersonEmailDigest>2FD4E1C67A2D28FCED849EE1BB76E7391B93EB13</MPReg:PersonEmailDigest>
              <MPReg:PersonLiveIdCID>1234567890123456789</MPReg:PersonLiveIdCID>
             </rdf:Description>
           </rdf:li>
           <rdf:li>
             <rdf:Description xmlns:MPRI="http://ns.microsoft.com/photo/1.2/t/RegionInfo#>
                <MPReg:Rectangle>0.222656, 0.302083, 0.378906, 0.505208</MPReg:Rectangle>
                <MPReg:PersonDisplayName>Jane Doe</MPReg:PersonDisplayName>
             </rdf:Description>
           </rdf:li>
           <!-- Addition Regions -->
           ...
           <rdf:li>...</rdf:li>          
        </rdf:Bag>
      </MPRI:Regions></rdf:Description>
    </MP:RegionInfo>

MWG:Regions by Picasa 3.9

   <mwg-rs:Regions rdf:parseType="Resource">
    <mwg-rs:AppliedToDimensions
     stDim:w="1600" 
     stDim:h="800" 
     stDim:unit="pixel"/>
    <mwg-rs:RegionList>
     <rdf:Bag>
      <rdf:li>
       <rdf:Description
        mwg-rs:Name="Baby Tux" 
        mwg-rs:Type="Face">
       <mwg-rs:Area
        stArea:x="0.72625" 
        stArea:y="0.3775" 
        stArea:w="0.125" 
        stArea:h="0.25" 
        stArea:unit="normalized"/>
       </rdf:Description>
      </rdf:li>
      <rdf:li>
       <rdf:Description
        mwg-rs:Name="Baby Gnu" 
        mwg-rs:Type="Face">
       <mwg-rs:Area
        stArea:x="0.275312" 
        stArea:y="0.409375" 
        stArea:w="0.164375" 
        stArea:h="0.28125" 
        stArea:unit="normalized"/>
       </rdf:Description>
      </rdf:li>
     </rdf:Bag>
    </mwg-rs:RegionList>
   </mwg-rs:Regions>

mwg-rs:Regions as described by MWG

<mwg-rs:Regions rdf:parseType=“Resource”>
 <mwg-rs:AppliedToDimensions stDim:w=“4288” stDim:h=“2848” stDim:unit=“pixel”/> <mwg-rs:RegionList>
  <rdf:Bag>
   <!-- Simple example for face detection -->
   <rdf:li rdf:parseType=“Resource”> 
    <mwg-rs:Area stArea:x=“0.5” stArea:y=“0.5” stArea:w=“0.06” stArea:h=“0.09” stArea:unit=“normalized”/> 
    <mwg-rs:Type>Face</mwg-rs:Type> 
    <mwg-rs:Title>John Doe</mwg-rs:Title>
   </rdf:li>
   <!-- Simple example for pet detection -->
   <rdf:li rdf:parseType=“Resource”> 
    <mwg-rs:Area stArea:x=“0.5” stArea:y=“0.5” stArea:w=“0.06” stArea:h=“0.09” stArea:unit=“normalized”/> 
    <mwg-rs:Type>Pet</mwg-rs:Type> 
    <mwg-rs:Title>Fido</mwg-rs:Title> 
    <mwg-rs:Description>Fido looks happy!</mwg-rs:Description>
   </rdf:li>
   <!-- Metadata applied to a specific region, defined locally -->
   <rdf:li rdf:parseType=“Resource”> 
    <mwg-rs:Area stArea:x=“0.5” stArea:y=“0.5” stArea:w=“0.003” stArea:h=“0.002” stArea:unit=“normalized”/> 
    <mwg-rs:Type>Focus</mwg-rs:Type> 
    <mwg-rs:FocusUsage>EvaluatedUsed</mwg-rs:FocusUsage>
   </rdf:li> 
   <rdf:li rdf:parseType=“Resource”>
    <mwg-rs:Area stArea:x=“0.5” stArea:y=“0.5” stArea:w=“0.003” stArea:h=“0.002” stArea:unit=“normalized”/> 
    <mwg-rs:Type>BarCode</mwg-rs:Type> 
    <mwg-rs:BarCodeValue>ISBN:1234567890</mwg-rs:BarCodeValue> 
    <mwg-rs:Name>The Best Book</mwg-rs:Name>
    <mwg-rs:Description>The best book is the best book ever.</mwg-rs:Description> 
   </rdf:li>
  </rdf:Bag> 
 </mwg-rs:RegionList>
</mwg-rs:Regions>

RE: Adding new XMP namespaces - Added by Benjamin H. over 7 years ago

I now tried the simpler Microsoft Photo Schema at first. There must be something wrong or incomplete in detail (see diff). Look at the following example: I have to set type=Bag. This is ok for using exiv2 cli at the console only, but I cannot use it in such a way with pyexiv2. All types must be defined in the lib, so that pyexiv2 can ask the lib for types to use the right type mapping.

 src/exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions XmpText type=Bag" BabyGnuTux-Big.jpg
 src/exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:Rectangle 0.11, 0.22, 0.33, 0.44" BabyGnuTux-Big.jpg
 src/exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:PersonDisplayName Leif" BabyGnuTux-Big.jpg

result of my patch using exiv2 cli

  <rdf:Description rdf:about="" 
    xmlns:MP="http://ns.microsoft.com/photo/1.2/" 
    xmlns:MPRI="http://ns.microsoft.com/photo/1.2/t/RegionInfo#" 
    xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">
   <MP:RegionInfo rdf:parseType="Resource">
    <MPRI:Regions>
     <rdf:Bag>
      <rdf:li
       MPReg:Rectangle="0.11, 0.22, 0.33, 0.44" 
       MPReg:PersonDisplayName="Benjamin"/>
     </rdf:Bag>
    </MPRI:Regions>
   </MP:RegionInfo>
  </rdf:Description>

BUT using pyexiv2 does not work right now

md['Xmp.MP.RegionInfo/MPRI:Regions'] = 'type=Bag'

produces
Xmp.MP.RegionInfo                            XmpText     0  type="Struct" 
Xmp.MP.RegionInfo/MPRI:Regions               XmpText     0  

and then
md['Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:Rectangle'] = '0.10, 0.10, 0.90, 0.90'

clears all xmp data.

RE: Adding new XMP namespaces - Added by Andreas Huggel over 7 years ago

The patch looks quite ok. Does pyexiv2 have a method that corresponds to Exiv2::XmpValue::setXmpArrayType() that you could use instead of the 'type=Bag' qualifier? That's what Exiv2 calls when it encounters a 'type=' keyword in an XmpText value. (See http://www.exiv2.org/example5.html and look for 'setXmpArrayType' for a C++ example).

Andreas

RE: Adding new XMP namespaces - Added by Benjamin H. over 7 years ago

Is there a way define/setup the namespace definition, so that exiv2 will do the type=Bag automatically. I am not deep enough in the exiv2 code and XMP details to answer this question now. pyexiv2 seems not to use/wrap the setXmpArrayType, only the *ArrayValue things even in the developer code (wrapping: http://bazaar.launchpad.net/~osomon/pyexiv2/pyexiv2-0.3/view/head:/src/exiv2wrapper_python.cpp). If the type could not be set/got implicitly, pyexiv2 would have to be extended I guess. It currently only retrieves all type information via exiv2 via XmpTag::getType for mapping to python types, no possibility to set the array type.

I hope there is a possibility to drive the type=tag by the namespace setting in the properties, isn't it?

I have the same problem with the MWG Regions schema. This also includes the Bags as the MP 1.2 schema. A first diff for this namespace is appended. I will add a complete diff including docs if everything works fine.

here a first MWG Regions example

again I have to use the bad type=Bag explicitly :-( -- is there no other way?

src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:w 1600" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:h 800" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:unit pixel" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList XmpText type=Bag" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Name Baby Gnu" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Type Face" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:x 0.275312" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:y 0.3775" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:w 0.164375" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:h 0.28125" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:unit normalized" BabyGnuTux-Big.jpg

creates
  <rdf:Description rdf:about="" 
    xmlns:mwg-rs="http://www.metadataworkinggroup.com/schemas/regions/" 
    xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#" 
    xmlns:stArea="http://ns.adobe.com/xmp/sType/Area#">
   <mwg-rs:Regions rdf:parseType="Resource">
    <mwg-rs:AppliedToDimensions
     stDim:w="1600" 
     stDim:h="800" 
     stDim:unit="pixel"/>
    <mwg-rs:RegionList>
     <rdf:Bag>
      <rdf:li>
       <rdf:Description
        mwg-rs:Name="Baby Gnu" 
        mwg-rs:Type="Face">
       <mwg-rs:Area
        stArea:x="0.275312" 
        stArea:y="0.3775" 
        stArea:w="0.164375" 
        stArea:h="0.28125" 
        stArea:unit="normalized"/>
       </rdf:Description>
      </rdf:li>
     </rdf:Bag>
    </mwg-rs:RegionList>
   </mwg-rs:Regions>
  </rdf:Description>

RE: Adding new XMP namespaces - Added by Olivier Tilloy over 7 years ago

I can confirm Benjamin’s findings: pyexiv2 relies on libexiv2 to determine the type of a given tag and it doesn’t allow setting the type of an unknown tag in a custom namespace to anything other than XmpText (this is documented in the tutorial, see http://tilloy.net/dev/pyexiv2/tutorial.html#reading-and-writing-xmp-tags).

I guess making libexiv2 aware of the types of those tags is the way to go, pyexiv2 should then theoretically pick them up automatically. If it doesn’t once implemented in libexiv2, please file a bug at https://bugs.launchpad.net/pyexiv2/+filebug.

RE: Adding new XMP namespaces - Added by Benjamin H. over 7 years ago

Let's try to focus on my problem of the having to set explicit type=Bag. All other things should be ok, if this can be fixed.
The namespaces are not custom (anymore), because I added them to libexiv2. My examples here are made with the patched version.
See the appended diffs as http://dev.exiv2.org/attachments/307/MP_v1.diff.

The problem is to set up the (initial) empty bag, which only contains structs of other information. The namespace definition is

    extern const XmpPropertyInfo xmpMicrosoftPhotoRegionInfoInfo[] = {
        { "DateRegionsValid", N_("DateRegionsValid"), "Date",       xmpText, xmpExternal, N_("Date the last region was created")  },
        { "Regions",          N_("Regions"),          "bag Region", xmpBag,  xmpExternal, N_("Contains Regions/person tags") },           // SEE THIS LINE
        // End of list marker
        { 0, 0, 0, invalidTypeId, xmpInternal, 0 }
    };

As I understood the code of exiv2, I defined that Regions is a Bag, so why do one have to set explicitly type=Bag like the following?
src/exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions XmpText type=Bag" myfile
## src/exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:PersonDisplayName Tux" myfile ## This one needs the manual Bag setup

resulting in the correct xmp in file
  <rdf:Description rdf:about="" 
    xmlns:MP="http://ns.microsoft.com/photo/1.2/" 
    xmlns:MPRI="http://ns.microsoft.com/photo/1.2/t/RegionInfo#">
   <MP:RegionInfo rdf:parseType="Resource">
    <MPRI:Regions>
     <rdf:Bag/>
    </MPRI:Regions>
   </MP:RegionInfo>
  </rdf:Description>

RE: Adding new XMP namespaces - Added by Andreas Huggel over 7 years ago

It looks like the bug you've found here is that the type of a nested Exiv2 XMP key (one which contains a "/"), like Xmp.MP.RegionInfo/MPRI:Regions, is not properly detected. Exiv2 returns the type of Xmp.MP.RegionInfo for this construct instead of that of MPRI:Regions. I'm working on a fix, but it's a bit tricky and I'm quite tied up with other stuff, so please bear with me.

Andreas

RE: Adding new XMP namespaces - Added by Andreas Huggel over 7 years ago

Exiv2 returns the type of Xmp.MP.RegionInfo for this construct

No. It tries to find an XMP "RegionInfo/MPRI:Regions" property in the lookup table for namespace "MP". Since that doesn't exist it uses the default XmpText type.

RE: Adding new XMP namespaces - Added by Benjamin H. over 7 years ago

That means it is a general issue with hierarchic namespaces that has to be fixed in exiv2?

Wouldn't it be enough to lookup type for substring right to the last / in the flattened tag name?

RE: Adding new XMP namespaces - Added by Benjamin H. over 7 years ago

I added a complete diff (see http://dev.exiv2.org/attachments/309/RegionTagging.diff)
for both schemas (MP*, mwg-rs) in issue #798 I opened for this feature extension.

Hope the found bug can be fixed.

RE: Adding new XMP namespaces - Added by Andreas Huggel over 7 years ago

Good stuff, thanks! I've created issue #799 for the bug and checked-in what I think is a fix with r2644 (essentially as you suggested above, plus two less related tweaks). I've only tested one of your exiv2 cli examples from above. Can you test if the fix works with your patch for what you want to do eventually? I'll look at your patch in the meantime.

Andreas

RE: Adding new XMP namespaces - Added by Benjamin H. over 7 years ago

How would I use exiv2 CLI now? The first line I always used to setup the bag. Must this be done to open the bag? I cannot use this without any value, can I?

#src/exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions XmpText type=Bag" BabyGnuTux-Big.jpg  #  <-- How do I set up this bag?

src/exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:Rectangle 0.11, 0.22, 0.33, 0.44" BabyGnuTux-Big.jpg
Error: XMP Toolkit error 102: Indexing applied to non-array
Error: Failed to encode XMP metadata.

src/exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:PersonDisplayName Baby Gnu" BabyGnuTux-Big.jpg

RE: Adding new XMP namespaces - Added by Benjamin H. over 7 years ago

Back again, I tried more after applying your changes.

src/exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions ''" BabyGnuTux-Big.jpg
src/exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:Rectangle 0.11, 0.22, 0.33, 0.44" BabyGnuTux-Big.jpg
src/exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:PersonDisplayName Baby Gnu" BabyGnuTux-Big.jpg

src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:w 1600" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:h 800" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:unit pixel" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList ''" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Name Baby Gnu" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Type Face" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:x 0.275312" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:y 0.3775" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:w 0.164375" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:h 0.28125" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:unit normalized" BabyGnuTux-Big.jpg

results in
  <rdf:Description rdf:about="" 
    xmlns:MP="http://ns.microsoft.com/photo/1.2/" 
    xmlns:MPRI="http://ns.microsoft.com/photo/1.2/t/RegionInfo#" 
    xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#" 
    xmlns:mwg-rs="http://www.metadataworkinggroup.com/schemas/regions/" 
    xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#" 
    xmlns:stArea="http://ns.adobe.com/xmp/sType/Area#">
   <MP:RegionInfo rdf:parseType="Resource">
    <MPRI:Regions>
     <rdf:Bag>
      <rdf:li
       MPReg:Rectangle="0.11, 0.22, 0.33, 0.44" 
       MPReg:PersonDisplayName="Leif"/>
     </rdf:Bag>
    </MPRI:Regions>
   </MP:RegionInfo>
   <mwg-rs:Regions rdf:parseType="Resource">
    <mwg-rs:AppliedToDimensions
     stDim:w="1600" 
     stDim:h="800" 
     stDim:unit="pixel"/>
    <mwg-rs:RegionList>
     <rdf:Bag>
      <rdf:li>
       <rdf:Description
        mwg-rs:Name="Baby Gnu" 
        mwg-rs:Type="Face">
       <mwg-rs:Area
        stArea:x="0.275312" 
        stArea:y="0.3775" 
        stArea:w="0.164375" 
        stArea:h="0.28125" 
        stArea:unit="normalized"/>
       </rdf:Description>
      </rdf:li>
     </rdf:Bag>
    </mwg-rs:RegionList>
   </mwg-rs:Regions>
  </rdf:Description>

It seems to work with exiv2 CLI. But I have to test more tomorrow.
Btw., the man page says the value is optional in set command, but one has to define an empty value.

I now have to struggle a problem with the empty value in pyexiv2, but that is out of scope here. There it should then look like

import pyexiv2
md = pyexiv2.ImageMetadata('BabyGnuTux-Big.jpg')
md.read()
md['Xmp.MP.RegionInfo/MPRI:Regions'] = ['']                                         # this produces a "ValueError: Value not set" at write()
md['Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:Rectangle'] = '0.11, 0.22, 0.33, 0.44'
md['Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:PersonDisplayName'] = 'Baby Gnu'
md.write()

RE: Adding new XMP namespaces - Added by Andreas Huggel over 7 years ago

src/exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions ''" BabyGnuTux-Big.jpg

Yes, that's what I used too. Strictly speaking, this is more concise than no value at all (just "set Xmp.MP.RegionInfo/MPRI:Regions"), since libexiv2 actually supports creating a metadatum with only a key and no value, but in this case, we want a metadatum with an empty value of the correct type. On the other hand, I don't see a use-case for which it would make sense to add a metadatum with no value at all from the Exiv2 CLI, so it may eventually support this notation too. Either way, this is a consideration for the CLI only.

Similarly, I think it's an issue for pyexiv2 that it should allow setting a metadatum with an empty value.

(The reason why the manpage indicates that the value is optional is just because it's not needed for the del command.)

(Historically, the simple notation metaData[key] = "Value" existed before XMP support was added to Exiv2 and the "nested" XMP keys are just an awkward way to deal with the complexity that XMP allows without Exiv2 having to know much about it.)

Andreas

RE: Adding new XMP namespaces - Added by Benjamin H. over 7 years ago

I am still looking for the error in pyexiv2 ... As I see, pyexiv2 also relies on the xmpValueType of the XmpPropertyInfo.
via http://bazaar.launchpad.net/~osomon/pyexiv2/pyexiv2-0.2/view/head:/src/exiv2wrapper.cpp#L747

import pyexiv2
print '<%s>' % pyexiv2.XmpTag('Xmp.dc.subject/dc:contributor').type
print '<%s>' % pyexiv2.XmpTag('Xmp.dc.contributor').type
print '<%s>' % pyexiv2.XmpTag('Xmp.MPRI.Regions').type
print '<%s>' % pyexiv2.XmpTag('Xmp.MP.RegionInfo/MPRI:Regions').type

output: the nested-or-not info line including type comes from exiv2 properties.cpp debug output,
the output in the <> is the type attribute of the XmpTag class in Python that seems to wrap xmpValueType.
Nested key: Xmp.dc.subject/dc:contributor, prefix: dc, property: contributor, type: XmpBag
<>
Not nested key, type: XmpBag
<bag ProperName>
Not nested key, type: XmpBag
<bag Region>
Nested key: Xmp.MP.RegionInfo/MPRI:Regions, prefix: MPRI, property: Regions, type: XmpBag
<>

May the empty xmpValueType be a missing issue of your patch?
Or may this be the issue exiv2 had also in pyexiv2 now.

RE: Adding new XMP namespaces - Added by Andreas Huggel over 7 years ago

md['Xmp.MP.RegionInfo/MPRI:Regions'] = ['']

I don't see where this assignment happens in the pyexiv2 code. The question is why does this not set a value?

-ahu.

RE: Adding new XMP namespaces - Added by Benjamin H. over 7 years ago

Ok, I spotted the code where it is getting wrong. It is in pyexiv2's exiv2 wrapper code.
Here are my findings:

setting a new XmpTag with Python - let's call it newtag - with key 'Xmp.MP.RegionInfo/MPRI:Regions' and value = ['']
in Python newtag.type is '', because xmpValueType does not return the right right (see above!) but it should be 'bad Region' (to be fixed) but where?

when now setting the value, this happenes:

newtag._set_value(self, value=[''])
       -> type = XmpBag
       -> stype = self.type[4:]  # !!! should be "Region", because type=xmpValueType should be "bag Region"  -  to be fixed!
       -> self.raw_value calls newtag._convert_to_string(for each in value => once with '' should be 'Region') 
          -> "Region" should be handled as ProperName, Text… shouldn't it?
          -> '' is a string and is handled as one - but should be added to "elif type in ('Propername', ...
             -> newtag._set_raw_value([''])
                -> newtag._tag._setArrayValue([''])
                   ## here it happenes in the C++ wrapper

This now may be the solution for the pyexiv2 guys (thanks to a collegue). Replace this line http://bazaar.launchpad.net/~osomon/pyexiv2/pyexiv2-0.2/view/head:/src/exiv2wrapper.cpp#L767

    //_datum->setValue(0);
    _datum->setValue(std::string(""));     //  parameter=0 leads to "ValueError: Value not set" if array=['']

After having changed that line, I can set up regions with both exiv2 CLI and pyexiv2 producing exactly the same XMP data.

working examples for MS Photo 1.2 RegionInfo and MWG-RS Regions (Face)

CLI-way

src/exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions ''" BabyGnuTux-Big.jpg
src/exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:Rectangle 0.11, 0.22, 0.33, 0.44" BabyGnuTux-Big.jpg
src/exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:PersonDisplayName Baby Gnu" BabyGnuTux-Big.jpg

src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:w 1600" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:h 800" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:unit pixel" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList ''" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Name Baby Gnu" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Type Face" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:x 0.275312" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:y 0.3775" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:w 0.164375" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:h 0.28125" BabyGnuTux-Big.jpg
src/exiv2 mo -M "set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:unit normalized" BabyGnuTux-Big.jpg

pyexiv2-way

import pyexiv2
md = pyexiv2.ImageMetadata('BabyGnuTux-Big.jpg')
md.read()
md['Xmp.MP.RegionInfo/MPRI:Regions'] = ['']
md['Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:Rectangle'] = '0.11, 0.22, 0.33, 0.44'
md['Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:PersonDisplayName'] = 'Baby Gnu'

md['Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:w'] = '1600'
md['Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:h'] = '800'
md['Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:unit'] = 'pixel'
md['Xmp.mwg-rs.Regions/mwg-rs:RegionList'] = ['']
md['Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Name'] = 'Baby Gnu'
md['Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Type'] = 'Face'
md['Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:x'] = '0.275312'
md['Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:y'] = '0.3775'
md['Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:w'] = '0.164375'
md['Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:h'] = '0.28125'
md['Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:unit'] = 'normalized'

md.write()

RE: Adding new XMP namespaces - Added by Benjamin H. over 7 years ago

My Summary

Starting with revision r2643
+ changes (bugfix #799) in revision r2644 (http://dev.exiv2.org/projects/exiv2/repository/diff?rev=2644&rev_to=2643)
+ namespaces added by me here / in #798 (http://dev.exiv2.org/attachments/309/RegionTagging.diff)

=> libexiv2 and exiv2 CLI support the Microsoft Photo 1.2 RegionInfo (Windows Live Photo Gallery) and the MWG-RS Regions schema (Picasa 3.9+)

If you want to use it with pyexiv2, there are still some issues
  1. In Python the type attribute of the XmpTag is not correct. It is the empty string, but should be "Region" and "RegionStruct" because exiv2's xmpValueType is "bag Region" and "bag RegionStruct". It is still the question why this is not correct handled over to/in Python. Because the empty string as type is correctly handled as string it is not fully correct but works correct with current pyexiv2 code at the moment.
  2. One line in the exiv2 wrapper code of pyexiv2 creates an Exception when setting an empty bag. This can be ommited by changing that line in the code as described before in http://dev.exiv2.org/boards/3/topics/1039#message-1064. This may be a bug in pyexiv2 code and has to be reviewed by pyexiv2 developers.

Regards,
Benjamin

RE: Adding new XMP namespaces - Added by Olivier Tilloy over 7 years ago

Thanks for your investigation Benjamin.
I reviewed the change you suggest in pyexiv2 and it may fix the issue for this use case but it won't work in the general case.

The role of `_datum->setValue(0)` in XmpTag::setArrayValue(...) is to reset the value of the datum, i.e. to empty it if it previously contained values. Then all values in the list are appended by iterating over the list and calling _datum->setValue(...) for each value.
Replacing this line of code by `_datum->setValue(std::string(""))` would lead to appending an empty string (this is a no-op as the string is checked for emptiness) instead of actually clearing the potentially existing values.

I've discovered that the issue can be worked around by assigning [' '] (one whitespace) to md['Xmp.MP.RegionInfo/MPRI:Regions'] instead of [''], which doesn't work because XmpArrayValue::read(...) checks for the emptiness of the string before appending the value.
I wonder how this is implemented in exiv2 CLI. I would have thought that it roughly went through the same code path, but it seems it somehow works around it to allow appending a truly empty value to the datum. Andreas, could you maybe shed some light on this? If there's a way to implement that in pyexiv2, I think it would solve our issue.

RE: Adding new XMP namespaces - Added by Olivier Tilloy over 7 years ago

Regarding your first point (the type attribute of the XmpTag is not correct, it's the empty string, but should be "Region" and "RegionStruct"...), it appears that Exiv2::XmpProperties::propertyInfo(key) for key = 'Xmp.MP.RegionInfo/MPRI:Regions' returns a null pointer, which is why the type in pyexiv2 is an empty string.

Could it be that some static information regarding this tag is missing in libexiv2? Or that the implementation of XmpProperties::propertyInfo(...) doesn't play well with subkeys?

RE: Adding new XMP namespaces - Added by Andreas Huggel over 7 years ago

Olivier Tilloy wrote:

Regarding your first point (the type attribute of the XmpTag is not correct, it's the empty string, but should be "Region" and "RegionStruct"...), it appears that Exiv2::XmpProperties::propertyInfo(key) for key = 'Xmp.MP.RegionInfo/MPRI:Regions' returns a null pointer, which is why the type in pyexiv2 is an empty string.

Could it be that some static information regarding this tag is missing in libexiv2? Or that the implementation of XmpProperties::propertyInfo(...) doesn't play well with subkeys?

Yes, that doesn't work either. It needs to be fixed in Exiv2, similar to #799. I've only changed XmpProperties::propertyType() but probably should rather move the fix to propertyInfo. Will check, thanks for pointing this out!

-ahu.

RE: Adding new XMP namespaces - Added by Andreas Huggel over 7 years ago

The role of `_datum->setValue(0)` in XmpTag::setArrayValue(...) is to reset the value of the datum, i.e. to empty it if it previously contained values.

An Exiv2::Xmpdatum has a Value* pointing to its Exiv2 Value. This operation will reset that pointer, i.e., destroy the Exiv2 Value, not just empty it.

Then all values in the list are appended by iterating over the list and calling _datum->setValue(...) for each value.

The first of these appends will re-create the Exiv2 Value.

Replacing this line of code by `_datum->setValue(std::string(""))` would lead to appending an empty string (this is a no-op as the string is checked for emptiness) instead of actually clearing the potentially existing values.

Benjamin's `_datum->setValue(std::string(""))` creates an empty Exiv2::Value of the correct type and sets that in the Exiv2::Xmpdatum. In the case of an XmpArrayValue, "empty" means that it has an empty vector of strings.

I've discovered that the issue can be worked around by assigning [' '] (one whitespace) to md['Xmp.MP.RegionInfo/MPRI:Regions'] instead of ['']

I think the XMP-SDK will complain when encoding this (maybe only if the Regions bag actually contains something).

, which doesn't work because XmpArrayValue::read(...) checks for the emptiness of the string before appending the value.

This newly added check was also necessary to prevent an XMP-SDK error. Without the check, the read() would add an empty string, so that the value of the XmpArrayValue became a vector of strings containing one element, an empty string.

I wonder how this is implemented in exiv2 CLI. I would have thought that it roughly went through the same code path, but it seems it somehow works around it to allow appending a truly empty value to the datum. Andreas, could you maybe shed some light on this? If there's a way to implement that in pyexiv2, I think it would solve our issue.

The respective exiv2 cli code is in actions.cpp in Modify::setMetadatum and Modify::addMetadatum. It first creates an Exiv2 Value (if necessary), then reads the value string and sets the Exiv2 Value in the metadatum or adds a metadatum with a key and this new Exiv2 Value to the container as needed. See from here (note the comment at the beginning of the function): http://dev.exiv2.org/projects/exiv2/repository/entry/trunk/src/actions.cpp#L1351

-ahu.

RE: Adding new XMP namespaces - Added by Olivier Tilloy over 7 years ago

Thanks for the pointers Andreas!

I re-worked the way tags are assigned to an image in pyexiv2, and Benjamin’s tests as described at http://dev.exiv2.org/boards/3/topics/1039#pyexiv2-way now work as expected and produce the same result as the exiv2 CLI.

This has been committed to pyexiv2’s trunk (revision 373).
Tests are welcome!

RE: Adding new XMP namespaces - Added by Benjamin H. over 7 years ago

Andreas Huggel wrote:

Yes, that doesn't work either. It needs to be fixed in Exiv2, similar to #799. I've only changed XmpProperties::propertyType() but probably should rather move the fix to propertyInfo. Will check, thanks for pointing this out!

Finally, that would be nice.

As addition, in the follwing some more (but not all) examples of the new namespaces:

# set MP 1.2 schema regioninfo
exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions ''" empty.jpg
#  a person anywhere on the picture
exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions[1]/MPReg:PersonDisplayName Mister Anywhere" empty.jpg
#  a person with a bounding box
exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions[2]/MPReg:PersonDisplayName Bob" empty.jpg
exiv2 -M"set Xmp.MP.RegionInfo/MPRI:Regions[2]/MPReg:Rectangle 0.11, 0.22, 0.33, 0.44" empty.jpg

# set MWG Regions to an image of size 640x400 pixels
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:w 640" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:h 400" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:AppliedToDimensions/stDim:unit pixel" empty.jpg
#  open the regions list
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList ''" empty.jpg
#   a person with a bounding box
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Name Mister Rectangle" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Type Face" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:x 0.275312" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:y 0.3775" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:w 0.164375" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:h 0.28125" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[1]/mwg-rs:Area/stArea:unit normalized" empty.jpg
#   a person with a bounding circle and a description
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[2]/mwg-rs:Name Miss Circle" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[2]/mwg-rs:Description Girlfriend of Mister Rectangle" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[2]/mwg-rs:Type Face" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[2]/mwg-rs:Area/stArea:x 0.575312" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[2]/mwg-rs:Area/stArea:y 0.5775" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[2]/mwg-rs:Area/stArea:d 0.2122" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[2]/mwg-rs:Area/stArea:unit normalized" empty.jpg
#   a person at a coordinate
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[3]/mwg-rs:Name Madame Dot" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[3]/mwg-rs:Type Face" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[3]/mwg-rs:Area/stArea:x 0.975" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[3]/mwg-rs:Area/stArea:y 0.976" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[3]/mwg-rs:Area/stArea:unit normalized" empty.jpg
#   a pet in a bounding box with description
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[4]/mwg-rs:Name Doggy" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[4]/mwg-rs:Description My first dog" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[4]/mwg-rs:Type Pet" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[4]/mwg-rs:Area/stArea:x 0.2" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[4]/mwg-rs:Area/stArea:y 0.8" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[4]/mwg-rs:Area/stArea:w 0.25" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[4]/mwg-rs:Area/stArea:h 0.15" empty.jpg
exiv2 -M"set Xmp.mwg-rs.Regions/mwg-rs:RegionList[4]/mwg-rs:Area/stArea:unit normalized" empty.jpg
# missing: barcodes and focus points

~ Benjamin

(1-25/26)