diff --git a/src/doc/manual/regions.rst b/src/doc/manual/regions.rst index c0c018fd..5036de09 100644 --- a/src/doc/manual/regions.rst +++ b/src/doc/manual/regions.rst @@ -49,6 +49,12 @@ The entire region can be dragged by clicking within it. Positioning the region is made easier if you zoom in by holding down the ``Ctrl`` key while scrolling with the mouse scroll wheel. (The zoom can also be adjusted with ``Ctrl-Plus`` and ``Ctrl-Minus`` key combinations.) +After creating a region you can add metadata about the it. +The ``Region name`` and ``Identifier`` fields are unique labels for the region, but I haven't found a use for them. + +The ``Boundary unit`` specifies whether the vertex positions are stored as absolute pixel values or relative proportions of the image width and height. +Don't use pixel values unless you have some other software that requires them. + .. image:: ../images/screenshot_276.png The most important metadata for a region is probably its "role_". @@ -56,8 +62,6 @@ This is chosen from a "controlled vocabulary" defined by the IPTC. Photini shows the IPTC names and definitions (as "tooltips") in a drop down menu when you click on the ``Role`` entry. You can select one or more roles from the list. -Other, less useful, metadata includes a name and identifier for the region. - .. image:: ../images/screenshot_277.png The `content type`_ is another controlled vocabulary that allows you to say what's special about the selected area. diff --git a/src/doc/manual/tags.rst b/src/doc/manual/tags.rst index 6aebc99b..8a3a26d0 100644 --- a/src/doc/manual/tags.rst +++ b/src/doc/manual/tags.rst @@ -51,6 +51,10 @@ You may find this useful when deciding what to write in those fields. - - Xmp.iptc.ExtDescrAccessibility - + * - `Person(s) shown`_ + - + - Xmp.iptcExt.PersonInImage + - * - Rating_ - - Xmp.xmp.Rating @@ -133,7 +137,7 @@ You may find this useful when deciding what to write in those fields. - * - `Image Regions`_ - - - Xmp.iptcExt.ImageRegion + - Xmp.iptcExt.ImageRegion Xmp.mwg-rs.Regions Xmp.MP.RegionInfo - * - Latitude_, longitude_ - Exif.GPSInfo.GPSLatitude Exif.GPSInfo.GPSLatitudeRef Exif.GPSInfo.GPSLongitude Exif.GPSInfo.GPSLongitudeRef @@ -324,6 +328,8 @@ It is applied to the Date / time Taken, Date / time Digitised and Date / time Mo http://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata#gps-latitude .. _longitude: http://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata#gps-longitude +.. _Person(s) shown: + http://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata#person-shown-in-the-image .. _Rating: http://www.iptc.org/std/photometadata/specification/IPTC-PhotoMetadata#image-rating .. _Rights\: Usage Terms: diff --git a/src/lang/ca/LC_MESSAGES/documentation.po b/src/lang/ca/LC_MESSAGES/documentation.po index 01d68d37..6866da34 100644 --- a/src/lang/ca/LC_MESSAGES/documentation.po +++ b/src/lang/ca/LC_MESSAGES/documentation.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: Photini 2023.4.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-19 16:57+0000\n" +"POT-Creation-Date: 2024-12-20 14:43+0000\n" "PO-Revision-Date: 2023-04-19 08:54+0000\n" "Last-Translator: Joan , 2023\n" "Language: ca\n" @@ -1920,6 +1920,19 @@ msgid "" " be adjusted with ``Ctrl-Plus`` and ``Ctrl-Minus`` key combinations.)" msgstr "" +msgid "" +"After creating a region you can add metadata about the it. The ``Region " +"name`` and ``Identifier`` fields are unique labels for the region, but I " +"haven't found a use for them." +msgstr "" + +msgid "" +"The ``Boundary unit`` specifies whether the vertex positions are stored as " +"absolute pixel values or relative proportions of the image width and height." +" Don't use pixel values unless you have some other software that requires " +"them." +msgstr "" + msgid "" "The most important metadata for a region is probably its \"role_\". This is " "chosen from a \"controlled vocabulary\" defined by the IPTC. Photini shows " @@ -1928,9 +1941,6 @@ msgid "" "list." msgstr "" -msgid "Other, less useful, metadata includes a name and identifier for the region." -msgstr "" - msgid "" "The `content type`_ is another controlled vocabulary that allows you to say " "what's special about the selected area. The upper part shows the IPTC " @@ -2071,6 +2081,12 @@ msgstr "" msgid "Xmp.iptc.ExtDescrAccessibility" msgstr "" +msgid "`Person(s) shown`_" +msgstr "" + +msgid "Xmp.iptcExt.PersonInImage" +msgstr "" + msgid "Rating_" msgstr "" @@ -2238,7 +2254,7 @@ msgstr "" msgid "`Image Regions`_" msgstr "" -msgid "Xmp.iptcExt.ImageRegion" +msgid "Xmp.iptcExt.ImageRegion Xmp.mwg-rs.Regions Xmp.MP.RegionInfo" msgstr "" msgid "Latitude_, longitude_" @@ -5830,3 +5846,11 @@ msgstr "" #~ " metadata." #~ msgstr "" +#~ msgid "" +#~ "Other, less useful, metadata includes a " +#~ "name and identifier for the region." +#~ msgstr "" + +#~ msgid "Xmp.iptcExt.ImageRegion" +#~ msgstr "" + diff --git a/src/lang/cs/LC_MESSAGES/documentation.po b/src/lang/cs/LC_MESSAGES/documentation.po index fe018779..06bd2a7e 100644 --- a/src/lang/cs/LC_MESSAGES/documentation.po +++ b/src/lang/cs/LC_MESSAGES/documentation.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: Photini 2023.4.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-19 16:57+0000\n" +"POT-Creation-Date: 2024-12-20 14:43+0000\n" "PO-Revision-Date: 2023-04-19 08:54+0000\n" "Last-Translator: fri, 2023\n" "Language: cs@qtfiletype\n" @@ -1917,6 +1917,19 @@ msgid "" " be adjusted with ``Ctrl-Plus`` and ``Ctrl-Minus`` key combinations.)" msgstr "" +msgid "" +"After creating a region you can add metadata about the it. The ``Region " +"name`` and ``Identifier`` fields are unique labels for the region, but I " +"haven't found a use for them." +msgstr "" + +msgid "" +"The ``Boundary unit`` specifies whether the vertex positions are stored as " +"absolute pixel values or relative proportions of the image width and height." +" Don't use pixel values unless you have some other software that requires " +"them." +msgstr "" + msgid "" "The most important metadata for a region is probably its \"role_\". This is " "chosen from a \"controlled vocabulary\" defined by the IPTC. Photini shows " @@ -1925,9 +1938,6 @@ msgid "" "list." msgstr "" -msgid "Other, less useful, metadata includes a name and identifier for the region." -msgstr "" - msgid "" "The `content type`_ is another controlled vocabulary that allows you to say " "what's special about the selected area. The upper part shows the IPTC " @@ -2068,6 +2078,12 @@ msgstr "" msgid "Xmp.iptc.ExtDescrAccessibility" msgstr "" +msgid "`Person(s) shown`_" +msgstr "" + +msgid "Xmp.iptcExt.PersonInImage" +msgstr "" + msgid "Rating_" msgstr "" @@ -2235,7 +2251,7 @@ msgstr "" msgid "`Image Regions`_" msgstr "" -msgid "Xmp.iptcExt.ImageRegion" +msgid "Xmp.iptcExt.ImageRegion Xmp.mwg-rs.Regions Xmp.MP.RegionInfo" msgstr "" msgid "Latitude_, longitude_" @@ -5799,3 +5815,11 @@ msgstr "" #~ " metadata." #~ msgstr "" +#~ msgid "" +#~ "Other, less useful, metadata includes a " +#~ "name and identifier for the region." +#~ msgstr "" + +#~ msgid "Xmp.iptcExt.ImageRegion" +#~ msgstr "" + diff --git a/src/lang/de/LC_MESSAGES/documentation.po b/src/lang/de/LC_MESSAGES/documentation.po index d6a79651..ac9cc9bb 100644 --- a/src/lang/de/LC_MESSAGES/documentation.po +++ b/src/lang/de/LC_MESSAGES/documentation.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: Photini 2023.4.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-19 16:57+0000\n" +"POT-Creation-Date: 2024-12-20 14:43+0000\n" "PO-Revision-Date: 2023-04-19 08:54+0000\n" "Last-Translator: Jim Easterbrook , 2023\n" "Language: de\n" @@ -1914,6 +1914,19 @@ msgid "" " be adjusted with ``Ctrl-Plus`` and ``Ctrl-Minus`` key combinations.)" msgstr "" +msgid "" +"After creating a region you can add metadata about the it. The ``Region " +"name`` and ``Identifier`` fields are unique labels for the region, but I " +"haven't found a use for them." +msgstr "" + +msgid "" +"The ``Boundary unit`` specifies whether the vertex positions are stored as " +"absolute pixel values or relative proportions of the image width and height." +" Don't use pixel values unless you have some other software that requires " +"them." +msgstr "" + msgid "" "The most important metadata for a region is probably its \"role_\". This is " "chosen from a \"controlled vocabulary\" defined by the IPTC. Photini shows " @@ -1922,9 +1935,6 @@ msgid "" "list." msgstr "" -msgid "Other, less useful, metadata includes a name and identifier for the region." -msgstr "" - msgid "" "The `content type`_ is another controlled vocabulary that allows you to say " "what's special about the selected area. The upper part shows the IPTC " @@ -2065,6 +2075,12 @@ msgstr "" msgid "Xmp.iptc.ExtDescrAccessibility" msgstr "" +msgid "`Person(s) shown`_" +msgstr "" + +msgid "Xmp.iptcExt.PersonInImage" +msgstr "" + msgid "Rating_" msgstr "" @@ -2232,7 +2248,7 @@ msgstr "" msgid "`Image Regions`_" msgstr "" -msgid "Xmp.iptcExt.ImageRegion" +msgid "Xmp.iptcExt.ImageRegion Xmp.mwg-rs.Regions Xmp.MP.RegionInfo" msgstr "" msgid "Latitude_, longitude_" @@ -5827,3 +5843,11 @@ msgstr "" #~ " metadata." #~ msgstr "" +#~ msgid "" +#~ "Other, less useful, metadata includes a " +#~ "name and identifier for the region." +#~ msgstr "" + +#~ msgid "Xmp.iptcExt.ImageRegion" +#~ msgstr "" + diff --git a/src/lang/es/LC_MESSAGES/documentation.po b/src/lang/es/LC_MESSAGES/documentation.po index 573e8021..8c77e5c6 100644 --- a/src/lang/es/LC_MESSAGES/documentation.po +++ b/src/lang/es/LC_MESSAGES/documentation.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: Photini 2023.4.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-19 16:57+0000\n" +"POT-Creation-Date: 2024-12-20 14:43+0000\n" "PO-Revision-Date: 2023-04-19 08:54+0000\n" "Last-Translator: Jim Easterbrook , 2023\n" "Language: es@qtfiletype\n" @@ -1910,6 +1910,19 @@ msgid "" " be adjusted with ``Ctrl-Plus`` and ``Ctrl-Minus`` key combinations.)" msgstr "" +msgid "" +"After creating a region you can add metadata about the it. The ``Region " +"name`` and ``Identifier`` fields are unique labels for the region, but I " +"haven't found a use for them." +msgstr "" + +msgid "" +"The ``Boundary unit`` specifies whether the vertex positions are stored as " +"absolute pixel values or relative proportions of the image width and height." +" Don't use pixel values unless you have some other software that requires " +"them." +msgstr "" + msgid "" "The most important metadata for a region is probably its \"role_\". This is " "chosen from a \"controlled vocabulary\" defined by the IPTC. Photini shows " @@ -1918,9 +1931,6 @@ msgid "" "list." msgstr "" -msgid "Other, less useful, metadata includes a name and identifier for the region." -msgstr "" - msgid "" "The `content type`_ is another controlled vocabulary that allows you to say " "what's special about the selected area. The upper part shows the IPTC " @@ -2061,6 +2071,12 @@ msgstr "" msgid "Xmp.iptc.ExtDescrAccessibility" msgstr "" +msgid "`Person(s) shown`_" +msgstr "" + +msgid "Xmp.iptcExt.PersonInImage" +msgstr "" + msgid "Rating_" msgstr "" @@ -2228,7 +2244,7 @@ msgstr "" msgid "`Image Regions`_" msgstr "" -msgid "Xmp.iptcExt.ImageRegion" +msgid "Xmp.iptcExt.ImageRegion Xmp.mwg-rs.Regions Xmp.MP.RegionInfo" msgstr "" msgid "Latitude_, longitude_" @@ -5814,3 +5830,11 @@ msgstr "" #~ " metadata." #~ msgstr "" +#~ msgid "" +#~ "Other, less useful, metadata includes a " +#~ "name and identifier for the region." +#~ msgstr "" + +#~ msgid "Xmp.iptcExt.ImageRegion" +#~ msgstr "" + diff --git a/src/lang/fr/LC_MESSAGES/documentation.po b/src/lang/fr/LC_MESSAGES/documentation.po index 52e9f633..ddbac402 100644 --- a/src/lang/fr/LC_MESSAGES/documentation.po +++ b/src/lang/fr/LC_MESSAGES/documentation.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: Photini 2023.4.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-19 16:57+0000\n" +"POT-Creation-Date: 2024-12-20 14:43+0000\n" "PO-Revision-Date: 2023-04-19 08:54+0000\n" "Last-Translator: Jim Easterbrook , 2023\n" "Language: fr@qtfiletype\n" @@ -1904,6 +1904,19 @@ msgid "" " be adjusted with ``Ctrl-Plus`` and ``Ctrl-Minus`` key combinations.)" msgstr "" +msgid "" +"After creating a region you can add metadata about the it. The ``Region " +"name`` and ``Identifier`` fields are unique labels for the region, but I " +"haven't found a use for them." +msgstr "" + +msgid "" +"The ``Boundary unit`` specifies whether the vertex positions are stored as " +"absolute pixel values or relative proportions of the image width and height." +" Don't use pixel values unless you have some other software that requires " +"them." +msgstr "" + msgid "" "The most important metadata for a region is probably its \"role_\". This is " "chosen from a \"controlled vocabulary\" defined by the IPTC. Photini shows " @@ -1912,9 +1925,6 @@ msgid "" "list." msgstr "" -msgid "Other, less useful, metadata includes a name and identifier for the region." -msgstr "" - msgid "" "The `content type`_ is another controlled vocabulary that allows you to say " "what's special about the selected area. The upper part shows the IPTC " @@ -2055,6 +2065,12 @@ msgstr "" msgid "Xmp.iptc.ExtDescrAccessibility" msgstr "" +msgid "`Person(s) shown`_" +msgstr "" + +msgid "Xmp.iptcExt.PersonInImage" +msgstr "" + msgid "Rating_" msgstr "" @@ -2222,7 +2238,7 @@ msgstr "" msgid "`Image Regions`_" msgstr "" -msgid "Xmp.iptcExt.ImageRegion" +msgid "Xmp.iptcExt.ImageRegion Xmp.mwg-rs.Regions Xmp.MP.RegionInfo" msgstr "" msgid "Latitude_, longitude_" @@ -5814,3 +5830,11 @@ msgstr "" #~ " metadata." #~ msgstr "" +#~ msgid "" +#~ "Other, less useful, metadata includes a " +#~ "name and identifier for the region." +#~ msgstr "" + +#~ msgid "Xmp.iptcExt.ImageRegion" +#~ msgstr "" + diff --git a/src/lang/it/LC_MESSAGES/documentation.po b/src/lang/it/LC_MESSAGES/documentation.po index 899781a5..e840c2ca 100644 --- a/src/lang/it/LC_MESSAGES/documentation.po +++ b/src/lang/it/LC_MESSAGES/documentation.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: Photini 2023.4.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-19 16:57+0000\n" +"POT-Creation-Date: 2024-12-20 14:43+0000\n" "PO-Revision-Date: 2023-04-19 08:54+0000\n" "Last-Translator: alessio Cadore , 2023\n" "Language: it@qtfiletype\n" @@ -1903,6 +1903,19 @@ msgid "" " be adjusted with ``Ctrl-Plus`` and ``Ctrl-Minus`` key combinations.)" msgstr "" +msgid "" +"After creating a region you can add metadata about the it. The ``Region " +"name`` and ``Identifier`` fields are unique labels for the region, but I " +"haven't found a use for them." +msgstr "" + +msgid "" +"The ``Boundary unit`` specifies whether the vertex positions are stored as " +"absolute pixel values or relative proportions of the image width and height." +" Don't use pixel values unless you have some other software that requires " +"them." +msgstr "" + msgid "" "The most important metadata for a region is probably its \"role_\". This is " "chosen from a \"controlled vocabulary\" defined by the IPTC. Photini shows " @@ -1911,9 +1924,6 @@ msgid "" "list." msgstr "" -msgid "Other, less useful, metadata includes a name and identifier for the region." -msgstr "" - msgid "" "The `content type`_ is another controlled vocabulary that allows you to say " "what's special about the selected area. The upper part shows the IPTC " @@ -2054,6 +2064,12 @@ msgstr "" msgid "Xmp.iptc.ExtDescrAccessibility" msgstr "" +msgid "`Person(s) shown`_" +msgstr "" + +msgid "Xmp.iptcExt.PersonInImage" +msgstr "" + msgid "Rating_" msgstr "" @@ -2221,7 +2237,7 @@ msgstr "" msgid "`Image Regions`_" msgstr "" -msgid "Xmp.iptcExt.ImageRegion" +msgid "Xmp.iptcExt.ImageRegion Xmp.mwg-rs.Regions Xmp.MP.RegionInfo" msgstr "" msgid "Latitude_, longitude_" @@ -5813,3 +5829,11 @@ msgstr "" #~ " metadata." #~ msgstr "" +#~ msgid "" +#~ "Other, less useful, metadata includes a " +#~ "name and identifier for the region." +#~ msgstr "" + +#~ msgid "Xmp.iptcExt.ImageRegion" +#~ msgstr "" + diff --git a/src/lang/ko/LC_MESSAGES/documentation.po b/src/lang/ko/LC_MESSAGES/documentation.po index 0d670e46..53edbea5 100644 --- a/src/lang/ko/LC_MESSAGES/documentation.po +++ b/src/lang/ko/LC_MESSAGES/documentation.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: Photini 2023.4.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-19 16:57+0000\n" +"POT-Creation-Date: 2024-12-20 14:43+0000\n" "PO-Revision-Date: 2023-04-19 08:54+0000\n" "Last-Translator: Jim Easterbrook , 2023\n" "Language: ko\n" @@ -2290,6 +2290,19 @@ msgid "" " be adjusted with ``Ctrl-Plus`` and ``Ctrl-Minus`` key combinations.)" msgstr "" +msgid "" +"After creating a region you can add metadata about the it. The ``Region " +"name`` and ``Identifier`` fields are unique labels for the region, but I " +"haven't found a use for them." +msgstr "" + +msgid "" +"The ``Boundary unit`` specifies whether the vertex positions are stored as " +"absolute pixel values or relative proportions of the image width and height." +" Don't use pixel values unless you have some other software that requires " +"them." +msgstr "" + msgid "" "The most important metadata for a region is probably its \"role_\". This is " "chosen from a \"controlled vocabulary\" defined by the IPTC. Photini shows " @@ -2298,9 +2311,6 @@ msgid "" "list." msgstr "" -msgid "Other, less useful, metadata includes a name and identifier for the region." -msgstr "" - msgid "" "The `content type`_ is another controlled vocabulary that allows you to say " "what's special about the selected area. The upper part shows the IPTC " @@ -2452,6 +2462,13 @@ msgstr "" msgid "Xmp.iptc.ExtDescrAccessibility" msgstr "" +msgid "`Person(s) shown`_" +msgstr "" + +#, fuzzy +msgid "Xmp.iptcExt.PersonInImage" +msgstr "Xmp.iptcExt.LocationShown" + msgid "Rating_" msgstr "Rating_" @@ -2621,7 +2638,7 @@ msgstr "" msgid "`Image Regions`_" msgstr "" -msgid "Xmp.iptcExt.ImageRegion" +msgid "Xmp.iptcExt.ImageRegion Xmp.mwg-rs.Regions Xmp.MP.RegionInfo" msgstr "" msgid "Latitude_, longitude_" @@ -5817,3 +5834,11 @@ msgstr "" #~ " metadata." #~ msgstr "" +#~ msgid "" +#~ "Other, less useful, metadata includes a " +#~ "name and identifier for the region." +#~ msgstr "" + +#~ msgid "Xmp.iptcExt.ImageRegion" +#~ msgstr "" + diff --git a/src/lang/nb/LC_MESSAGES/documentation.po b/src/lang/nb/LC_MESSAGES/documentation.po index 9d047f97..d0375f90 100644 --- a/src/lang/nb/LC_MESSAGES/documentation.po +++ b/src/lang/nb/LC_MESSAGES/documentation.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: Photini 2023.4.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-19 16:57+0000\n" +"POT-Creation-Date: 2024-12-20 14:43+0000\n" "PO-Revision-Date: 2023-04-19 08:54+0000\n" "Last-Translator: Jim Easterbrook , 2023\n" "Language: nb\n" @@ -1904,6 +1904,19 @@ msgid "" " be adjusted with ``Ctrl-Plus`` and ``Ctrl-Minus`` key combinations.)" msgstr "" +msgid "" +"After creating a region you can add metadata about the it. The ``Region " +"name`` and ``Identifier`` fields are unique labels for the region, but I " +"haven't found a use for them." +msgstr "" + +msgid "" +"The ``Boundary unit`` specifies whether the vertex positions are stored as " +"absolute pixel values or relative proportions of the image width and height." +" Don't use pixel values unless you have some other software that requires " +"them." +msgstr "" + msgid "" "The most important metadata for a region is probably its \"role_\". This is " "chosen from a \"controlled vocabulary\" defined by the IPTC. Photini shows " @@ -1912,9 +1925,6 @@ msgid "" "list." msgstr "" -msgid "Other, less useful, metadata includes a name and identifier for the region." -msgstr "" - msgid "" "The `content type`_ is another controlled vocabulary that allows you to say " "what's special about the selected area. The upper part shows the IPTC " @@ -2055,6 +2065,12 @@ msgstr "" msgid "Xmp.iptc.ExtDescrAccessibility" msgstr "" +msgid "`Person(s) shown`_" +msgstr "" + +msgid "Xmp.iptcExt.PersonInImage" +msgstr "" + msgid "Rating_" msgstr "" @@ -2222,7 +2238,7 @@ msgstr "" msgid "`Image Regions`_" msgstr "" -msgid "Xmp.iptcExt.ImageRegion" +msgid "Xmp.iptcExt.ImageRegion Xmp.mwg-rs.Regions Xmp.MP.RegionInfo" msgstr "" msgid "Latitude_, longitude_" @@ -5814,3 +5830,11 @@ msgstr "" #~ " metadata." #~ msgstr "" +#~ msgid "" +#~ "Other, less useful, metadata includes a " +#~ "name and identifier for the region." +#~ msgstr "" + +#~ msgid "Xmp.iptcExt.ImageRegion" +#~ msgstr "" + diff --git a/src/lang/pl/LC_MESSAGES/documentation.po b/src/lang/pl/LC_MESSAGES/documentation.po index 234a14b5..723838e8 100644 --- a/src/lang/pl/LC_MESSAGES/documentation.po +++ b/src/lang/pl/LC_MESSAGES/documentation.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: Photini 2023.4.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-19 16:57+0000\n" +"POT-Creation-Date: 2024-12-20 14:43+0000\n" "PO-Revision-Date: 2023-04-19 08:54+0000\n" "Last-Translator: Dawid Głaz, 2023\n" "Language: pl@qtfiletype\n" @@ -1906,6 +1906,19 @@ msgid "" " be adjusted with ``Ctrl-Plus`` and ``Ctrl-Minus`` key combinations.)" msgstr "" +msgid "" +"After creating a region you can add metadata about the it. The ``Region " +"name`` and ``Identifier`` fields are unique labels for the region, but I " +"haven't found a use for them." +msgstr "" + +msgid "" +"The ``Boundary unit`` specifies whether the vertex positions are stored as " +"absolute pixel values or relative proportions of the image width and height." +" Don't use pixel values unless you have some other software that requires " +"them." +msgstr "" + msgid "" "The most important metadata for a region is probably its \"role_\". This is " "chosen from a \"controlled vocabulary\" defined by the IPTC. Photini shows " @@ -1914,9 +1927,6 @@ msgid "" "list." msgstr "" -msgid "Other, less useful, metadata includes a name and identifier for the region." -msgstr "" - msgid "" "The `content type`_ is another controlled vocabulary that allows you to say " "what's special about the selected area. The upper part shows the IPTC " @@ -2057,6 +2067,12 @@ msgstr "" msgid "Xmp.iptc.ExtDescrAccessibility" msgstr "" +msgid "`Person(s) shown`_" +msgstr "" + +msgid "Xmp.iptcExt.PersonInImage" +msgstr "" + msgid "Rating_" msgstr "" @@ -2224,7 +2240,7 @@ msgstr "" msgid "`Image Regions`_" msgstr "" -msgid "Xmp.iptcExt.ImageRegion" +msgid "Xmp.iptcExt.ImageRegion Xmp.mwg-rs.Regions Xmp.MP.RegionInfo" msgstr "" msgid "Latitude_, longitude_" @@ -5816,3 +5832,11 @@ msgstr "" #~ " metadata." #~ msgstr "" +#~ msgid "" +#~ "Other, less useful, metadata includes a " +#~ "name and identifier for the region." +#~ msgstr "" + +#~ msgid "Xmp.iptcExt.ImageRegion" +#~ msgstr "" + diff --git a/src/lang/templates/gettext/documentation.pot b/src/lang/templates/gettext/documentation.pot index 68cfc77c..a452a152 100644 --- a/src/lang/templates/gettext/documentation.pot +++ b/src/lang/templates/gettext/documentation.pot @@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: Photini 2024.11.1.post38\n" +"Project-Id-Version: Photini 2024.11.1.post43\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-19 16:57+0000\n" +"POT-Creation-Date: 2024-12-20 14:43+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -1012,10 +1012,13 @@ msgstr "" msgid "Positioning the region is made easier if you zoom in by holding down the ``Ctrl`` key while scrolling with the mouse scroll wheel. (The zoom can also be adjusted with ``Ctrl-Plus`` and ``Ctrl-Minus`` key combinations.)" msgstr "" -msgid "The most important metadata for a region is probably its \"role_\". This is chosen from a \"controlled vocabulary\" defined by the IPTC. Photini shows the IPTC names and definitions (as \"tooltips\") in a drop down menu when you click on the ``Role`` entry. You can select one or more roles from the list." +msgid "After creating a region you can add metadata about the it. The ``Region name`` and ``Identifier`` fields are unique labels for the region, but I haven't found a use for them." +msgstr "" + +msgid "The ``Boundary unit`` specifies whether the vertex positions are stored as absolute pixel values or relative proportions of the image width and height. Don't use pixel values unless you have some other software that requires them." msgstr "" -msgid "Other, less useful, metadata includes a name and identifier for the region." +msgid "The most important metadata for a region is probably its \"role_\". This is chosen from a \"controlled vocabulary\" defined by the IPTC. Photini shows the IPTC names and definitions (as \"tooltips\") in a drop down menu when you click on the ``Role`` entry. You can select one or more roles from the list." msgstr "" msgid "The `content type`_ is another controlled vocabulary that allows you to say what's special about the selected area. The upper part shows the IPTC controlled vocabulary. The most useful of these is probably ``human``. The lower part shows MWG \"types\". These are primarily intended for use by automatic systems such as face detectors and camera autofocus." @@ -1117,6 +1120,12 @@ msgstr "" msgid "Xmp.iptc.ExtDescrAccessibility" msgstr "" +msgid "`Person(s) shown`_" +msgstr "" + +msgid "Xmp.iptcExt.PersonInImage" +msgstr "" + msgid "Rating_" msgstr "" @@ -1282,7 +1291,7 @@ msgstr "" msgid "`Image Regions`_" msgstr "" -msgid "Xmp.iptcExt.ImageRegion" +msgid "Xmp.iptcExt.ImageRegion Xmp.mwg-rs.Regions Xmp.MP.RegionInfo" msgstr "" msgid "Latitude_, longitude_" diff --git a/src/photini/cv.py b/src/photini/cv.py index f89ae40c..5320090d 100644 --- a/src/photini/cv.py +++ b/src/photini/cv.py @@ -5,219 +5,160 @@ # Date: 2019-12-06T12:00:00+00:00 # Licence: http://creativecommons.org/licenses/by/4.0/ # http://cv.iptc.org/newscodes/imageregiontype/ -image_region_types = \ -({'data': {'Iptc4xmpExt:Name': {'en-GB': 'animal'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/animal',)}, - 'definition': {'en-GB': 'A living organism different from humans or flora'}, - 'name': {'en-GB': 'animal'}, - 'note': {}, - 'qcode': 'imgregtype:animal'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'artwork'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/artwork',)}, - 'definition': {'en-GB': 'Artistic work'}, - 'name': {'en-GB': 'artwork'}, - 'note': {}, - 'qcode': 'imgregtype:artwork'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'dividing line'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/dividingLine',)}, - 'definition': {'en-GB': 'A line expressing a visual division of the image, ' - 'such as a horizon'}, - 'name': {'en-GB': 'dividing line'}, - 'note': {}, - 'qcode': 'imgregtype:dividingLine'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'plant'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/plant',)}, - 'definition': {'en-GB': 'A living organism different from humans and ' - 'animals'}, - 'name': {'en-GB': 'plant'}, - 'note': {}, - 'qcode': 'imgregtype:plant'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'geographic area'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/geoArea',)}, - 'definition': {'en-GB': 'A named area on the surface of the planet earth'}, - 'name': {'en-GB': 'geographic area'}, - 'note': {'en-GB': 'Specific details of the area can be expressed by other ' - 'metadata'}, - 'qcode': 'imgregtype:geoArea'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'graphic'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/graphic',)}, - 'definition': {'en-GB': 'A graphic representation of information'}, - 'name': {'en-GB': 'graphic'}, - 'note': {}, - 'qcode': 'imgregtype:graphic'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'machine-readable code'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/machineCode',)}, - 'definition': {'en-GB': 'Optical label such as barcode or QR code'}, - 'name': {'en-GB': 'machine-readable code'}, - 'note': {}, - 'qcode': 'imgregtype:machineCode'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'human'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/human',)}, - 'definition': {'en-GB': 'A human being'}, - 'name': {'en-GB': 'human'}, - 'note': {}, - 'qcode': 'imgregtype:human'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'product'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/product',)}, - 'definition': {'en-GB': 'A thing that was produced and can be handed over'}, - 'name': {'en-GB': 'product'}, - 'note': {}, - 'qcode': 'imgregtype:product'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'text'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/text',)}, - 'definition': {'en-GB': 'Human readable script of any language'}, - 'name': {'en-GB': 'text'}, - 'note': {}, - 'qcode': 'imgregtype:text'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'building'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/building',)}, - 'definition': {'en-GB': 'A structure with walls and roof in most cases'}, - 'name': {'en-GB': 'building'}, - 'note': {}, - 'qcode': 'imgregtype:building'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'vehicle'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/vehicle',)}, - 'definition': {'en-GB': 'An object used for transporting something, like ' - 'car, train, ship, plane or bike'}, - 'name': {'en-GB': 'vehicle'}, - 'note': {}, - 'qcode': 'imgregtype:vehicle'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'food'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/food',)}, - 'definition': {'en-GB': 'Substances providing nutrition for a living body'}, - 'name': {'en-GB': 'food'}, - 'note': {}, - 'qcode': 'imgregtype:food'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'clothing'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/clothing',)}, - 'definition': {'en-GB': 'Something worn to cover the body'}, - 'name': {'en-GB': 'clothing'}, - 'note': {}, - 'qcode': 'imgregtype:clothing'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'rock formation'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/rockFormation',)}, - 'definition': {'en-GB': 'A special formation of stone mass'}, - 'name': {'en-GB': 'rock formation'}, - 'note': {}, - 'qcode': 'imgregtype:rockFormation'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'body of water'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/bodyOfWater',)}, - 'definition': {'en-GB': 'A significant accumulation of water'}, - 'name': {'en-GB': 'body of water'}, - 'note': {'en-GB': 'Including a waterfall, a geyser and other phenomena of ' - 'water'}, - 'qcode': 'imgregtype:bodyOfWater'}) - -image_region_types_idx = \ -{'imgregtype:animal': 0, - 'imgregtype:artwork': 1, - 'imgregtype:bodyOfWater': 15, - 'imgregtype:building': 10, - 'imgregtype:clothing': 13, - 'imgregtype:dividingLine': 2, - 'imgregtype:food': 12, - 'imgregtype:geoArea': 4, - 'imgregtype:graphic': 5, - 'imgregtype:human': 7, - 'imgregtype:machineCode': 6, - 'imgregtype:plant': 3, - 'imgregtype:product': 8, - 'imgregtype:rockFormation': 14, - 'imgregtype:text': 9, - 'imgregtype:vehicle': 11} - +image_region_types = { +'animal': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'animal'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/animal',)}, + 'definition': {'en-GB': 'A living organism different from humans or flora'}, + 'note': {}}, +'artwork': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'artwork'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/artwork',)}, + 'definition': {'en-GB': 'Artistic work'}, + 'note': {}}, +'dividingLine': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'dividing line'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/dividingLine',)}, + 'definition': {'en-GB': 'A line expressing a visual division of the image, ' + 'such as a horizon'}, + 'note': {}}, +'plant': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'plant'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/plant',)}, + 'definition': {'en-GB': 'A living organism different from humans and animals'}, + 'note': {}}, +'geoArea': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'geographic area'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/geoArea',)}, + 'definition': {'en-GB': 'A named area on the surface of the planet earth'}, + 'note': {'en-GB': 'Specific details of the area can be expressed by other ' + 'metadata'}}, +'graphic': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'graphic'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/graphic',)}, + 'definition': {'en-GB': 'A graphic representation of information'}, + 'note': {}}, +'machineCode': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'machine-readable code'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/machineCode',)}, + 'definition': {'en-GB': 'Optical label such as barcode or QR code'}, + 'note': {}}, +'human': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'human'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/human',)}, + 'definition': {'en-GB': 'A human being'}, + 'note': {}}, +'product': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'product'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/product',)}, + 'definition': {'en-GB': 'A thing that was produced and can be handed over'}, + 'note': {}}, +'text': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'text'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/text',)}, + 'definition': {'en-GB': 'Human readable script of any language'}, + 'note': {}}, +'building': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'building'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/building',)}, + 'definition': {'en-GB': 'A structure with walls and roof in most cases'}, + 'note': {}}, +'vehicle': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'vehicle'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/vehicle',)}, + 'definition': {'en-GB': 'An object used for transporting something, like car, ' + 'train, ship, plane or bike'}, + 'note': {}}, +'food': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'food'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/food',)}, + 'definition': {'en-GB': 'Substances providing nutrition for a living body'}, + 'note': {}}, +'clothing': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'clothing'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/clothing',)}, + 'definition': {'en-GB': 'Something worn to cover the body'}, + 'note': {}}, +'rockFormation': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'rock formation'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/rockFormation',)}, + 'definition': {'en-GB': 'A special formation of stone mass'}, + 'note': {}}, +'bodyOfWater': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'body of water'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregiontype/bodyOfWater',)}, + 'definition': {'en-GB': 'A significant accumulation of water'}, + 'note': {'en-GB': 'Including a waterfall, a geyser and other phenomena of ' + 'water'}}, +} # ©IPTC, International Press Telecommunications Council - https://iptc.org # Date: 2019-12-06T12:00:00+00:00 # Licence: http://creativecommons.org/licenses/by/4.0/ # http://cv.iptc.org/newscodes/imageregionrole/ -image_region_roles = \ -({'data': {'Iptc4xmpExt:Name': {'en-GB': 'cropping'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/cropping',)}, - 'definition': {'en-GB': 'Image region can be used for any cropping'}, - 'name': {'en-GB': 'cropping'}, - 'note': {}, - 'qcode': 'imgregrole:cropping'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'recommended cropping'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/recomCropping',)}, - 'definition': {'en-GB': 'Image region is recommended for cropping'}, - 'name': {'en-GB': 'recommended cropping'}, - 'note': {}, - 'qcode': 'imgregrole:recomCropping'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'landscape format cropping'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/landscapeCropping',)}, - 'definition': {'en-GB': 'Image region suggested for cropping in landscape ' - 'format'}, - 'name': {'en-GB': 'landscape format cropping'}, - 'note': {'en-GB': 'Use for images of non-landscape format'}, - 'qcode': 'imgregrole:landscapeCropping'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'portrait format cropping'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/portraitCropping',)}, - 'definition': {'en-GB': 'Image region suggested for cropping in portrait ' - 'format'}, - 'name': {'en-GB': 'portrait format cropping'}, - 'note': {'en-GB': 'Use for images of non-portrait format'}, - 'qcode': 'imgregrole:portraitCropping'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'square format cropping'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/squareCropping',)}, - 'definition': {'en-GB': 'Image region suggested for cropping in square ' - 'format'}, - 'name': {'en-GB': 'square format cropping'}, - 'note': {}, - 'qcode': 'imgregrole:squareCropping'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'composite image item'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/compositeImageItem',)}, - 'definition': {'en-GB': 'Image region of an item in a composite image'}, - 'name': {'en-GB': 'composite image item'}, - 'note': {}, - 'qcode': 'imgregrole:compositeImageItem'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'copyright region'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/copyrightRegion',)}, - 'definition': {'en-GB': 'Image region with a copyright different from the ' - 'copyright of the whole picture'}, - 'name': {'en-GB': 'copyright region'}, - 'note': {}, - 'qcode': 'imgregrole:copyrightRegion'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'subject area'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/subjectArea',)}, - 'definition': {'en-GB': 'Image region contains a subject in the overall ' - 'scene.'}, - 'name': {'en-GB': 'subject area'}, - 'note': {'en-GB': 'Multiple regions of an image may be set as subject area.'}, - 'qcode': 'imgregrole:subjectArea'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'main subject area'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/mainSubjectArea',)}, - 'definition': {'en-GB': 'Image region contains the main subject in the ' - 'overall scene. Same as the Exif SubjectArea.'}, - 'name': {'en-GB': 'main subject area'}, - 'note': {'en-GB': 'Only a single region of an image may be set as main ' - 'subject area.'}, - 'qcode': 'imgregrole:mainSubjectArea'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'area of interest'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/areaOfInterest',)}, - 'definition': {'en-GB': 'Image region contains a thing of special interest ' - 'to the viewer'}, - 'name': {'en-GB': 'area of interest'}, - 'note': {}, - 'qcode': 'imgregrole:areaOfInterest'}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'business use'}, - 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/businessUse',)}, - 'definition': {'en-GB': 'Image region is dedicated to a specific business ' - 'use'}, - 'name': {'en-GB': 'business use'}, - 'note': {'en-GB': 'In addition a more granular role could be expressed by a ' - 'term of a role CV of this business.'}, - 'qcode': 'imgregrole:businessUse'}) - -image_region_roles_idx = \ -{'imgregrole:areaOfInterest': 9, - 'imgregrole:businessUse': 10, - 'imgregrole:compositeImageItem': 5, - 'imgregrole:copyrightRegion': 6, - 'imgregrole:cropping': 0, - 'imgregrole:landscapeCropping': 2, - 'imgregrole:mainSubjectArea': 8, - 'imgregrole:portraitCropping': 3, - 'imgregrole:recomCropping': 1, - 'imgregrole:squareCropping': 4, - 'imgregrole:subjectArea': 7} - +image_region_roles = { +'cropping': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'cropping'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/cropping',)}, + 'definition': {'en-GB': 'Image region can be used for any cropping'}, + 'note': {}}, +'recomCropping': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'recommended cropping'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/recomCropping',)}, + 'definition': {'en-GB': 'Image region is recommended for cropping'}, + 'note': {}}, +'landscapeCropping': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'landscape format cropping'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/landscapeCropping',)}, + 'definition': {'en-GB': 'Image region suggested for cropping in landscape ' + 'format'}, + 'note': {'en-GB': 'Use for images of non-landscape format'}}, +'portraitCropping': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'portrait format cropping'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/portraitCropping',)}, + 'definition': {'en-GB': 'Image region suggested for cropping in portrait ' + 'format'}, + 'note': {'en-GB': 'Use for images of non-portrait format'}}, +'squareCropping': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'square format cropping'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/squareCropping',)}, + 'definition': {'en-GB': 'Image region suggested for cropping in square ' + 'format'}, + 'note': {}}, +'compositeImageItem': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'composite image item'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/compositeImageItem',)}, + 'definition': {'en-GB': 'Image region of an item in a composite image'}, + 'note': {}}, +'copyrightRegion': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'copyright region'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/copyrightRegion',)}, + 'definition': {'en-GB': 'Image region with a copyright different from the ' + 'copyright of the whole picture'}, + 'note': {}}, +'subjectArea': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'subject area'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/subjectArea',)}, + 'definition': {'en-GB': 'Image region contains a subject in the overall ' + 'scene.'}, + 'note': {'en-GB': 'Multiple regions of an image may be set as subject area.'}}, +'mainSubjectArea': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'main subject area'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/mainSubjectArea',)}, + 'definition': {'en-GB': 'Image region contains the main subject in the ' + 'overall scene. Same as the Exif SubjectArea.'}, + 'note': {'en-GB': 'Only a single region of an image may be set as main ' + 'subject area.'}}, +'areaOfInterest': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'area of interest'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/areaOfInterest',)}, + 'definition': {'en-GB': 'Image region contains a thing of special interest to ' + 'the viewer'}, + 'note': {}}, +'businessUse': +{'data': {'Iptc4xmpExt:Name': {'en-GB': 'business use'}, + 'xmp:Identifier': ('http://cv.iptc.org/newscodes/imageregionrole/businessUse',)}, + 'definition': {'en-GB': 'Image region is dedicated to a specific business ' + 'use'}, + 'note': {'en-GB': 'In addition a more granular role could be expressed by a ' + 'term of a role CV of this business.'}}, +} diff --git a/src/photini/flickr.py b/src/photini/flickr.py index 751db7f4..c35886ce 100644 --- a/src/photini/flickr.py +++ b/src/photini/flickr.py @@ -248,16 +248,19 @@ def set_notes(self, params, photo_id): return 'Failed to delete note' # add new notes for note in params['notes']: - if not note['is_person']: - rsp = self.api_call( - 'flickr.photos.notes.add', post=True, photo_id=photo_id, - note_x=note['x'], note_y=note['y'], note_w=note['w'], - note_h=note['h'], note_text=note['content']) - elif note['content'] == self.user_data['fullname']: + if (note['is_person'] and + note['content'] == self.user_data['fullname']): rsp = self.api_call( 'flickr.photos.people.add', post=True, photo_id=photo_id, person_x=note['x'], person_y=note['y'], person_w=note['w'], person_h=note['h'], user_id=self.user_data['user_nsid']) + if rsp is None: + return 'Failed to add person' + # flickr.photos.people.add doesn't show a box, so do it separately + rsp = self.api_call( + 'flickr.photos.notes.add', post=True, photo_id=photo_id, + note_x=note['x'], note_y=note['y'], note_w=note['w'], + note_h=note['h'], note_text=note['content']) if rsp is None: return 'Failed to add note' return '' @@ -650,11 +653,6 @@ def get_params(self, image, upload_prefs, replace_prefs, photo_id): params['notes'] = [] for note in image.metadata.image_region.to_notes(image, 500): params['notes'].append(note) - if note['is_person']: - # Flickr doesn't show box around person, so add one - note = dict(note) - note['is_person'] = False - params['notes'].append(note) return params def replace_dialog(self, image): diff --git a/src/photini/metadata.py b/src/photini/metadata.py index d5ed775c..baea0a2a 100644 --- a/src/photini/metadata.py +++ b/src/photini/metadata.py @@ -719,7 +719,8 @@ def __init__(self, path, notify=None): if extras: value = list(value) + extras values[0] = (tag, self._data_type[name](value)) - logger.info('%s: merged people in regions', tag) + logger.info('%s(%s): merged image_region people', + os.path.basename(self._path), name) # merge in camera timezone if (name in ('date_digitised', 'date_modified', 'date_taken') and self.timezone): diff --git a/src/photini/pixelfed.py b/src/photini/pixelfed.py index df6e498e..8fcf5a9a 100644 --- a/src/photini/pixelfed.py +++ b/src/photini/pixelfed.py @@ -720,6 +720,38 @@ def alt_text(self, image): return '\n\n'.join(description) return '' + def focus(self, image): + md = image.metadata + if not md.image_region: + return None + dims = md.dimensions + portrait_format = dims['height'] > dims['width'] + transform = md.orientation and md.orientation.get_transform() + if transform and transform.isRotating(): + portrait_format = not portrait_format + if portrait_format: + roles = ('landscapeCropping', 'squareCropping', 'recomCropping', + 'cropping', 'portraitCropping') + else: + roles = ('squareCropping', 'portraitCropping', 'landscapeCropping', + 'recomCropping', 'cropping') + for role in roles: + for region in md.image_region: + if not region.has_role(role): + continue + points = region.to_Qt(image) + boundary = region['Iptc4xmpExt:RegionBoundary'] + if boundary['Iptc4xmpExt:rbShape'] == 'rectangle': + centre = (points.at(0) + points.at(1)) / 2.0 + elif boundary['Iptc4xmpExt:rbShape'] == 'circle': + centre = points.at(0) + else: + centre = points.boundingRect().center() + if transform: + centre = transform.map(centre) + return (centre.x() * 2.0) - 1.0, 1.0 - (centre.y() * 2.0) + return None + def get_upload_params(self, image, state): if 'ask_alt_text' not in state: state['ask_alt_text'] = self.user_widget.compose_settings[ @@ -748,7 +780,7 @@ def get_upload_params(self, image, state): state['ask_alt_text'] = False elif result == dialog.StandardButton.Abort: return 'abort' - focus = image.metadata.image_region.get_focus(image) + focus = self.focus(image) if focus: params['media']['focus'] = '{:f},{:f}'.format(*focus) params['license'] = self.widget['license'].get_value() diff --git a/src/photini/regions.py b/src/photini/regions.py index 441d5e0e..7c0ec291 100644 --- a/src/photini/regions.py +++ b/src/photini/regions.py @@ -21,10 +21,10 @@ import os import re -from photini.cv import image_region_types, image_region_roles from photini.pyqt import * from photini.pyqt import set_symbol_font, using_pyside from photini.types import ImageRegionItem, MD_LangAlt +from photini.vocab import IPTCRoleCV, IPTCTypeCV, MWGTypeCV from photini.widgets import LangAltWidget, MultiStringEdit, SingleLineEdit logger = logging.getLogger(__name__) @@ -589,14 +589,14 @@ def draw_boundaries(self, idx, regions): active = n == idx boundary = region['Iptc4xmpExt:RegionBoundary'] if boundary['Iptc4xmpExt:rbShape'] == 'rectangle': - if region.has_role('imgregrole:squareCropping'): + if region.has_role('squareCropping'): constraint = 'square' - elif region.has_role('imgregrole:landscapeCropping'): + elif region.has_role('landscapeCropping'): if self.transform().isRotating(): constraint = 'portrait' else: constraint = 'landscape' - elif region.has_role('imgregrole:portraitCropping'): + elif region.has_role('portraitCropping'): if self.transform().isRotating(): constraint = 'landscape' else: @@ -668,7 +668,7 @@ def add_menu_items(self, items, add_separator=True, exclusive=False): group = QtWidgets.QActionGroup(self) group.setExclusionPolicy(group.ExclusionPolicy.ExclusiveOptional) for item in items: - label = MD_LangAlt(item['name']).best_match() + label = MD_LangAlt(item['data']['Iptc4xmpExt:Name']).best_match() tip = MD_LangAlt(item['definition']).best_match() if item['note']: tip += ' ({})'.format(MD_LangAlt(item['note']).best_match()) @@ -716,7 +716,6 @@ def set_value(self, value): self.add_menu_items([{ 'data': item, 'definition': None, - 'name': item['Iptc4xmpExt:Name'], 'note': None}], add_separator=False) self._updating = False self.update_display() @@ -775,52 +774,6 @@ def set_value(self, value): class RegionForm(QtWidgets.QScrollArea): new_value = QtSignal(int, dict) - MWG_region_types = ( - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'Face'}, - 'xmp:Identifier': ('mwg-rs:Type Face',)}, - 'definition': {'en-GB': "Region area for people's faces."}, - 'name': {'en-GB': 'Face'}, - 'note': None}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'Pet'}, - 'xmp:Identifier': ('mwg-rs:Type Pet',)}, - 'definition': {'en-GB': "Region area for pets."}, - 'name': {'en-GB': 'Pet'}, - 'note': None}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'Focus/EvaluatedUsed'}, - 'xmp:Identifier': ('mwg-rs:Type Focus', - 'mwg-rs:FocusUsage EvaluatedUsed')}, - 'definition': {'en-GB': "Region area for camera auto-focus regions." - "
EvaluatedUsed specifies that the focus point was" - " considered during focusing and was used in the final" - " image."}, - 'name': {'en-GB': 'Focus (EvaluatedUsed)'}, - 'note': None}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'Focus/EvaluatedNotUsed'}, - 'xmp:Identifier': ('mwg-rs:Type Focus', - 'mwg-rs:FocusUsage EvaluatedNotUsed')}, - 'definition': {'en-GB': "Region area for camera auto-focus regions." - "
EvaluatedNotUsed specifies that the focus point" - " was considered during focusing but not utilised in" - " the final image."}, - 'name': {'en-GB': 'Focus (EvaluatedNotUsed)'}, - 'note': None}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'Focus/NotEvaluatedNotUsed'}, - 'xmp:Identifier': ('mwg-rs:Type Focus' - 'mwg-rs:FocusUsage NotEvaluatedNotUsed')}, - 'definition': {'en-GB': "Region area for camera auto-focus regions." - "
NotEvaluatedNotUsed specifies that a focus point" - " was not evaluated and not used, e.g. a fixed focus" - " point on the camera which was not used in any" - " fashion."}, - 'name': {'en-GB': 'Focus (NotEvaluatedNotUsed)'}, - 'note': None}, - {'data': {'Iptc4xmpExt:Name': {'en-GB': 'BarCode'}, - 'xmp:Identifier': ('mwg-rs:Type BarCode',)}, - 'definition': {'en-GB': "One dimensional linear or two dimensional" - " matrix optical code."}, - 'name': {'en-GB': 'BarCode'}, - 'note': None}, - ) def __init__(self, idx, *arg, **kw): super(RegionForm, self).__init__(*arg, **kw) @@ -863,7 +816,7 @@ def __init__(self, idx, *arg, **kw): translate('RegionsTab', 'Boundary unit'), self.widgets[key]) # roles key = 'Iptc4xmpExt:rRole' - self.widgets[key] = EntityConceptWidget(key, image_region_roles) + self.widgets[key] = EntityConceptWidget(key, IPTCRoleCV.vocab.values()) self.widgets[key].setToolTip('

{}

'.format(translate( 'RegionsTab', 'Role of this region among all regions of this image' ' or of other images. The value SHOULD be taken from a Controlled' @@ -872,9 +825,9 @@ def __init__(self, idx, *arg, **kw): layout.addRow(translate('RegionsTab', 'Role'), self.widgets[key]) # content types key = 'Iptc4xmpExt:rCtype' - self.widgets[key] = EntityConceptWidget(key, image_region_types) + self.widgets[key] = EntityConceptWidget(key, IPTCTypeCV.vocab.values()) self.widgets[key].add_menu_items( - self.MWG_region_types, exclusive=True) + MWGTypeCV.vocab.values(), exclusive=True) self.widgets[key].setToolTip('

{}

'.format(translate( 'RegionsTab', 'The semantic type of what is shown inside the' ' region. The value SHOULD be taken from a Controlled' diff --git a/src/photini/types.py b/src/photini/types.py index cc0cf63c..6201e097 100644 --- a/src/photini/types.py +++ b/src/photini/types.py @@ -24,11 +24,12 @@ import pprint import re -from photini.cv import (image_region_roles, image_region_roles_idx, - image_region_types, image_region_types_idx) +import exiv2 + from photini.exiv2 import MetadataHandler from photini.pyqt import * from photini.pyqt import qt_version_info, using_pyside +from photini.vocab import IPTCRoleCV, IPTCTypeCV, MWGTypeCV logger = logging.getLogger(__name__) @@ -684,13 +685,22 @@ def __init__(self, value=None): @classmethod def get_type(cls, key, value): if cls.extendable and key not in cls.item_type: - logger.warning('Inferring type for %s', key) - if isinstance(value, (list, tuple)): - cls.item_type[key] = MD_MultiString - elif isinstance(value, dict): - cls.item_type[key] = MD_LangAlt + result = exiv2.XmpProperties.propertyType( + exiv2.XmpKey('Xmp.' + key.replace(':', '.'))) + if result in (exiv2.TypeId.xmpAlt, exiv2.TypeId.xmpBag, + exiv2.TypeId.xmpSeq): + result = MD_MultiString + elif result == exiv2.TypeId.langAlt: + result = MD_LangAlt + elif isinstance(value, (list, tuple, dict)): + logger.warning('Inferring type for %s', key) + if isinstance(value, dict): + result = MD_LangAlt + else: + result = MD_MultiString else: - cls.item_type[key] = MD_String + result = MD_String + cls.item_type[key] = result return cls.item_type[key] @classmethod @@ -1719,6 +1729,9 @@ def __eq__(self, other): class EntityConceptArray(MD_StructArray): item_type = EntityConcept + def has_data(self, data): + return data in self + class RegionBoundaryNumber(MD_Float): def set_decimals(self, decimals): @@ -1880,68 +1893,45 @@ class ImageRegionItem(MD_Structure): 'Iptc4xmpExt:rCtype': EntityConceptArray, 'Iptc4xmpExt:rRole': EntityConceptArray, 'Iptc4xmpExt:PersonInImage': MD_MultiString, - 'Iptc4xmpExt:OrganisationInImageName': MD_MultiString, - 'mwg-rs:BarCodeValue': MD_String, + 'mwg-rs:Description': MD_String, 'mwg-rs:Name': MD_String, - 'mwg-rs:Title': MD_String, - 'photoshop:CaptionWriter': MD_String, - 'dc:creator': MD_MultiString, 'dc:description': MD_LangAlt, - 'dc:subject': MD_MultiString, - 'xmpRights:UsageTerms': MD_LangAlt, } - @staticmethod - def ctype_IPTC_to_MWG(file_value): - if 'Iptc4xmpExt:rCtype' not in file_value: - return file_value, {} - # move MWG type info from Iptc4xmpExt:rCtype to extra info - ctype_list = [] - extras = {} - for item in file_value['Iptc4xmpExt:rCtype']: - if any(x.startswith('mwg') for x in item['xmp:Identifier']): - extras.update(x.split() for x in item['xmp:Identifier']) - else: - ctype_list.append(item) - file_value['Iptc4xmpExt:rCtype'] = ctype_list - return file_value, extras + def merge(self, info, tag, other): + result = super(ImageRegionItem, self).merge(info, tag, other) + for old_key, new_key in (('mwg-rs:Name', 'dc:description'), + ('mwg-rs:Description', 'dc:description')): + if result[old_key]: + value = result[old_key] + del result[old_key] + if old_key == 'mwg-rs:Name' and ( + result.has_type('human') or result.has_type('Face')): + new_key = 'Iptc4xmpExt:PersonInImage' + value = [value] + if result[new_key]: + result[new_key] = result[new_key].merge( + info, '{}[{}]'.format(tag, new_key), value) + else: + result[new_key] = value + self.log_merged(info, '{}[{}]'.format(tag, new_key), value) + return self.__class__(result) def to_IPTC(self): - # move MWG type info from Iptc4xmpExt:rCtype to extra info - file_value, extras = self.ctype_IPTC_to_MWG(dict(self)) - file_value.update(extras) + # remove MWG ctype from Iptc4xmpExt:rCtype + file_value = dict(self) + file_value['Iptc4xmpExt:rCtype'] = MWGTypeCV.clean_file_data( + file_value['Iptc4xmpExt:rCtype']) return file_value - @staticmethod - def ctype_MWG_to_IPTC(file_value): - if 'mwg-rs:Type' not in file_value: - return file_value, {} - # move MWG type info from extra info to Iptc4xmpExt:rCtype - name = file_value['mwg-rs:Type'] - del file_value['mwg-rs:Type'] - identifier = ['mwg-rs:Type ' + name] - if 'mwg-rs:FocusUsage' in file_value: - focus_usage = file_value['mwg-rs:FocusUsage'] - del file_value['mwg-rs:FocusUsage'] - name += '/' + focus_usage - identifier.append('mwg-rs:FocusUsage ' + focus_usage) - return file_value, { - 'Iptc4xmpExt:Name': name, 'xmp:Identifier': identifier} - - @classmethod - def from_IPTC(cls, file_value): - file_value, ctype = cls.ctype_MWG_to_IPTC(file_value) - if ctype: - if 'Iptc4xmpExt:rCtype' not in file_value: - file_value['Iptc4xmpExt:rCtype'] = [] - file_value['Iptc4xmpExt:rCtype'].append(ctype) - return cls(file_value) - def to_MWG(self, dims): - file_value, region = self.ctype_IPTC_to_MWG(dict(self)) # convert some IPTC region data to MWG format - region['mwg-rs:Extensions'] = {} - for key, value in file_value.items(): + region = { + 'mwg-rs:Extensions': {}, + 'mwg-rs:Name': self['mwg-rs:Name'], + } + region.update(MWGTypeCV.to_file_data(self['Iptc4xmpExt:rCtype'])) + for key, value in self.items(): if not value: continue if key == 'Iptc4xmpExt:RegionBoundary': @@ -1971,13 +1961,17 @@ def to_MWG(self, dims): return None region['mwg-rs:Area'] = area elif (key == 'Iptc4xmpExt:PersonInImage' - and not file_value['mwg-rs:Name']): - region['mwg-rs:Name'] = str(value) + and not region['mwg-rs:Name'] + and 'mwg-rs:Type' in region + and region['mwg-rs:Type'] == 'Face'): + region['mwg-rs:Name'] = str(value) elif key == 'dc:description': region['mwg-rs:Description'] = value.best_match() elif key.startswith('mwg-rs:'): region[key] = value - else: + elif key not in ('Iptc4xmpExt:rCtype', 'Iptc4xmpExt:rRole'): + # MWG docs say extension can be "any additional top + # level XMP property". ctype and role are not top level region['mwg-rs:Extensions'][key] = value return region @@ -2003,7 +1997,7 @@ def to_MP(self, dims): return region @classmethod - def from_MWG(cls, file_value, scale_diameter): + def from_MWG(cls, file_value, dims): if not file_value: return None # convert MWG region data to IPTC format @@ -2026,6 +2020,7 @@ def from_MWG(cls, file_value, scale_diameter): boundary['Iptc4xmpExt:rbH'] = h elif 'stArea:d' in area: # circle + scale_diameter = min(dims['stDim:h'] / dims['stDim:w'], 1.0) d = float(area['stArea:d']) * scale_diameter boundary['Iptc4xmpExt:rbShape'] = 'circle' boundary['Iptc4xmpExt:rbX'] = x @@ -2036,28 +2031,20 @@ def from_MWG(cls, file_value, scale_diameter): boundary['Iptc4xmpExt:rbShape'] = 'polygon' boundary['Iptc4xmpExt:rbVertices'] = [{ 'Iptc4xmpExt:rbX': x, 'Iptc4xmpExt:rbY': y}] - region = {'Iptc4xmpExt:RegionBoundary': boundary} - file_value, ctype = cls.ctype_MWG_to_IPTC(file_value) - region['Iptc4xmpExt:rCtype'] = [ctype] + region = { + 'Iptc4xmpExt:RegionBoundary': boundary, + 'Iptc4xmpExt:rCtype': [MWGTypeCV.from_file_data(file_value)], + } + core_region = cls(region) for key, value in file_value.items(): - if key in ('mwg-rs:Area', 'mwg-rs:Extensions', 'rdfs:seeAlso'): - continue - elif (key == 'mwg-rs:Name' and 'Iptc4xmpExt:Name' in ctype - and ctype['Iptc4xmpExt:Name'] == 'Face'): - region['Iptc4xmpExt:PersonInImage'] = [value] - elif key == 'mwg-rs:Description': - region['dc:description'] = value - else: + if key in ('rdfs:seeAlso', 'mwg-rs:Extensions'): + for k, v in value.items(): + region[k] = v + elif key not in ('mwg-rs:Area', 'mwg-rs:Type', 'mwg-rs:FocusUsage'): region[key] = value - region = cls(region) - for key in ('mwg-rs:Extensions', 'rdfs:seeAlso'): - if key in file_value: - for k, v in file_value[key].items(): - if k in region: - region[k] = region[k].merge('info', 'tag', v) - else: - region[k] = v - return cls(region) + # merge core data with full data to convert items + return core_region.merge( + '(image_region)', 'Xmp.mwg-rs.Regions', cls(region)) @classmethod def from_MP(cls, file_value): @@ -2113,25 +2100,19 @@ def from_Exif(cls, file_value): region['Iptc4xmpExt:rbUnit'] = 'pixel' return cls({ 'Iptc4xmpExt:RegionBoundary': region, - 'Iptc4xmpExt:rRole': [image_region_roles[ - image_region_roles_idx['imgregrole:mainSubjectArea']]['data']], + 'Iptc4xmpExt:rRole': [IPTCRoleCV.data_for_name('mainSubjectArea')], }) - def has_uid(self, key, uid): - if key not in self: - return False - for item in self[key]: - if 'xmp:Identifier' in item and uid in item['xmp:Identifier']: - return True - return False - - def has_type(self, qcode): - data = image_region_types[image_region_types_idx[qcode]]['data'] - return self.has_uid('Iptc4xmpExt:rCtype', data['xmp:Identifier'][0]) + def has_type(self, label): + if label in MWGTypeCV.vocab: + data = MWGTypeCV.vocab[label]['data'] + else: + data = IPTCTypeCV.vocab[label]['data'] + return self['Iptc4xmpExt:rCtype'].has_data(data) - def has_role(self, qcode): - data = image_region_roles[image_region_roles_idx[qcode]]['data'] - return self.has_uid('Iptc4xmpExt:rRole', data['xmp:Identifier'][0]) + def has_role(self, label): + return self['Iptc4xmpExt:rRole'].has_data( + IPTCRoleCV.vocab[label]['data']) def to_Qt(self, image): return self['Iptc4xmpExt:RegionBoundary'].to_Qt(image) @@ -2155,10 +2136,10 @@ class RegionList(MD_StructArray): item_type = ImageRegionItem def find(self, other): - if other.has_role('imgregrole:mainSubjectArea'): + if other.has_role('mainSubjectArea'): # only one main subject area region allowed for n, value in enumerate(self): - if value.has_role('imgregrole:mainSubjectArea'): + if value.has_role('mainSubjectArea'): return n return len(self) for n, value in enumerate(self): @@ -2196,14 +2177,12 @@ def from_exiv2(cls, file_value, tag): if not file_value: return cls() if tag == 'Xmp.iptcExt.ImageRegion': - value = {'RegionList': [ImageRegionItem.from_IPTC(x) - for x in file_value]} + value = {'RegionList': [ImageRegionItem(x) for x in file_value]} elif tag == 'Xmp.mwg-rs.Regions': dims = AppliedToDimensions(file_value['mwg-rs:AppliedToDimensions']) - scale_diameter = min(dims['stDim:h'] / dims['stDim:w'], 1.0) value = { 'AppliedToDimensions': dims, - 'RegionList': [ImageRegionItem.from_MWG(x, scale_diameter) + 'RegionList': [ImageRegionItem.from_MWG(x, dims) for x in file_value['mwg-rs:RegionList']]} elif tag == 'Xmp.MP.RegionInfo': value = {'RegionList': [ImageRegionItem.from_MP(x) @@ -2318,13 +2297,12 @@ def from_notes(self, notes, image, target_size): continue region = { 'Iptc4xmpExt:RegionBoundary': boundary, - 'Iptc4xmpExt:rRole': [image_region_roles[ - image_region_roles_idx['imgregrole:subjectArea']]['data']], + 'Iptc4xmpExt:rRole': [IPTCRoleCV.data_for_name('subjectArea')], } if note['is_person']: region['Iptc4xmpExt:PersonInImage'] = [note['content']] - region['Iptc4xmpExt:rCtype'] = [image_region_types[ - image_region_types_idx['imgregtype:human']]['data']] + region['Iptc4xmpExt:rCtype'] = [ + IPTCTypeCV.data_for_name('human')] else: region['dc:description'] = {'x-default': note['content']} if note['authorrealname']: @@ -2357,59 +2335,19 @@ def to_notes(self, image, target_size): for region, note in self.to_note_boundary(image, target_size): note['content'] = '' note['is_person'] = False - if region.has_type('imgregtype:human'): - if 'Iptc4xmpExt:PersonInImage' in region: - note['content'] = ', '.join( - region['Iptc4xmpExt:PersonInImage']) + if region.has_type('human') or region.has_type('Face'): + note['content'] = ', '.join(region['Iptc4xmpExt:PersonInImage']) note['is_person'] = True elif not any(region.has_role(x) for x in ( - 'imgregrole:subjectArea', - 'imgregrole:mainSubjectArea', - 'imgregrole:areaOfInterest')): + 'subjectArea', 'mainSubjectArea', 'areaOfInterest')): continue - if 'dc:description' in region and not note['content']: - note['content'] = MD_LangAlt( - region['dc:description']).best_match() - if 'Iptc4xmpExt:Name' in region and not note['content']: - note['content'] = MD_LangAlt( - region['Iptc4xmpExt:Name']).best_match() + if not note['content']: + note['content'] = region['dc:description'].best_match() + if not note['content']: + note['content'] = ', '.join(region['Iptc4xmpExt:PersonInImage']) + if not note['content']: + note['content'] = region['Iptc4xmpExt:Name'].best_match() if not note['content']: continue result.append(note) return result - - def get_focus(self, image): - image_dims = image.metadata.dimensions - portrait_format = image_dims['height'] > image_dims['width'] - transform = (image.metadata.orientation - and image.metadata.orientation.get_transform()) - if transform and transform.isRotating(): - portrait_format = not portrait_format - if portrait_format: - roles = ('imgregrole:landscapeCropping', - 'imgregrole:squareCropping', - 'imgregrole:recomCropping', - 'imgregrole:cropping', - 'imgregrole:portraitCropping') - else: - roles = ('imgregrole:squareCropping', - 'imgregrole:portraitCropping', - 'imgregrole:landscapeCropping', - 'imgregrole:recomCropping', - 'imgregrole:cropping') - for role in roles: - for region in self: - if not region.has_role(role): - continue - points = region.to_Qt(image) - boundary = region['Iptc4xmpExt:RegionBoundary'] - if boundary['Iptc4xmpExt:rbShape'] == 'rectangle': - centre = (points.at(0) + points.at(1)) / 2.0 - elif boundary['Iptc4xmpExt:rbShape'] == 'circle': - centre = points.at(0) - else: - centre = points.boundingRect().center() - if transform: - centre = transform.map(centre) - return (centre.x() * 2.0) - 1.0, 1.0 - (centre.y() * 2.0) - return None diff --git a/src/photini/vocab.py b/src/photini/vocab.py new file mode 100644 index 00000000..8ce39e38 --- /dev/null +++ b/src/photini/vocab.py @@ -0,0 +1,124 @@ +# Photini - a simple photo metadata editor. +# http://github.com/jim-easterbrook/Photini +# Copyright (C) 2024 Jim Easterbrook jim@jim-easterbrook.me.uk +# +# This file is part of Photini. +# +# Photini is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# Photini is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License +# along with Photini. If not, see . + +# Stuff to handle "controlled vocabularies" from IPTC and others + +from photini.cv import image_region_types, image_region_roles + + +class IPTCBaseCV(object): + @classmethod + def data_for_name(cls, name): + return cls.vocab[name]['data'] + + +class IPTCRoleCV(IPTCBaseCV): + vocab = image_region_roles + + +class IPTCTypeCV(IPTCBaseCV): + vocab = image_region_types + + +class MWGTypeCV(IPTCBaseCV): + vocab = { + 'Face': { + 'data': {'Iptc4xmpExt:Name': {'en-GB': 'Face'}, + 'xmp:Identifier': ('Face',)}, + 'file_data': {'mwg-rs:Type': 'Face'}, + 'definition': {'en-GB': "Region area for people's faces."}, + 'note': None}, + 'Pet': { + 'data': {'Iptc4xmpExt:Name': {'en-GB': 'Pet'}, + 'xmp:Identifier': ('Pet',)}, + 'file_data': {'mwg-rs:Type': 'Pet'}, + 'definition': {'en-GB': "Region area for pets."}, + 'note': None}, + 'FocusEvaluatedUsed': { + 'data': {'Iptc4xmpExt:Name': {'en-GB': 'Focus (EvaluatedUsed)'}, + 'xmp:Identifier': ('FocusEvaluatedUsed',)}, + 'file_data': {'mwg-rs:Type': 'Focus', + 'mwg-rs:FocusUsage': 'EvaluatedUsed'}, + 'definition': {'en-GB': "Region area for camera auto-focus regions." + "
EvaluatedUsed specifies that the focus point" + " was considered during focusing and was used in the" + " final image."}, + 'note': None}, + 'FocusEvaluatedNotUsed': { + 'data': {'Iptc4xmpExt:Name': {'en-GB': 'Focus (EvaluatedNotUsed)'}, + 'xmp:Identifier': ('FocusEvaluatedNotUsed',)}, + 'file_data': {'mwg-rs:Type': 'Focus', + 'mwg-rs:FocusUsage': 'EvaluatedNotUsed'}, + 'definition': {'en-GB': "Region area for camera auto-focus regions." + "
EvaluatedNotUsed specifies that the focus" + " point was considered during focusing but not" + " utilised in the final image."}, + 'note': None}, + 'FocusNotEvaluatedNotUsed': { + 'data': { + 'Iptc4xmpExt:Name': {'en-GB': 'Focus (NotEvaluatedNotUsed)'}, + 'xmp:Identifier': ('FocusNotEvaluatedNotUsed',)}, + 'file_data': {'mwg-rs:Type': 'Focus', + 'mwg-rs:FocusUsage': 'NotEvaluatedNotUsed'}, + 'definition': {'en-GB': "Region area for camera auto-focus regions." + "
NotEvaluatedNotUsed specifies that a focus" + " point was not evaluated and not used, e.g. a fixed" + " focus point on the camera which was not used in" + " any fashion."}, + 'note': None}, + 'BarCode': { + 'data': {'Iptc4xmpExt:Name': {'en-GB': 'BarCode'}, + 'xmp:Identifier': ('BarCode',)}, + 'file_data': {'mwg-rs:Type': 'BarCode'}, + 'definition': {'en-GB': "One dimensional linear or two dimensional" + " matrix optical code."}, + 'note': None}, + } + + @classmethod + def clean_file_data(cls, ctype_data): + # remove any MWG ctype from a list of ctypes + result = list(ctype_data) + for item in cls.vocab.values(): + if item['data'] in result: + result.remove(item['data']) + break + return result + + @classmethod + def to_file_data(cls, ctype_data): + for item in cls.vocab.values(): + if item['data'] in ctype_data: + return item['file_data'] + return {} + + @classmethod + def from_file_data(cls, data): + if 'mwg-rs:Type' not in data: + return {} + label = data['mwg-rs:Type'] + if 'mwg-rs:FocusUsage' in data: + label += data['mwg-rs:FocusUsage'] + if label in cls.vocab: + return cls.vocab[label]['data'] + name = data['mwg-rs:Type'] + if 'mwg-rs:FocusUsage' in data: + name = '{} ({})'.format(name, data['mwg-rs:FocusUsage']) + return {'Iptc4xmpExt:Name': {'en-GB': name}, + 'xmp:Identifier': (label,)} diff --git a/utils/download_cvs.py b/utils/download_cvs.py index ff4ae6aa..af25cc27 100644 --- a/utils/download_cvs.py +++ b/utils/download_cvs.py @@ -17,7 +17,7 @@ # . import os -from pprint import pprint +import pprint import sys import requests @@ -44,36 +44,28 @@ def main(argv=None): rsp = session.get(url, params=params) rsp.raise_for_status() rsp = rsp.json() - pprint(rsp) + pprint.pprint(rsp) py.write('''# ©{copyrightHolder} # Date: {dateReleased} # Licence: {licenceLink} # {uri} '''.format(**rsp)) - uris = [] - data = {} + py.write(data_name) + py.write(' = {\n') for concept in rsp['conceptSet']: - uri = concept['uri'] - if uri not in uris: - uris.append(uri) - data[uri] = {'name': {}, 'definition': {}, 'note': {}} - data[uri]['name'].update(concept['prefLabel']) - data[uri]['definition'].update(concept['definition']) + key = concept['qcode'].split(':')[1] + value = { + 'data': { + 'Iptc4xmpExt:Name': concept['prefLabel'], + 'xmp:Identifier': (concept['uri'],), + }, + 'definition': concept['definition'], + 'note': {}, + } if 'note' in concept: - data[uri]['note'].update(concept['note']) - data[uri]['qcode'] = concept['qcode'] - data[uri]['data'] = { - 'xmp:Identifier': (concept['uri'],), - 'Iptc4xmpExt:Name': concept['prefLabel']} - py.write(data_name) - py.write(' = \\\n') - pprint(tuple(data[x] for x in uris), stream=py) - py.write('\n') - py.write(data_name) - py.write('_idx = \\\n') - pprint(dict((data[x]['qcode'], n) - for n, x in enumerate(uris)), stream=py) - py.write('\n') + value['note'].update(concept['note']) + py.write("'{}':\n{},\n".format(key, pprint.pformat(value))) + py.write('}\n') return 0