Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
875f79b92d | ||
|
|
3a110bb6d3 | ||
|
|
e73327c164 | ||
|
|
3799594473 | ||
|
|
db430173b5 | ||
|
|
defe5cb61a |
@@ -156,6 +156,16 @@
|
||||
"bug",
|
||||
"userTesting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "martinhrpi",
|
||||
"name": "Martin",
|
||||
"avatar_url": "https://avatars2.githubusercontent.com/u/19407684?v=4",
|
||||
"profile": "https://github.com/martinhrpi",
|
||||
"contributions": [
|
||||
"research",
|
||||
"userTesting"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
14
CHANGELOG.md
14
CHANGELOG.md
@@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file. Dates are d
|
||||
|
||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
|
||||
#### [v0.39.22](https://github.com/RhetTbull/osxphotos/compare/v0.39.21...v0.39.22)
|
||||
|
||||
> 18 January 2021
|
||||
|
||||
- Beta fix for Digikam reading XMP [`3799594`](https://github.com/RhetTbull/osxphotos/commit/379959447373f951ffca372598ea8f1d5834fe52)
|
||||
- Add @martinhrpi as a contributor [`db43017`](https://github.com/RhetTbull/osxphotos/commit/db430173b59732f944ca52b53c928370684580df)
|
||||
|
||||
#### [v0.39.21](https://github.com/RhetTbull/osxphotos/compare/v0.39.20...v0.39.21)
|
||||
|
||||
> 18 January 2021
|
||||
|
||||
- Added beta support for face regions in xmp [`2773ff7`](https://github.com/RhetTbull/osxphotos/commit/2773ff73815ef4667f88a45b016539e490d31769)
|
||||
- Fixed osxphotos.spec datas [`f58f8dd`](https://github.com/RhetTbull/osxphotos/commit/f58f8dd804f432d07048b98e5dcedca57fec0a5e)
|
||||
|
||||
#### [v0.39.20](https://github.com/RhetTbull/osxphotos/compare/v0.39.19...v0.39.20)
|
||||
|
||||
> 16 January 2021
|
||||
|
||||
19
README.md
19
README.md
@@ -3,7 +3,7 @@
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://github.com/RhetTbull/osxphotos/workflows/Tests/badge.svg)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
[](#contributors)
|
||||
[](#contributors)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
|
||||
OSXPhotos provides the ability to interact with and query Apple's Photos.app library on macOS. You can query the Photos library database — for example, file name, file path, and metadata such as keywords/tags, persons/faces, albums, etc. You can also easily export both the original and edited photos.
|
||||
@@ -2268,6 +2268,22 @@ UUID of the photo this face is associated with.
|
||||
#### `photo`
|
||||
[PhotoInfo](#photoinfo) object representing the photo that contains this face.
|
||||
|
||||
#### `mwg_rs_area`
|
||||
Returns named tuple with following coordinates as used in Metdata Working Group (mwg) face regions in XMP files.
|
||||
|
||||
* `x` = `stArea:x`
|
||||
* `y` = `stArea:y`
|
||||
* `h` = `stArea:h`
|
||||
* `w` = `stArea:w`
|
||||
|
||||
#### `mpri_reg_rect`
|
||||
Returnes named tuple with following coordinates as used in Microsoft Photo Region Rectangle (mpri) in XMP files.
|
||||
|
||||
* `x` = x coordinate of top left corner of rectangle
|
||||
* `y` = y coordinate of top left corner of rectangle
|
||||
* `h` = height of rectangle
|
||||
* `w` = width of rectangle
|
||||
|
||||
#### `face_rect()`
|
||||
Returns list of x, y coordinates as tuples `[(x0, y0), (x1, y1)]` representing the corners of rectangular region that contains the face. Coordinates are in same format and [reference frame](https://pillow.readthedocs.io/en/stable/handbook/concepts.html#coordinate-system) as used by [Pillow](https://pypi.org/project/Pillow/) imaging library. **Note**: face_rect() and all other properties/methods that return coordinates refer to the *current version* of the image. E.g. if the image has been edited ([`PhotoInfo.hasadjustments`](#hasadjustments)), these refer to [`PhotoInfo.path_edited`](#pathedited). If the image has no adjustments, these coordinates refer to the original photo ([`PhotoInfo.path`](#path)).
|
||||
|
||||
@@ -2595,6 +2611,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/Rott-Apple"><img src="https://avatars1.githubusercontent.com/u/67875570?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Rott-Apple</b></sub></a><br /><a href="#research-Rott-Apple" title="Research">🔬</a></td>
|
||||
<td align="center"><a href="https://github.com/narensankar0529"><img src="https://avatars3.githubusercontent.com/u/74054766?v=4?s=75" width="75px;" alt=""/><br /><sub><b>narensankar0529</b></sub></a><br /><a href="https://github.com/RhetTbull/osxphotos/issues?q=author%3Anarensankar0529" title="Bug reports">🐛</a> <a href="#userTesting-narensankar0529" title="User Testing">📓</a></td>
|
||||
<td align="center"><a href="https://github.com/martinhrpi"><img src="https://avatars2.githubusercontent.com/u/19407684?v=4?s=75" width="75px;" alt=""/><br /><sub><b>Martin</b></sub></a><br /><a href="#research-martinhrpi" title="Research">🔬</a> <a href="#userTesting-martinhrpi" title="User Testing">📓</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
""" version info """
|
||||
|
||||
__version__ = "0.39.21"
|
||||
__version__ = "0.39.23"
|
||||
|
||||
@@ -235,8 +235,8 @@ class FaceInfo:
|
||||
Reference:
|
||||
https://photo.stackexchange.com/questions/106410/how-does-xmp-define-the-face-region
|
||||
"""
|
||||
x = self.center_x
|
||||
y = 1.0 - self.center_y
|
||||
x, y = self.center_x, self.center_y
|
||||
x, y = self._fix_orientation((x, y))
|
||||
h = self.size_pixels / self.photo.height
|
||||
w = self.size_pixels / self.photo.width
|
||||
|
||||
@@ -256,8 +256,10 @@ class FaceInfo:
|
||||
Reference:
|
||||
https://docs.microsoft.com/en-us/windows/win32/wic/-wic-people-tagging
|
||||
"""
|
||||
x = self.center_x - self.size_pixels / self.photo.width / 2
|
||||
y = 1.0 - self.center_y - self.size_pixels / self.photo.height / 2
|
||||
x, y = self.center_x, self.center_y
|
||||
x, y = self._fix_orientation((x, y))
|
||||
x = x - self.size_pixels / self.photo.width / 2
|
||||
y = y - self.size_pixels / self.photo.height / 2
|
||||
|
||||
# though the docs clearly say height, width, these appear to be flipped
|
||||
h = self.size_pixels / self.photo.width
|
||||
@@ -308,6 +310,33 @@ class FaceInfo:
|
||||
_, _, yaw = self.roll_pitch_yaw()
|
||||
return yaw
|
||||
|
||||
def _fix_orientation(self, xy):
|
||||
""" Translate an (x, y) tuple based on image orientation
|
||||
|
||||
Arguments:
|
||||
xy: tuple of (x, y) coordinates for point to translate
|
||||
in format used by Photos (percent of height/width)
|
||||
|
||||
Returns:
|
||||
(x, y) tuple of translated coordinates
|
||||
"""
|
||||
# Reference: https://github.com/neilpa/phace/blob/7594776480505d0c389688a42099c94ac5d34f3f/cmd/phace/draw.go#L79-L94
|
||||
|
||||
orientation = self.photo.orientation
|
||||
x, y = xy
|
||||
if orientation in [1, 2]:
|
||||
y = 1.0 - y
|
||||
elif orientation in [3, 4]:
|
||||
x = 1.0 - x
|
||||
elif orientation in [5, 6]:
|
||||
x, y = 1.0 - y, 1.0 - x
|
||||
elif orientation in [7, 8]:
|
||||
x, y = y, x
|
||||
else:
|
||||
logging.warning(f"Unhandled orientation: {orientation}")
|
||||
|
||||
return (x, y)
|
||||
|
||||
def _make_point(self, xy):
|
||||
""" Translate an (x, y) tuple based on image orientation
|
||||
and convert to image coordinates
|
||||
@@ -322,22 +351,11 @@ class FaceInfo:
|
||||
# Reference: https://github.com/neilpa/phace/blob/7594776480505d0c389688a42099c94ac5d34f3f/cmd/phace/draw.go#L79-L94
|
||||
|
||||
orientation = self.photo.orientation
|
||||
x, y = xy
|
||||
x, y = self._fix_orientation(xy)
|
||||
dx = self.photo.width
|
||||
dy = self.photo.height
|
||||
if orientation in [1, 2]:
|
||||
y = 1.0 - y
|
||||
elif orientation in [3, 4]:
|
||||
x = 1.0 - x
|
||||
elif orientation in [5, 6]:
|
||||
x, y = 1.0 - y, 1.0 - x
|
||||
if orientation in [5, 6, 7, 8]:
|
||||
dx, dy = dy, dx
|
||||
elif orientation in [7, 8]:
|
||||
x, y = y, x
|
||||
dx, dy = dy, dx
|
||||
else:
|
||||
logging.warning(f"Unhandled orientation: {orientation}")
|
||||
|
||||
return (int(x * dx), int(y * dy))
|
||||
|
||||
def _make_point_with_rotation(self, xy):
|
||||
|
||||
@@ -1715,6 +1715,7 @@ def _xmp_sidecar(
|
||||
use_persons_as_keywords: treat person names as keywords
|
||||
keyword_template: (list of strings); list of template strings to render as keywords
|
||||
description_template: string; optional template string that will be rendered for use as photo description
|
||||
extension: which extension to use for SidecarForExtension property
|
||||
merge_exif_keywords: boolean; if True, merged keywords found in file's exif data (requires exiftool)
|
||||
merge_exif_persons: boolean; if True, merged persons found in file's exif data (requires exiftool)
|
||||
"""
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
% else:
|
||||
<dc:description>
|
||||
<rdf:Alt>
|
||||
<rdf:li xml:lang='x-default'>${desc | x}</rdf:li>
|
||||
<rdf:li xml:lang='x-default'>${desc | x}</rdf:li>
|
||||
</rdf:Alt>
|
||||
</dc:description>
|
||||
% endif
|
||||
@@ -43,7 +43,7 @@
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
% for subj in subject:
|
||||
<rdf:li>${subj | x}</rdf:li>
|
||||
<rdf:li>${subj | x}</rdf:li>
|
||||
% endfor
|
||||
</rdf:Bag>
|
||||
</dc:subject>
|
||||
@@ -59,11 +59,11 @@
|
||||
<%def name="iptc_personinimage(persons)">
|
||||
% if persons:
|
||||
<Iptc4xmpExt:PersonInImage>
|
||||
<rdf:Bag>
|
||||
% for person in persons:
|
||||
<rdf:li>${person | x}</rdf:li>
|
||||
% endfor
|
||||
</rdf:Bag>
|
||||
<rdf:Bag>
|
||||
% for person in persons:
|
||||
<rdf:li>${person | x}</rdf:li>
|
||||
% endfor
|
||||
</rdf:Bag>
|
||||
</Iptc4xmpExt:PersonInImage>
|
||||
% endif
|
||||
</%def>
|
||||
@@ -71,11 +71,11 @@
|
||||
<%def name="dk_tagslist(keywords)">
|
||||
% if keywords:
|
||||
<digiKam:TagsList>
|
||||
<rdf:Seq>
|
||||
% for keyword in keywords:
|
||||
<rdf:li>${keyword | x}</rdf:li>
|
||||
% endfor
|
||||
</rdf:Seq>
|
||||
<rdf:Seq>
|
||||
% for keyword in keywords:
|
||||
<rdf:li>${keyword | x}</rdf:li>
|
||||
% endfor
|
||||
</rdf:Seq>
|
||||
</digiKam:TagsList>
|
||||
% endif
|
||||
</%def>
|
||||
@@ -105,37 +105,44 @@
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="face_regions(photo)">
|
||||
<%def name="mwg_face_regions(photo)">
|
||||
% if photo.face_info:
|
||||
<mwg-rs:Regions rdf:parseType="Resource">
|
||||
<mwg-rs:AppliedToDimensions stDim:w=${photo.width} stDim:h=${photo.height} stDim:unit="pixel"/>
|
||||
<mwg-rs:RegionList>
|
||||
<rdf:Bag>
|
||||
% for face in photo.face_info:
|
||||
<rdf:li>
|
||||
<rdf:Description
|
||||
mwg-rs:Rotation="${face.roll}"
|
||||
mwg-rs:Name="${face.name}"
|
||||
mwg-rs:Type="Face">
|
||||
<mwg-rs:Area
|
||||
stArea:h="${'{0:.6f}'.format(face.mwg_rs_area.h)}"
|
||||
stArea:w="${'{0:.6f}'.format(face.mwg_rs_area.w)}"
|
||||
stArea:x="${'{0:.6f}'.format(face.mwg_rs_area.x)}"
|
||||
stArea:y="${'{0:.6f}'.format(face.mwg_rs_area.y)}"
|
||||
stArea:unit="normalized"/>
|
||||
</rdf:Description>
|
||||
</rdf:li>
|
||||
% endfor
|
||||
</rdf:Bag>
|
||||
</mwg-rs:RegionList>
|
||||
<mwg-rs:AppliedToDimensions rdf:parseType="Resource">
|
||||
<stDim:unit>pixel</stDim:unit>
|
||||
</mwg-rs:AppliedToDimensions>
|
||||
<mwg-rs:RegionList>
|
||||
<rdf:Bag>
|
||||
% for face in photo.face_info:
|
||||
<rdf:li rdf:parseType="Resource">
|
||||
<mwg-rs:Area rdf:parseType="Resource">
|
||||
<stArea:h>${'{0:.6f}'.format(face.mwg_rs_area.h)}</stArea:h>
|
||||
<stArea:w>${'{0:.6f}'.format(face.mwg_rs_area.w)}</stArea:w>
|
||||
<stArea:x>${'{0:.6f}'.format(face.mwg_rs_area.x)}</stArea:x>
|
||||
<stArea:y>${'{0:.6f}'.format(face.mwg_rs_area.y)}</stArea:y>
|
||||
<stArea:unit>normalized</stArea:unit>
|
||||
</mwg-rs:Area>
|
||||
<mwg-rs:Name>${face.name}</mwg-rs:Name>
|
||||
<mwg-rs:Rotation>${face.roll}</mwg-rs:Rotation>
|
||||
<mwg-rs:Type>Face</mwg-rs:Type>
|
||||
</rdf:li>
|
||||
% endfor
|
||||
</rdf:Bag>
|
||||
</mwg-rs:RegionList>
|
||||
</mwg-rs:Regions>
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
<%def name="mpri_face_regions(photo)">
|
||||
% if photo.face_info:
|
||||
<MP:RegionInfo rdf:parseType="Resource">
|
||||
<MPRI:Regions>
|
||||
<rdf:Bag>
|
||||
% for face in photo.face_info:
|
||||
<rdf:li
|
||||
MPReg:Rectangle="${'{0:.6f}'.format(face.mpri_reg_rect.x)}, ${'{0:.6f}'.format(face.mpri_reg_rect.y)}, ${'{0:.6f}'.format(face.mpri_reg_rect.h)}, ${'{0:.6f}'.format(face.mpri_reg_rect.w)}"
|
||||
MPReg:PersonDisplayName="${face.name}"/>
|
||||
<rdf:li rdf:parseType="Resource">
|
||||
<MPReg:PersonDisplayName>${face.name}</MPReg:PersonDisplayName>
|
||||
<MPReg:Rectangle>${'{0:.6f}'.format(face.mpri_reg_rect.x)}, ${'{0:.6f}'.format(face.mpri_reg_rect.y)}, ${'{0:.6f}'.format(face.mpri_reg_rect.h)}, ${'{0:.6f}'.format(face.mpri_reg_rect.w)}</MPReg:Rectangle>
|
||||
</rdf:li>
|
||||
% endfor
|
||||
</rdf:Bag>
|
||||
</MPRI:Regions>
|
||||
@@ -143,46 +150,61 @@
|
||||
% endif
|
||||
</%def>
|
||||
|
||||
|
||||
<?xpacket begin="${"\uFEFF"}" id="W5M0MpCehiHzreSzNTczkc9d"?>
|
||||
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="osxphotos ${version}">
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/">
|
||||
${photoshop_sidecar_for_extension(extension)}
|
||||
${dc_description(description)}
|
||||
${dc_title(photo.title)}
|
||||
${dc_subject(subjects)}
|
||||
${dc_datecreated(photo.date)}
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:Iptc4xmpExt='http://iptc.org/std/Iptc4xmpExt/2008-02-29/'>
|
||||
${iptc_personinimage(persons)}
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:digiKam='http://www.digikam.org/ns/1.0/'>
|
||||
${dk_tagslist(keywords)}
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:xmp='http://ns.adobe.com/xap/1.0/'>
|
||||
${adobe_createdate(photo.date)}
|
||||
${adobe_modifydate(photo.date)}
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:exif='http://ns.adobe.com/exif/1.0/'>
|
||||
${gps_info(*photo.location)}
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=''
|
||||
xmlns:tiff='http://ns.adobe.com/tiff/1.0/'>
|
||||
${orientation(photo.orientation)}
|
||||
</rdf:Description>
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:mwg-rs="http://www.metadataworkinggroup.com/schemas/regions/"
|
||||
xmlns:stArea="http://ns.adobe.com/xmp/sType/Area#"
|
||||
xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#"
|
||||
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#">
|
||||
${face_regions(photo)}
|
||||
</rdf:Description>
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/">
|
||||
${photoshop_sidecar_for_extension(extension)}
|
||||
${dc_description(description)}
|
||||
${dc_title(photo.title)}
|
||||
${dc_subject(subjects)}
|
||||
${dc_datecreated(photo.date)}
|
||||
</rdf:Description>
|
||||
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:Iptc4xmpExt='http://iptc.org/std/Iptc4xmpExt/2008-02-29/'>
|
||||
${iptc_personinimage(persons)}
|
||||
</rdf:Description>
|
||||
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:digiKam='http://www.digikam.org/ns/1.0/'>
|
||||
${dk_tagslist(keywords)}
|
||||
</rdf:Description>
|
||||
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:xmp='http://ns.adobe.com/xap/1.0/'>
|
||||
${adobe_createdate(photo.date)}
|
||||
${adobe_modifydate(photo.date)}
|
||||
</rdf:Description>
|
||||
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:exif='http://ns.adobe.com/exif/1.0/'>
|
||||
${gps_info(*photo.location)}
|
||||
</rdf:Description>
|
||||
|
||||
<rdf:Description rdf:about=''
|
||||
xmlns:tiff='http://ns.adobe.com/tiff/1.0/'>
|
||||
${orientation(photo.orientation)}
|
||||
</rdf:Description>
|
||||
|
||||
<rdf:Description rdf:about=""
|
||||
xmlns:mwg-rs="http://www.metadataworkinggroup.com/schemas/regions/"
|
||||
xmlns:stArea="http://ns.adobe.com/xmp/sType/Area#"
|
||||
xmlns:stDim="http://ns.adobe.com/xap/1.0/sType/Dimensions#">
|
||||
${mwg_face_regions(photo)}
|
||||
</rdf:Description>
|
||||
|
||||
|
||||
<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#">
|
||||
${mpri_face_regions(photo)}
|
||||
</rdf:Description>
|
||||
|
||||
</rdf:RDF>
|
||||
</x:xmpmeta>
|
||||
<?xpacket end="w"?>
|
||||
Reference in New Issue
Block a user