235 lines
9.6 KiB
Plaintext
235 lines
9.6 KiB
Plaintext
|
#------------------------------------------------------------------------------
|
||
|
# File: convert_regions.config
|
||
|
#
|
||
|
# Description: User-defined Composite tag definitions to allow conversion of
|
||
|
# face regions between Microsoft Windows Live Photo Gallery (WLPG),
|
||
|
# Metadata Working Group (MWG), and IPTC Extension formats
|
||
|
#
|
||
|
# Usage: 1) Convert from MP WLPG or IPTC regions to MWG regions:
|
||
|
#
|
||
|
# exiftool -config convert_regions.config "-RegionInfo<MPRegion2MWGRegion" FILE
|
||
|
#
|
||
|
# 2) Convert from MWG or IPTC to MP WLPG regions:
|
||
|
#
|
||
|
# exiftool -config convert_regions.config "-RegionInfoMP<MWGRegion2MPRegion" FILE
|
||
|
#
|
||
|
# 3) Convert from MWG or MP WLPG to IPTC regions:
|
||
|
#
|
||
|
# exiftool -config convert_regions.config "-ImageRegion<MWGRegion2IPTCRegion" FILE
|
||
|
#
|
||
|
# Requires: ExifTool version 11.74 or later
|
||
|
#
|
||
|
# Revisions: 2012/12/27 - P. Harvey Created
|
||
|
# 2013/02/20 - PH Don't add ignored MP faces
|
||
|
# 2017/02/13 - PH Handle MP regions without Rectangle or Name entries
|
||
|
# 2019/10/26 - PH Added support for the new IPTC ImageRegion
|
||
|
# 2021/05-27 - PH Changed a few tag names and added shortcuts for
|
||
|
# backward compatibility
|
||
|
#
|
||
|
# References: http://www.metadataworkinggroup.org/specs/
|
||
|
#------------------------------------------------------------------------------
|
||
|
|
||
|
%Image::ExifTool::UserDefined = (
|
||
|
|
||
|
'Image::ExifTool::Composite' => {
|
||
|
|
||
|
# create an MWG RegionInfo structure from a Microsoft RegionInfoMP structure
|
||
|
MPRegion2MWGRegion => {
|
||
|
Require => {
|
||
|
0 => 'RegionInfoMP',
|
||
|
1 => 'ImageWidth',
|
||
|
2 => 'ImageHeight',
|
||
|
},
|
||
|
ValueConv => q{
|
||
|
my ($rgn, @newRgns);
|
||
|
foreach $rgn (@{$val[0]{Regions}}) {
|
||
|
my $name = $$rgn{PersonDisplayName};
|
||
|
next unless $$rgn{Rectangle} or defined $name;
|
||
|
my %newRgn = ( Type => 'Face' );
|
||
|
if (defined $name) {
|
||
|
# don't add ignored faces
|
||
|
next if $name eq 'ffffffffffffffff';
|
||
|
$newRgn{Name} = $name;
|
||
|
}
|
||
|
if ($$rgn{Rectangle}) {
|
||
|
my @rect = split /\s*,\s*/, $$rgn{Rectangle};
|
||
|
$newRgn{Area} = {
|
||
|
X => $rect[0] + $rect[2]/2,
|
||
|
Y => $rect[1] + $rect[3]/2,
|
||
|
W => $rect[2],
|
||
|
H => $rect[3],
|
||
|
Unit => 'normalized',
|
||
|
} if @rect == 4;
|
||
|
}
|
||
|
push @newRgns, \%newRgn;
|
||
|
}
|
||
|
return {
|
||
|
AppliedToDimensions => { W => $val[1], H => $val[2], Unit => 'pixel' },
|
||
|
RegionList => \@newRgns,
|
||
|
};
|
||
|
},
|
||
|
},
|
||
|
|
||
|
# create an MWG RegionInfo structure from an IPTC ImageRegion list
|
||
|
IPTCRegion2MWGRegion => {
|
||
|
Name => 'MPRegion2MWGRegion',
|
||
|
Require => {
|
||
|
0 => 'ImageRegion',
|
||
|
1 => 'ImageWidth',
|
||
|
2 => 'ImageHeight',
|
||
|
},
|
||
|
ValueConv => q{
|
||
|
my ($rgn, @newRgns);
|
||
|
my $rgns = ref $val[0] eq 'ARRAY' ? $val[0] : [ $val[0] ];
|
||
|
foreach $rgn (@$rgns) {
|
||
|
my %newRgn = ( Type => 'Face' );
|
||
|
if ($$rgn{RegionBoundary} and $$rgn{RegionBoundary}{RbShape} eq 'rectangle') {
|
||
|
my @rect = @{$$rgn{RegionBoundary}}{'RbX','RbY','RbW','RbH'};
|
||
|
if ($$rgn{RegionBoundary}{RbUnit} eq 'pixel') {
|
||
|
$rect[0] /= $val[1], $rect[2] /= $val[1];
|
||
|
$rect[1] /= $val[2]; $rect[3] /= $val[2];
|
||
|
}
|
||
|
$newRgn{Area} = {
|
||
|
X => $rect[0] + $rect[2]/2,
|
||
|
Y => $rect[1] + $rect[3]/2,
|
||
|
W => $rect[2],
|
||
|
H => $rect[3],
|
||
|
Unit => 'normalized',
|
||
|
};
|
||
|
} else {
|
||
|
next unless defined $$rgn{Name};
|
||
|
}
|
||
|
$newRgn{Name} = $$rgn{Name} if defined $$rgn{Name};
|
||
|
push @newRgns, \%newRgn;
|
||
|
}
|
||
|
return {
|
||
|
AppliedToDimensions => { W => $val[1], H => $val[2], Unit => 'pixel' },
|
||
|
RegionList => \@newRgns,
|
||
|
};
|
||
|
},
|
||
|
},
|
||
|
|
||
|
# create a Microsoft RegionInfoMP structure from an MWG RegionInfo structure
|
||
|
MWGRegion2MPRegion => {
|
||
|
Require => 'RegionInfo',
|
||
|
ValueConv => q{
|
||
|
my ($rgn, @newRgns);
|
||
|
foreach $rgn (@{$val[0]{RegionList}}) {
|
||
|
next unless $$rgn{Area} or defined $$rgn{Name};
|
||
|
my %newRgn;
|
||
|
if ($$rgn{Area}) {
|
||
|
my @rect = @{$$rgn{Area}}{'X','Y','W','H'};
|
||
|
$rect[0] -= $rect[2]/2;
|
||
|
$rect[1] -= $rect[3]/2;
|
||
|
$newRgn{Rectangle} = join(', ', @rect);
|
||
|
}
|
||
|
$newRgn{PersonDisplayName} = $$rgn{Name} if defined $$rgn{Name};
|
||
|
push @newRgns, \%newRgn;
|
||
|
}
|
||
|
return { Regions => \@newRgns };
|
||
|
},
|
||
|
},
|
||
|
|
||
|
# create a Microsoft RegionInfoMP structure from an IPTC ImageRegion list
|
||
|
IPTCRegion2MPRegion => {
|
||
|
Name => 'MWGRegion2MPRegion',
|
||
|
Require => {
|
||
|
0 => 'ImageRegion',
|
||
|
1 => 'ImageWidth',
|
||
|
2 => 'ImageHeight',
|
||
|
},
|
||
|
ValueConv => q{
|
||
|
my ($rgn, @newRgns);
|
||
|
my $rgns = ref $val[0] eq 'ARRAY' ? $val[0] : [ $val[0] ];
|
||
|
foreach $rgn (@$rgns) {
|
||
|
my %newRgn;
|
||
|
if ($$rgn{RegionBoundary} and $$rgn{RegionBoundary}{RbShape} eq 'rectangle') {
|
||
|
my @rect = @{$$rgn{RegionBoundary}}{'RbX','RbY','RbW','RbH'};
|
||
|
if ($$rgn{RegionBoundary}{RbUnit} eq 'pixel') {
|
||
|
$rect[0] /= $val[1], $rect[2] /= $val[1];
|
||
|
$rect[1] /= $val[2]; $rect[3] /= $val[2];
|
||
|
}
|
||
|
$newRgn{Rectangle} = join(', ', @rect);
|
||
|
} else {
|
||
|
next unless defined $$rgn{Name};
|
||
|
}
|
||
|
$newRgn{PersonDisplayName} = $$rgn{Name} if defined $$rgn{Name};
|
||
|
push @newRgns, \%newRgn;
|
||
|
}
|
||
|
return { Regions => \@newRgns };
|
||
|
},
|
||
|
},
|
||
|
|
||
|
# create an IPTC ImageRegion list from an MWG RegionInfo structure
|
||
|
MWGRegion2IPTCRegion => {
|
||
|
Require => 'RegionInfo',
|
||
|
ValueConv => q{
|
||
|
my ($rgn, @newRgns);
|
||
|
foreach $rgn (@{$val[0]{RegionList}}) {
|
||
|
next unless $$rgn{Area} or defined $$rgn{Name};
|
||
|
my %newRgn;
|
||
|
if ($$rgn{Area}) {
|
||
|
my @rect = @{$$rgn{Area}}{'X','Y','W','H'};
|
||
|
$rect[0] -= $rect[2]/2;
|
||
|
$rect[1] -= $rect[3]/2;
|
||
|
$newRgn{RegionBoundary} = {
|
||
|
RbShape => 'rectangle',
|
||
|
RbUnit => 'relative',
|
||
|
RbX => $rect[0],
|
||
|
RbY => $rect[1],
|
||
|
RbW => $rect[2],
|
||
|
RbH => $rect[3],
|
||
|
};
|
||
|
}
|
||
|
$newRgn{Name} = $$rgn{Name} if defined $$rgn{Name};
|
||
|
push @newRgns, \%newRgn;
|
||
|
}
|
||
|
return \@newRgns;
|
||
|
},
|
||
|
},
|
||
|
|
||
|
# create an IPTC ImageRegion list from a Microsoft RegionInfoMP structure
|
||
|
MPRegion2IPTCRegion => {
|
||
|
Name => 'MWGRegion2IPTCRegion',
|
||
|
Require => 'RegionInfoMP',
|
||
|
ValueConv => q{
|
||
|
my ($rgn, @newRgns);
|
||
|
foreach $rgn (@{$val[0]{Regions}}) {
|
||
|
my $name = $$rgn{PersonDisplayName};
|
||
|
next unless $$rgn{Rectangle} or defined $name;
|
||
|
my %newRgn;
|
||
|
if (defined $name) {
|
||
|
# don't add ignored faces
|
||
|
next if $name eq 'ffffffffffffffff';
|
||
|
$newRgn{Name} = $name;
|
||
|
}
|
||
|
if ($$rgn{Rectangle}) {
|
||
|
my @rect = split /\s*,\s*/, $$rgn{Rectangle};
|
||
|
$newRgn{RegionBoundary} = {
|
||
|
RbShape => 'rectangle',
|
||
|
RbUnit => 'relative',
|
||
|
RbX => $rect[0],
|
||
|
RbY => $rect[1],
|
||
|
RbW => $rect[2],
|
||
|
RbH => $rect[3],
|
||
|
} if @rect == 4;
|
||
|
}
|
||
|
push @newRgns, \%newRgn;
|
||
|
}
|
||
|
return \@newRgns;
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
);
|
||
|
|
||
|
%Image::ExifTool::UserDefined::Shortcuts = (
|
||
|
MyRegion => 'MPRegion2MWGRegion',
|
||
|
MyRegion2 => 'IPTCRegion2MWGRegion',
|
||
|
MyRegionMP => 'MWGRegion2MPRegion',
|
||
|
MyRegionMP2 => 'IPTCRegion2MPRegion',
|
||
|
MyRegionIPTC => 'MWGRegion2IPTCRegion',
|
||
|
MyRegionIPTC2 => 'MPRegion2IPTCRegion',
|
||
|
);
|
||
|
|
||
|
1; #end
|