From d00cf9420ef92d8b17458ac867534e950dc8acd9 Mon Sep 17 00:00:00 2001 From: "Patrik J. Braun" Date: Sun, 27 Dec 2020 14:35:30 +0100 Subject: [PATCH] Adding test to exiftool based face region parsing --- src/backend/model/threading/MetadataLoader.ts | 81 ++++++++++-------- .../backend/assets/exiftool.jpg | Bin test/backend/assets/exiftool.json | 42 +++++++++ .../model/threading/MetaDataLoader.spec.ts | 7 ++ 4 files changed, 93 insertions(+), 37 deletions(-) rename demo/images/IMG_6253_exiftool.jpg => test/backend/assets/exiftool.jpg (100%) create mode 100644 test/backend/assets/exiftool.json diff --git a/src/backend/model/threading/MetadataLoader.ts b/src/backend/model/threading/MetadataLoader.ts index f7f4e594..47fc9581 100644 --- a/src/backend/model/threading/MetadataLoader.ts +++ b/src/backend/model/threading/MetadataLoader.ts @@ -210,45 +210,52 @@ export class MetadataLoader { const faces: FaceRegion[] = []; if (ret.Regions && ret.Regions.value.RegionList && ret.Regions.value.RegionList.value) { for (let i = 0; i < ret.Regions.value.RegionList.value.length; i++) { - if (ret.Regions.value.RegionList.value[i].value && - ret.Regions.value.RegionList.value[i].value['rdf:Description'] && - ret.Regions.value.RegionList.value[i].value['rdf:Description'].value && - ret.Regions.value.RegionList.value[i].value['rdf:Description'].value['mwg-rs:Area']) { - const region = ret.Regions.value.RegionList.value[i].value['rdf:Description']; - const regionBox = ret.Regions.value.RegionList.value[i].value['rdf:Description'].value['mwg-rs:Area'].attributes; - if (region.attributes['mwg-rs:Type'] !== 'Face' || - !region.attributes['mwg-rs:Name']) { - continue; - } - const name = region.attributes['mwg-rs:Name']; - const box = { - width: Math.round(parseFloat('' + regionBox['stArea:w']) * metadata.size.width), - height: Math.round(parseFloat('' + regionBox['stArea:h']) * metadata.size.height), - left: Math.round(parseFloat('' + regionBox['stArea:x']) * metadata.size.width), - top: Math.round(parseFloat('' + regionBox['stArea:y']) * metadata.size.height) + + let type, name, box; + const regionRoot = ret.Regions.value.RegionList.value[i] as any; + const createFaceBox = (w: string, h: string, x: string, y: string) => { + return { + width: Math.round(parseFloat(w) * metadata.size.width), + height: Math.round(parseFloat(h) * metadata.size.height), + left: Math.round(parseFloat(x) * metadata.size.width), + top: Math.round(parseFloat(y) * metadata.size.height) }; - // convert center base box to corner based box - box.left = Math.max(0, box.left - box.width / 2); - box.top = Math.max(0, box.top - box.height / 2); - faces.push({name: name, box: box}); - } else if((ret.Regions.value.RegionList.value[i] as any).Area && - (ret.Regions.value.RegionList.value[i] as any).Name && - (ret.Regions.value.RegionList.value[i] as any).Type) { - const regionBox = (ret.Regions.value.RegionList.value[i] as any).Area.value; - const name = (ret.Regions.value.RegionList.value[i] as any).Name.value; - const type = (ret.Regions.value.RegionList.value[i] as any).Type.value; - if (type !== 'Face') continue; - const box = { - width: Math.round(parseFloat(regionBox.w.value) * metadata.size.width), - height: Math.round(parseFloat(regionBox.h.value) * metadata.size.height), - left: Math.round(parseFloat(regionBox.x.value) * metadata.size.width), - top: Math.round(parseFloat(regionBox.y.value) * metadata.size.height) - }; - // convert center base box to corner based box - box.left = Math.max(0, box.left - box.width / 2); - box.top = Math.max(0, box.top - box.height / 2); - faces.push({name: name, box: box}); + }; + + /* Adobe Lightroom based face region structure*/ + if (regionRoot.value && + regionRoot.value['rdf:Description'] && + regionRoot.value['rdf:Description'].value && + regionRoot.value['rdf:Description'].value['mwg-rs:Area']) { + + const region = regionRoot.value['rdf:Description']; + const regionBox = region.value['mwg-rs:Area'].attributes; + + name = region.attributes['mwg-rs:Name']; + type = region.attributes['mwg-rs:Type']; + box = createFaceBox(regionBox['stArea:w'], + regionBox['stArea:h'], + regionBox['stArea:x'], + regionBox['stArea:y']); + /* Load exiftool edited face region structure, see github issue #191 */ + } else if (regionRoot.Area && regionRoot.Name && regionRoot.Type) { + + const regionBox = regionRoot.Area.value; + name = regionRoot.Name.value; + type = regionRoot.Type.value; + box = createFaceBox(regionBox.w.value, + regionBox.h.value, + regionBox.x.value, + regionBox.y.value); } + + if (type !== 'Face' || !name) { + continue; + } + // convert center base box to corner based box + box.left = Math.max(0, box.left - box.width / 2); + box.top = Math.max(0, box.top - box.height / 2); + faces.push({name: name, box: box}); } } if (Config.Client.Faces.keywordsToPersons && faces.length > 0) { diff --git a/demo/images/IMG_6253_exiftool.jpg b/test/backend/assets/exiftool.jpg similarity index 100% rename from demo/images/IMG_6253_exiftool.jpg rename to test/backend/assets/exiftool.jpg diff --git a/test/backend/assets/exiftool.json b/test/backend/assets/exiftool.json new file mode 100644 index 00000000..5e88316f --- /dev/null +++ b/test/backend/assets/exiftool.json @@ -0,0 +1,42 @@ +{ + "cameraData": { + "ISO": 160, + "exposure": 0.0008, + "fStop": 5.6, + "focalLength": 85, + "lens": "EF-S15-85mm f/3.5-5.6 IS USM", + "make": "Canon", + "model": "Canon EOS 600D" + }, + "creationDate": 1434116432000, + "faces": [ + { + "box": { + "height": 626, + "left": 374.5, + "top": 353, + "width": 669 + }, + "name": "Alvin the Squirrel" + } + ], + "fileSize": 909930, + "keywords": [ + "USA", + "Yosemite" + ], + "orientation": 1, + "positionData": { + "GPSData": { + "altitude": 1960, + "latitude": 37.746765, + "longitude": -119.52988333333333 + }, + "country": "United States", + "state": "California" + }, + "size": { + "height": 1132, + "width": 1697 + } +} diff --git a/test/backend/unit/model/threading/MetaDataLoader.spec.ts b/test/backend/unit/model/threading/MetaDataLoader.spec.ts index d6dfa24f..c65765e9 100644 --- a/test/backend/unit/model/threading/MetaDataLoader.spec.ts +++ b/test/backend/unit/model/threading/MetaDataLoader.spec.ts @@ -32,6 +32,13 @@ describe('MetadataLoader', () => { }); + it('should load jpg edited with exiftool', async () => { + const data = await MetadataLoader.loadPhotoMetadata(path.join(__dirname, '/../../../assets/exiftool.jpg')); + const expected = require(path.join(__dirname, '/../../../assets/exiftool.json')); + expect(Utils.clone(data)).to.be.deep.equal(expected); + }); + + it('should load mp4', async () => { const data = await MetadataLoader.loadVideoMetadata(path.join(__dirname, '/../../../assets/video.mp4')); const expected = require(path.join(__dirname, '/../../../assets/video.json'));