mirror of
https://github.com/photoprism/photoprism.git
synced 2026-01-23 02:24:24 +00:00
Metadata: Do not estimate location if picture is non-photographic #4589
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
parent
689622dc7e
commit
253aa423ac
8 changed files with 218 additions and 109 deletions
|
|
@ -2759,9 +2759,9 @@ var FileFixtures = FileMap{
|
|||
},
|
||||
"photo52.gif.jpg": {
|
||||
ID: 1000068,
|
||||
Photo: PhotoFixtures.Pointer("photo52"),
|
||||
PhotoID: PhotoFixtures.Pointer("photo52").ID,
|
||||
PhotoUID: PhotoFixtures.Pointer("photo52").PhotoUID,
|
||||
Photo: PhotoFixtures.Pointer("Photo52"),
|
||||
PhotoID: PhotoFixtures.Pointer("Photo52").ID,
|
||||
PhotoUID: PhotoFixtures.Pointer("Photo52").PhotoUID,
|
||||
InstanceID: "a698ac56-6e7e-42b9-9c3e-a79ec9608355",
|
||||
FileUID: "fs6sg6bw15bnl355",
|
||||
FileName: "2020/GIF/photo52.gif.jpg",
|
||||
|
|
@ -2801,9 +2801,9 @@ var FileFixtures = FileMap{
|
|||
},
|
||||
"photo52.gif": {
|
||||
ID: 1000069,
|
||||
Photo: PhotoFixtures.Pointer("photo52"),
|
||||
PhotoID: PhotoFixtures.Pointer("photo52").ID,
|
||||
PhotoUID: PhotoFixtures.Pointer("photo52").PhotoUID,
|
||||
Photo: PhotoFixtures.Pointer("Photo52"),
|
||||
PhotoID: PhotoFixtures.Pointer("Photo52").ID,
|
||||
PhotoUID: PhotoFixtures.Pointer("Photo52").PhotoUID,
|
||||
InstanceID: "a698ac56-6e7e-42b9-9c3e-a79ec9608356",
|
||||
FileUID: "fs6sg6bw15bnl356",
|
||||
FileName: "2020/GIF/photo52.gif",
|
||||
|
|
@ -2844,9 +2844,9 @@ var FileFixtures = FileMap{
|
|||
},
|
||||
"photo53.jpg": {
|
||||
ID: 1000070,
|
||||
Photo: PhotoFixtures.Pointer("photo53"),
|
||||
PhotoID: PhotoFixtures.Pointer("photo53").ID,
|
||||
PhotoUID: PhotoFixtures.Pointer("photo53").PhotoUID,
|
||||
Photo: PhotoFixtures.Pointer("Photo53"),
|
||||
PhotoID: PhotoFixtures.Pointer("Photo53").ID,
|
||||
PhotoUID: PhotoFixtures.Pointer("Photo53").PhotoUID,
|
||||
InstanceID: "",
|
||||
FileUID: "fs6sg6bw15bnl357",
|
||||
FileName: "2023/holiday/photo53.jpg",
|
||||
|
|
@ -2886,9 +2886,9 @@ var FileFixtures = FileMap{
|
|||
},
|
||||
"photo54.jpg": {
|
||||
ID: 1000071,
|
||||
Photo: PhotoFixtures.Pointer("photo54"),
|
||||
PhotoID: PhotoFixtures.Pointer("photo54").ID,
|
||||
PhotoUID: PhotoFixtures.Pointer("photo54").PhotoUID,
|
||||
Photo: PhotoFixtures.Pointer("Photo54"),
|
||||
PhotoID: PhotoFixtures.Pointer("Photo54").ID,
|
||||
PhotoUID: PhotoFixtures.Pointer("Photo54").PhotoUID,
|
||||
InstanceID: "",
|
||||
FileUID: "fs6sg6bw15bnl358",
|
||||
FileName: "2023/holiday/photo54.jpg",
|
||||
|
|
@ -2928,9 +2928,9 @@ var FileFixtures = FileMap{
|
|||
},
|
||||
"photo54 (1).jpg": {
|
||||
ID: 1000072,
|
||||
Photo: PhotoFixtures.Pointer("photo54"),
|
||||
PhotoID: PhotoFixtures.Pointer("photo54").ID,
|
||||
PhotoUID: PhotoFixtures.Pointer("photo54").PhotoUID,
|
||||
Photo: PhotoFixtures.Pointer("Photo54"),
|
||||
PhotoID: PhotoFixtures.Pointer("Photo54").ID,
|
||||
PhotoUID: PhotoFixtures.Pointer("Photo54").PhotoUID,
|
||||
InstanceID: "",
|
||||
FileUID: "fs6sg6bw15bnl359",
|
||||
FileName: "2023/holiday/photo54 (1).jpg",
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ const Accuracy1Km = 1000
|
|||
// EstimateCountry updates the photo with an estimated country if possible.
|
||||
func (m *Photo) EstimateCountry() {
|
||||
if SrcPriority[m.PlaceSrc] > SrcPriority[SrcEstimate] || m.HasLocation() || m.HasPlace() {
|
||||
// Keep existing data.
|
||||
// Keep existing location data.
|
||||
return
|
||||
} else if m.UnknownCamera() && m.PhotoType == MediaImage {
|
||||
// Don't estimate if it seems to be a non-photographic image.
|
||||
} else if m.IsNonPhotographic() {
|
||||
// Don't estimate the country if the picture doesn't appear to be photographic.
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -28,7 +28,9 @@ func (m *Photo) EstimateCountry() {
|
|||
countryCode := unknown
|
||||
|
||||
// Try to guess country from photo title.
|
||||
if code := txt.CountryCode(m.PhotoTitle); code != unknown && m.TitleSrc != SrcAuto {
|
||||
if code := txt.CountryCode(m.PhotoTitle); code != unknown {
|
||||
countryCode = code
|
||||
} else if code = txt.CountryCode(m.PhotoDescription); code != unknown && m.DescriptionSrc != SrcAuto {
|
||||
countryCode = code
|
||||
}
|
||||
|
||||
|
|
@ -36,7 +38,7 @@ func (m *Photo) EstimateCountry() {
|
|||
if countryCode == unknown {
|
||||
if code := txt.CountryCode(m.PhotoName); code != unknown && !fs.IsGenerated(m.PhotoName) {
|
||||
countryCode = code
|
||||
} else if code := txt.CountryCode(m.PhotoPath); code != unknown {
|
||||
} else if code = txt.CountryCode(m.PhotoPath); code != unknown {
|
||||
countryCode = code
|
||||
}
|
||||
}
|
||||
|
|
@ -71,27 +73,27 @@ func (m *Photo) EstimateLocation(force bool) {
|
|||
|
||||
m.EstimatedAt = TimeStamp()
|
||||
|
||||
// Don't estimate if it seems to be a non-photographic image.
|
||||
if m.UnknownCamera() && m.PhotoType == MediaImage {
|
||||
// Remove the location estimate if the picture doesn't appear to be photographic.
|
||||
if m.IsNonPhotographic() {
|
||||
m.RemoveLocation(SrcEstimate, false)
|
||||
m.RemoveLocationLabels()
|
||||
return
|
||||
}
|
||||
|
||||
// Estimate country if TakenAt is unreliable.
|
||||
if SrcPriority[m.TakenSrc] <= SrcPriority[SrcName] {
|
||||
// Only estimate the country if TakenAt is unreliable or the picture has no camera metadata.
|
||||
if SrcPriority[m.TakenSrc] <= SrcPriority[SrcName] || m.UnknownCamera() {
|
||||
m.RemoveLocation(SrcEstimate, false)
|
||||
m.RemoveLocationLabels()
|
||||
m.EstimateCountry()
|
||||
return
|
||||
}
|
||||
|
||||
// Estimate the location based on nearby pictures if the date and camera model are known.
|
||||
var err error
|
||||
|
||||
rangeMin := m.TakenAt.Add(-1 * time.Hour * 37)
|
||||
rangeMax := m.TakenAt.Add(time.Hour * 37)
|
||||
|
||||
// Find photo with location info taken at a similar time...
|
||||
var mostRecent Photos
|
||||
|
||||
switch DbDialect() {
|
||||
|
|
|
|||
|
|
@ -8,39 +8,63 @@ import (
|
|||
)
|
||||
|
||||
func TestPhoto_EstimateCountry(t *testing.T) {
|
||||
t.Run("uk", func(t *testing.T) {
|
||||
m := Photo{CameraID: 2, PhotoName: "20200102_194030_9EFA9E5E", PhotoPath: "2000/05", OriginalName: "flickr import/changing-of-the-guard--buckingham-palace_7925318070_o.jpg"}
|
||||
t.Run("UnitedKingdom", func(t *testing.T) {
|
||||
m := Photo{
|
||||
CameraID: 2,
|
||||
PhotoType: MediaImage,
|
||||
PhotoName: "20200102_194030_9EFA9E5E",
|
||||
PhotoPath: "2000/05",
|
||||
OriginalName: "flickr import/changing-of-the-guard--buckingham-palace_7925318070_o.jpg",
|
||||
}
|
||||
assert.Equal(t, UnknownCountry.ID, m.CountryCode())
|
||||
assert.Equal(t, UnknownCountry.CountryName, m.CountryName())
|
||||
m.EstimateCountry()
|
||||
assert.Equal(t, "gb", m.CountryCode())
|
||||
assert.Equal(t, "United Kingdom", m.CountryName())
|
||||
})
|
||||
t.Run(UnknownID, func(t *testing.T) {
|
||||
m := Photo{CameraID: 2, PhotoName: "20200102_194030_ADADADAD", PhotoPath: "2020/Berlin", OriginalName: "Zimmermannstrasse.jpg"}
|
||||
t.Run("Berlin", func(t *testing.T) {
|
||||
m := Photo{
|
||||
CameraID: 2,
|
||||
PhotoType: MediaImage,
|
||||
PhotoName: "20200102_194030_ADADADAD",
|
||||
PhotoPath: "2020/Berlin",
|
||||
OriginalName: "Zimmermannstrasse.jpg",
|
||||
}
|
||||
assert.Equal(t, UnknownCountry.ID, m.CountryCode())
|
||||
assert.Equal(t, UnknownCountry.CountryName, m.CountryName())
|
||||
m.EstimateCountry()
|
||||
assert.Equal(t, "de", m.CountryCode())
|
||||
assert.Equal(t, "Germany", m.CountryName())
|
||||
})
|
||||
t.Run("de", func(t *testing.T) {
|
||||
m := Photo{CameraID: 2, PhotoName: "Brauhaus", PhotoPath: "2020/Bayern", OriginalName: "München.jpg"}
|
||||
t.Run("Munich", func(t *testing.T) {
|
||||
m := Photo{
|
||||
CameraID: 2,
|
||||
PhotoType: MediaImage,
|
||||
PhotoName: "Brauhaus",
|
||||
PhotoPath: "2020/Bayern",
|
||||
OriginalName: "München.jpg",
|
||||
}
|
||||
assert.Equal(t, UnknownCountry.ID, m.CountryCode())
|
||||
assert.Equal(t, UnknownCountry.CountryName, m.CountryName())
|
||||
m.EstimateCountry()
|
||||
assert.Equal(t, "de", m.CountryCode())
|
||||
assert.Equal(t, "Germany", m.CountryName())
|
||||
})
|
||||
t.Run("ca", func(t *testing.T) {
|
||||
m := Photo{CameraID: 2, PhotoTitle: "Port Lands / Gardiner Expressway / Toronto", PhotoPath: "2012/09", PhotoName: "20120910_231851_CA06E1AD", OriginalName: "demo/Toronto/port-lands--gardiner-expressway--toronto_7999515645_o.jpg"}
|
||||
t.Run("Toronto", func(t *testing.T) {
|
||||
m := Photo{
|
||||
CameraID: 2,
|
||||
PhotoType: MediaImage,
|
||||
PhotoTitle: "Port Lands / Gardiner Expressway / Toronto",
|
||||
PhotoPath: "2012/09", PhotoName: "20120910_231851_CA06E1AD",
|
||||
OriginalName: "demo/Toronto/port-lands--gardiner-expressway--toronto_7999515645_o.jpg",
|
||||
}
|
||||
assert.Equal(t, UnknownCountry.ID, m.CountryCode())
|
||||
assert.Equal(t, UnknownCountry.CountryName, m.CountryName())
|
||||
m.EstimateCountry()
|
||||
assert.Equal(t, "ca", m.CountryCode())
|
||||
assert.Equal(t, "Canada", m.CountryName())
|
||||
})
|
||||
t.Run("photo has latlng", func(t *testing.T) {
|
||||
t.Run("GpsCoordinates", func(t *testing.T) {
|
||||
m := Photo{
|
||||
PhotoTitle: "Port Lands / Gardiner Expressway / Toronto",
|
||||
PhotoLat: 13.333,
|
||||
|
|
@ -57,9 +81,19 @@ func TestPhoto_EstimateCountry(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPhoto_EstimateLocation(t *testing.T) {
|
||||
t.Run("photo already has location", func(t *testing.T) {
|
||||
t.Run("HasLocation", func(t *testing.T) {
|
||||
p := &Place{ID: "1000000001", PlaceCountry: "mx"}
|
||||
m := Photo{CameraID: 2, TakenSrc: SrcMeta, PhotoName: "PhotoWithLocation", OriginalName: "demo/xyz.jpg", Place: p, PlaceID: "1000000001", PlaceSrc: SrcManual, PhotoCountry: "mx"}
|
||||
m := Photo{
|
||||
CameraID: 2,
|
||||
TakenSrc: SrcMeta,
|
||||
PhotoType: MediaImage,
|
||||
PhotoName: "PhotoWithLocation",
|
||||
OriginalName: "demo/xyz.jpg",
|
||||
Place: p,
|
||||
PlaceID: "1000000001",
|
||||
PlaceSrc: SrcManual,
|
||||
PhotoCountry: "mx",
|
||||
}
|
||||
assert.True(t, m.HasPlace())
|
||||
assert.Equal(t, "mx", m.CountryCode())
|
||||
assert.Equal(t, "Mexico", m.CountryName())
|
||||
|
|
@ -67,67 +101,98 @@ func TestPhoto_EstimateLocation(t *testing.T) {
|
|||
assert.Equal(t, "mx", m.CountryCode())
|
||||
assert.Equal(t, "Mexico", m.CountryName())
|
||||
})
|
||||
t.Run("RecentlyEstimates", func(t *testing.T) {
|
||||
m2 := Photo{CameraID: 2, TakenSrc: SrcMeta, PhotoName: "PhotoWithoutLocation", OriginalName: "demo/xyy.jpg", EstimatedAt: TimeStamp(), TakenAt: time.Date(2016, 11, 11, 8, 7, 18, 0, time.UTC)}
|
||||
assert.Equal(t, UnknownID, m2.CountryCode())
|
||||
m2.EstimateLocation(false)
|
||||
assert.Equal(t, "zz", m2.CountryCode())
|
||||
assert.Equal(t, UnknownCountry.CountryName, m2.CountryName())
|
||||
assert.Equal(t, SrcAuto, m2.PlaceSrc)
|
||||
t.Run("RecentlyEstimated", func(t *testing.T) {
|
||||
m := Photo{
|
||||
CameraID: 2,
|
||||
TakenSrc: SrcMeta,
|
||||
PhotoType: MediaImage,
|
||||
PhotoName: "PhotoWithoutLocation",
|
||||
OriginalName: "demo/xyy.jpg",
|
||||
EstimatedAt: TimeStamp(),
|
||||
TakenAt: time.Date(2016, 11, 11, 8, 7, 18, 0, time.UTC),
|
||||
}
|
||||
assert.Equal(t, UnknownID, m.CountryCode())
|
||||
m.EstimateLocation(false)
|
||||
assert.Equal(t, "zz", m.CountryCode())
|
||||
assert.Equal(t, UnknownCountry.CountryName, m.CountryName())
|
||||
assert.Equal(t, SrcAuto, m.PlaceSrc)
|
||||
})
|
||||
t.Run("NotRecentlyEstimated", func(t *testing.T) {
|
||||
estimateTime := Now().Add(-1 * (MetadataEstimateInterval + 2*time.Hour))
|
||||
m2 := Photo{
|
||||
m := Photo{
|
||||
CameraID: 2,
|
||||
TakenSrc: SrcMeta,
|
||||
PhotoType: MediaImage,
|
||||
PhotoName: "PhotoWithoutLocation",
|
||||
OriginalName: "demo/xyy.jpg",
|
||||
EstimatedAt: &estimateTime,
|
||||
TakenAt: time.Date(2016, 11, 11, 8, 7, 18, 0, time.UTC)}
|
||||
assert.Equal(t, UnknownID, m2.CountryCode())
|
||||
m2.EstimateLocation(false)
|
||||
assert.Equal(t, "mx", m2.CountryCode())
|
||||
assert.Equal(t, "Mexico", m2.CountryName())
|
||||
assert.Equal(t, SrcEstimate, m2.PlaceSrc)
|
||||
assert.Equal(t, UnknownID, m.CountryCode())
|
||||
m.EstimateLocation(false)
|
||||
assert.Equal(t, "mx", m.CountryCode())
|
||||
assert.Equal(t, "Mexico", m.CountryName())
|
||||
assert.Equal(t, SrcEstimate, m.PlaceSrc)
|
||||
})
|
||||
t.Run("ForceEstimate", func(t *testing.T) {
|
||||
m2 := Photo{
|
||||
m := Photo{
|
||||
CameraID: 2,
|
||||
TakenSrc: SrcMeta,
|
||||
PhotoType: MediaImage,
|
||||
PhotoName: "PhotoWithoutLocation",
|
||||
OriginalName: "demo/xyy.jpg",
|
||||
EstimatedAt: TimeStamp(),
|
||||
TakenAt: time.Date(2016, 11, 11, 8, 7, 18, 0, time.UTC)}
|
||||
assert.Equal(t, UnknownID, m2.CountryCode())
|
||||
m2.EstimateLocation(true)
|
||||
assert.Equal(t, "mx", m2.CountryCode())
|
||||
assert.Equal(t, "Mexico", m2.CountryName())
|
||||
assert.Equal(t, SrcEstimate, m2.PlaceSrc)
|
||||
assert.Equal(t, UnknownID, m.CountryCode())
|
||||
m.EstimateLocation(true)
|
||||
assert.Equal(t, "mx", m.CountryCode())
|
||||
assert.Equal(t, "Mexico", m.CountryName())
|
||||
assert.Equal(t, SrcEstimate, m.PlaceSrc)
|
||||
})
|
||||
t.Run("recent photo has place", func(t *testing.T) {
|
||||
m2 := Photo{CameraID: 2, TakenSrc: SrcMeta, PhotoName: "PhotoWithoutLocation", OriginalName: "demo/xyy.jpg", TakenAt: time.Date(2016, 11, 11, 8, 7, 18, 0, time.UTC)}
|
||||
assert.Equal(t, UnknownID, m2.CountryCode())
|
||||
m2.EstimateLocation(false)
|
||||
assert.Equal(t, "mx", m2.CountryCode())
|
||||
assert.Equal(t, "Mexico", m2.CountryName())
|
||||
assert.Equal(t, SrcEstimate, m2.PlaceSrc)
|
||||
t.Run("HasPlace", func(t *testing.T) {
|
||||
m := Photo{
|
||||
CameraID: 2,
|
||||
TakenSrc: SrcMeta,
|
||||
PhotoType: MediaImage,
|
||||
PhotoName: "PhotoWithoutLocation",
|
||||
OriginalName: "demo/xyy.jpg",
|
||||
TakenAt: time.Date(2016, 11, 11, 8, 7, 18, 0, time.UTC),
|
||||
}
|
||||
assert.Equal(t, UnknownID, m.CountryCode())
|
||||
m.EstimateLocation(false)
|
||||
assert.Equal(t, "mx", m.CountryCode())
|
||||
assert.Equal(t, "Mexico", m.CountryName())
|
||||
assert.Equal(t, SrcEstimate, m.PlaceSrc)
|
||||
})
|
||||
t.Run("SrcAuto", func(t *testing.T) {
|
||||
m2 := Photo{CameraID: 2, TakenSrc: SrcAuto, PhotoName: "PhotoWithoutLocation", OriginalName: "demo/xyy.jpg", TakenAt: time.Date(2016, 11, 11, 8, 7, 18, 0, time.UTC)}
|
||||
assert.Equal(t, UnknownID, m2.CountryCode())
|
||||
m2.EstimateLocation(false)
|
||||
assert.Equal(t, "zz", m2.CountryCode())
|
||||
assert.Equal(t, "Unknown", m2.CountryName())
|
||||
assert.Equal(t, "zz", m2.PlaceID)
|
||||
assert.Equal(t, SrcAuto, m2.PlaceSrc)
|
||||
m := Photo{
|
||||
CameraID: 2,
|
||||
TakenSrc: SrcAuto,
|
||||
PhotoType: MediaImage,
|
||||
PhotoName: "PhotoWithoutLocation",
|
||||
OriginalName: "demo/xyy.jpg",
|
||||
TakenAt: time.Date(2016, 11, 11, 8, 7, 18, 0, time.UTC),
|
||||
}
|
||||
assert.Equal(t, UnknownID, m.CountryCode())
|
||||
m.EstimateLocation(false)
|
||||
assert.Equal(t, "zz", m.CountryCode())
|
||||
assert.Equal(t, "Unknown", m.CountryName())
|
||||
assert.Equal(t, "zz", m.PlaceID)
|
||||
assert.Equal(t, SrcAuto, m.PlaceSrc)
|
||||
})
|
||||
t.Run("cant estimate - out of scope", func(t *testing.T) {
|
||||
m2 := Photo{CameraID: 2, TakenSrc: SrcMeta, PhotoName: "PhotoWithoutLocation", OriginalName: "demo/xyy.jpg", TakenAt: time.Date(2016, 11, 13, 8, 7, 18, 0, time.UTC)}
|
||||
assert.Equal(t, UnknownID, m2.CountryCode())
|
||||
m2.EstimateLocation(true)
|
||||
assert.Equal(t, UnknownID, m2.CountryCode())
|
||||
t.Run("CannotEstimate", func(t *testing.T) {
|
||||
m := Photo{
|
||||
CameraID: 2,
|
||||
TakenSrc: SrcMeta,
|
||||
PhotoType: MediaImage,
|
||||
PhotoName: "PhotoWithoutLocation",
|
||||
OriginalName: "demo/xyy.jpg",
|
||||
TakenAt: time.Date(2016, 11, 13, 8, 7, 18, 0, time.UTC),
|
||||
}
|
||||
assert.Equal(t, UnknownID, m.CountryCode())
|
||||
m.EstimateLocation(true)
|
||||
assert.Equal(t, UnknownID, m.CountryCode())
|
||||
})
|
||||
/*t.Run("recent photo has country", func(t *testing.T) {
|
||||
/*t.Run("HasCountry", func(t *testing.T) {
|
||||
m2 := Photo{PhotoName: "PhotoWithoutLocation", OriginalName: "demo/zzz.jpg", TakenAt: time.Date(2001, 1, 1, 7, 20, 0, 0, time.UTC)}
|
||||
assert.Equal(t, UnknownID, m2.CountryCode())
|
||||
m2.EstimateLocation()
|
||||
|
|
|
|||
|
|
@ -3360,7 +3360,7 @@ var PhotoFixtures = PhotoMap{
|
|||
PhotoStack: 0,
|
||||
PhotoFaces: 0,
|
||||
},
|
||||
"photo52": { //GIF + JPG, Geo from metadata, indexed
|
||||
"Photo52": { //GIF + JPG, Geo from metadata, indexed
|
||||
ID: 1000053,
|
||||
PhotoUID: "ps6sg6byk7wrbk45",
|
||||
TakenAt: time.Date(2020, 11, 11, 9, 7, 18, 0, time.UTC),
|
||||
|
|
@ -3419,7 +3419,7 @@ var PhotoFixtures = PhotoMap{
|
|||
PhotoStack: 0,
|
||||
PhotoFaces: 0,
|
||||
},
|
||||
"photo53": { //JPG, Geo from metadata, indexed
|
||||
"Photo53": { //JPG, Geo from metadata, indexed
|
||||
ID: 1000054,
|
||||
PhotoUID: "ps6sg6byk7wrbk46",
|
||||
TakenAt: time.Date(2023, 11, 13, 9, 7, 18, 0, time.UTC),
|
||||
|
|
@ -3478,7 +3478,7 @@ var PhotoFixtures = PhotoMap{
|
|||
PhotoStack: 0,
|
||||
PhotoFaces: 0,
|
||||
},
|
||||
"photo54": { //JPG + JPG, Geo from metadata, indexed
|
||||
"Photo54": { //JPG + JPG, Geo from metadata, indexed
|
||||
ID: 1000055,
|
||||
PhotoUID: "ps6sg6byk7wrbk47",
|
||||
TakenAt: time.Date(2023, 11, 12, 9, 7, 18, 0, time.UTC),
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import (
|
|||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
// Optimize photo data, improve if possible.
|
||||
func (m *Photo) Optimize(mergeMeta, mergeUuid, estimatePlace, force bool) (updated bool, merged Photos, err error) {
|
||||
// Optimize the picture metadata based on the specified parameters.
|
||||
func (m *Photo) Optimize(mergeMeta, mergeUuid, estimateLocation, force bool) (updated bool, merged Photos, err error) {
|
||||
if !m.HasID() {
|
||||
return false, merged, errors.New("photo: cannot maintain, id is empty")
|
||||
}
|
||||
|
|
@ -20,25 +20,26 @@ func (m *Photo) Optimize(mergeMeta, mergeUuid, estimatePlace, force bool) (updat
|
|||
m.UpdateLocation()
|
||||
}
|
||||
|
||||
if original, photos, err := m.Merge(mergeMeta, mergeUuid); err != nil {
|
||||
return updated, merged, err
|
||||
if original, photos, mergeErr := m.Merge(mergeMeta, mergeUuid); mergeErr != nil {
|
||||
return updated, merged, mergeErr
|
||||
} else if len(photos) > 0 && original.ID == m.ID {
|
||||
merged = photos
|
||||
} else if len(photos) > 0 && original.ID != m.ID {
|
||||
return false, photos, nil
|
||||
}
|
||||
|
||||
// Estimate if feature is enabled and place wasn't set otherwise.
|
||||
if estimatePlace && SrcPriority[m.PlaceSrc] <= SrcPriority[SrcEstimate] {
|
||||
// Estimate the location if it is unknown and this feature is enabled.
|
||||
if estimateLocation && SrcPriority[m.PlaceSrc] <= SrcPriority[SrcEstimate] {
|
||||
m.EstimateLocation(force)
|
||||
}
|
||||
|
||||
// Get image classification labels.
|
||||
labels := m.ClassifyLabels()
|
||||
|
||||
m.UpdateDateFields()
|
||||
|
||||
if err := m.UpdateTitle(labels); err != nil {
|
||||
log.Info(err)
|
||||
if updateErr := m.UpdateTitle(labels); updateErr != nil {
|
||||
log.Info(updateErr)
|
||||
}
|
||||
|
||||
details := m.GetDetails()
|
||||
|
|
@ -46,8 +47,8 @@ func (m *Photo) Optimize(mergeMeta, mergeUuid, estimatePlace, force bool) (updat
|
|||
w = append(w, labels.Keywords()...)
|
||||
details.Keywords = strings.Join(txt.UniqueWords(w), ", ")
|
||||
|
||||
if err := m.IndexKeywords(); err != nil {
|
||||
log.Errorf("photo: %s", err.Error())
|
||||
if indexErr := m.IndexKeywords(); indexErr != nil {
|
||||
log.Errorf("photo: %s", indexErr.Error())
|
||||
}
|
||||
|
||||
m.PhotoQuality = m.QualityScore()
|
||||
|
|
|
|||
|
|
@ -40,24 +40,7 @@ func (m *Photo) QualityScore() (score int) {
|
|||
score++
|
||||
}
|
||||
|
||||
nonPhotographic := false
|
||||
|
||||
details := m.GetDetails()
|
||||
|
||||
if details.Keywords != "" {
|
||||
keywords := txt.Words(details.Keywords)
|
||||
|
||||
for _, w := range keywords {
|
||||
w = strings.ToLower(w)
|
||||
|
||||
if _, ok := NonPhotographicKeywords[w]; ok {
|
||||
nonPhotographic = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !nonPhotographic {
|
||||
if !m.IsNonPhotographic() {
|
||||
score++
|
||||
}
|
||||
|
||||
|
|
@ -78,3 +61,27 @@ func (m *Photo) UpdateQuality() error {
|
|||
|
||||
return m.Update("PhotoQuality", m.PhotoQuality)
|
||||
}
|
||||
|
||||
// IsNonPhotographic checks whether the image appears to be non-photographic.
|
||||
func (m *Photo) IsNonPhotographic() (result bool) {
|
||||
if m.PhotoType == MediaUnknown || m.PhotoType == MediaVector || m.PhotoType == MediaAnimated {
|
||||
return true
|
||||
}
|
||||
|
||||
details := m.GetDetails()
|
||||
|
||||
if details.Keywords != "" {
|
||||
keywords := txt.Words(details.Keywords)
|
||||
|
||||
for _, w := range keywords {
|
||||
w = strings.ToLower(w)
|
||||
|
||||
if _, ok := NonPhotographicKeywords[w]; ok {
|
||||
result = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ func TestPhoto_QualityScore(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPhoto_UpdateQuality(t *testing.T) {
|
||||
t.Run("nil", func(t *testing.T) {
|
||||
t.Run("Hidden", func(t *testing.T) {
|
||||
p := &Photo{PhotoQuality: -1}
|
||||
err := p.UpdateQuality()
|
||||
if err != nil {
|
||||
|
|
@ -33,12 +33,31 @@ func TestPhoto_UpdateQuality(t *testing.T) {
|
|||
}
|
||||
assert.Equal(t, -1, p.PhotoQuality)
|
||||
})
|
||||
t.Run("low quality expected", func(t *testing.T) {
|
||||
t.Run("Favorite", func(t *testing.T) {
|
||||
p := &Photo{PhotoQuality: 0, PhotoFavorite: true}
|
||||
err := p.UpdateQuality()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, 5, p.PhotoQuality)
|
||||
assert.Equal(t, 4, p.PhotoQuality)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPhoto_IsNonPhotographic(t *testing.T) {
|
||||
t.Run("Raw", func(t *testing.T) {
|
||||
m := PhotoFixtures.Get("Photo01")
|
||||
assert.False(t, m.IsNonPhotographic())
|
||||
})
|
||||
t.Run("Image", func(t *testing.T) {
|
||||
m := PhotoFixtures.Get("Photo04")
|
||||
assert.False(t, m.IsNonPhotographic())
|
||||
})
|
||||
t.Run("Video", func(t *testing.T) {
|
||||
m := PhotoFixtures.Get("Photo10")
|
||||
assert.False(t, m.IsNonPhotographic())
|
||||
})
|
||||
t.Run("Animated", func(t *testing.T) {
|
||||
m := PhotoFixtures.Get("Photo52")
|
||||
assert.True(t, m.IsNonPhotographic())
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,21 @@ func TestCountryCode(t *testing.T) {
|
|||
assert.Equal(t, "us", result)
|
||||
})
|
||||
|
||||
t.Run("Melbourne", func(t *testing.T) {
|
||||
result := CountryCode("The name Narrm is commonly used by the broader Aboriginal community\n\rto refer to the city, \t stemming from the traditional name recorded for the area on which the Melbourne city centre is built.")
|
||||
assert.Equal(t, "au", result)
|
||||
})
|
||||
|
||||
t.Run("ZugspitzeMelbourne", func(t *testing.T) {
|
||||
result := CountryCode("The name Narrm is commonly used by the broader Zugspitze community\n\rto refer to the city, \t stemming from the traditional name recorded for the area on which the Melbourne city centre is built.")
|
||||
assert.Equal(t, "au", result)
|
||||
})
|
||||
|
||||
t.Run("MelbourneZugspitze", func(t *testing.T) {
|
||||
result := CountryCode("The name Narrm is commonly used by the broader Melbourne community\n\rto refer to the city, \t stemming from the traditional name recorded for the area on which the Zugspitze city centre is built.")
|
||||
assert.Equal(t, "de", result)
|
||||
})
|
||||
|
||||
t.Run("StGallen", func(t *testing.T) {
|
||||
result := CountryCode("St.----Gallen")
|
||||
assert.Equal(t, "ch", result)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue