People: Create person based on existing marker labels #22
Work in progress.
This commit is contained in:
parent
35f0a1925c
commit
e78c4df22e
8 changed files with 67 additions and 22 deletions
|
@ -759,9 +759,15 @@ export class Photo extends RestModel {
|
|||
}
|
||||
|
||||
file.Markers.forEach((m) => {
|
||||
if (!valid || !m.Invalid) {
|
||||
result.push(m);
|
||||
if (valid && m.Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m.hasOwnProperty("Person") && !!m.Person && !!m.Person.Name) {
|
||||
m.Label = m.Person.Name;
|
||||
}
|
||||
|
||||
result.push(m);
|
||||
});
|
||||
|
||||
return result;
|
||||
|
|
|
@ -37,6 +37,7 @@ type Marker struct {
|
|||
H float32 `gorm:"type:FLOAT;" json:"H" yaml:"H,omitempty"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
Person *Person `gorm:"foreignkey:RefUID;association_foreignkey:PersonUID;association_autoupdate:false;association_autocreate:false;association_save_reference:false" json:"Person" yaml:"-"`
|
||||
}
|
||||
|
||||
// UnknownMarker can be used as a default for unknown markers.
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -45,7 +45,12 @@ func (m Markers) FaceCount() int {
|
|||
// FindMarkers returns all markers for a given file id.
|
||||
func FindMarkers(fileID uint) (Markers, error) {
|
||||
m := Markers{}
|
||||
err := Db().Where(`file_id = ?`, fileID).Order("id").Offset(0).Limit(1000).Find(&m).Error
|
||||
err := Db().
|
||||
Where(`file_id = ?`, fileID).
|
||||
Preload("Person").
|
||||
Order("id").
|
||||
Offset(0).Limit(1000).
|
||||
Find(&m).Error
|
||||
|
||||
return m, err
|
||||
}
|
||||
|
|
|
@ -57,9 +57,7 @@ type Person struct {
|
|||
BirthYear int `json:"BirthYear" yaml:"BirthYear,omitempty"`
|
||||
BirthMonth int `json:"BirthMonth" yaml:"BirthMonth,omitempty"`
|
||||
BirthDay int `json:"BirthDay" yaml:"BirthDay,omitempty"`
|
||||
DeathYear int `json:"DeathYear" yaml:"DeathYear,omitempty"`
|
||||
DeathMonth int `json:"DeathMonth" yaml:"DeathMonth,omitempty"`
|
||||
DeathDay int `json:"DeathDay" yaml:"DeathDay,omitempty"`
|
||||
PassedAway *time.Time `json:"PassedAway" yaml:"PassedAway,omitempty"`
|
||||
CreatedAt time.Time `json:"CreatedAt" yaml:"-"`
|
||||
UpdatedAt time.Time `json:"UpdatedAt" yaml:"-"`
|
||||
DeletedAt *time.Time `sql:"index" json:"DeletedAt,omitempty" yaml:"-"`
|
||||
|
|
|
@ -11,7 +11,7 @@ type PeopleFaces []PersonFace
|
|||
// PersonFace represents the face of a Person.
|
||||
type PersonFace struct {
|
||||
ID string `gorm:"type:VARBINARY(42);primary_key;auto_increment:false;" json:"ID" yaml:"ID"`
|
||||
PersonUID string `gorm:"type:VARBINARY(42);index;" json:"PersonUID" yaml:"PersonUID"`
|
||||
PersonUID string `gorm:"type:VARBINARY(42);index;" json:"PersonUID" yaml:"PersonUID,omitempty"`
|
||||
Embedding string `gorm:"type:LONGTEXT;" json:"Embedding" yaml:"Embedding,omitempty"`
|
||||
CreatedAt time.Time `json:"CreatedAt" yaml:"CreatedAt,omitempty"`
|
||||
UpdatedAt time.Time `json:"UpdatedAt" yaml:"UpdatedAt,omitempty"`
|
||||
|
|
|
@ -9,11 +9,12 @@ const (
|
|||
SrcAuto = ""
|
||||
SrcManual = "manual"
|
||||
SrcEstimate = "estimate"
|
||||
SrcPeople = "people"
|
||||
SrcName = "name"
|
||||
SrcMeta = "meta"
|
||||
SrcXmp = "xmp"
|
||||
SrcYaml = "yaml"
|
||||
SrcPeople = "people"
|
||||
SrcMarker = "marker"
|
||||
SrcImage = classify.SrcImage
|
||||
SrcKeyword = classify.SrcKeyword
|
||||
SrcLocation = classify.SrcLocation
|
||||
|
@ -23,10 +24,11 @@ const (
|
|||
var SrcPriority = Priorities{
|
||||
SrcAuto: 1,
|
||||
SrcEstimate: 2,
|
||||
SrcPeople: 2,
|
||||
SrcName: 4,
|
||||
SrcYaml: 8,
|
||||
SrcLocation: 8,
|
||||
SrcPeople: 8,
|
||||
SrcMarker: 8,
|
||||
SrcImage: 8,
|
||||
SrcKeyword: 16,
|
||||
SrcMeta: 16,
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/mutex"
|
||||
|
@ -124,15 +126,15 @@ func (m *People) Start() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
uidMap := make(map[string]string, len(peopleFaces))
|
||||
faceMap := make(map[string]entity.Embedding, len(peopleFaces))
|
||||
type Face = struct {
|
||||
Embedding entity.Embedding
|
||||
PersonUID string
|
||||
}
|
||||
|
||||
faceMap := make(map[string]Face, len(peopleFaces))
|
||||
|
||||
for _, f := range peopleFaces {
|
||||
faceMap[f.ID] = f.UnmarshalEmbedding()
|
||||
|
||||
if f.PersonUID != "" {
|
||||
uidMap[f.ID] = f.PersonUID
|
||||
}
|
||||
faceMap[f.ID] = Face{f.UnmarshalEmbedding(), f.PersonUID}
|
||||
}
|
||||
|
||||
limit := 500
|
||||
|
@ -157,20 +159,38 @@ func (m *People) Start() (err error) {
|
|||
var faceId string
|
||||
var faceDist float64
|
||||
|
||||
for _, e1 := range marker.UnmarshalEmbeddings() {
|
||||
for id, e2 := range faceMap {
|
||||
if d := clusters.EuclideanDistance(e1, e2); faceId == "" || d < faceDist {
|
||||
for _, e := range marker.UnmarshalEmbeddings() {
|
||||
for id, f := range faceMap {
|
||||
if d := clusters.EuclideanDistance(e, f.Embedding); faceId == "" || d < faceDist {
|
||||
faceId = id
|
||||
faceDist = d
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if marker.RefUID != "" && marker.RefUID == uidMap[faceId] {
|
||||
if faceId == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if refUID := uidMap[faceId]; refUID != "" {
|
||||
if marker.RefUID != "" && marker.RefUID == faceMap[faceId].PersonUID {
|
||||
continue
|
||||
}
|
||||
|
||||
// Create person from marker label?
|
||||
if marker.MarkerLabel == "" {
|
||||
// Do nothing.
|
||||
} else if person := entity.NewPerson(marker.MarkerLabel, entity.SrcMarker, 1); person == nil {
|
||||
log.Errorf("people: person should not be nil - bug?")
|
||||
} else if person = entity.FirstOrCreatePerson(person); person == nil {
|
||||
log.Errorf("people: failed adding %s", txt.Quote(marker.MarkerLabel))
|
||||
} else if f, ok := faceMap[faceId]; ok {
|
||||
faceMap[faceId] = Face{Embedding: f.Embedding, PersonUID: person.PersonUID}
|
||||
entity.Db().Model(&entity.PersonFace{}).Where("id = ?", faceId).Update("PersonUID", person.PersonUID)
|
||||
log.Infof("people: added %s", txt.Quote(person.PersonName))
|
||||
}
|
||||
|
||||
// Existing person?
|
||||
if refUID := faceMap[faceId].PersonUID; refUID != "" {
|
||||
if err := marker.Updates(entity.Val{"RefUID": refUID, "RefSrc": entity.SrcPeople, "FaceID": ""}); err != nil {
|
||||
log.Errorf("people: %s while updating person uid", err)
|
||||
} else {
|
||||
|
@ -188,7 +208,7 @@ func (m *People) Start() (err error) {
|
|||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
log.Infof("people: %d faces added, %d recognized, %d markers updated, %d errors", addedFaces, recognized, markersUpdated, updateErrors)
|
||||
log.Infof("people: %d faces added, %d recognized, %d unknown, %d errors", addedFaces, recognized, markersUpdated, updateErrors)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue