2021-05-26 14:41:59 +02:00
|
|
|
package entity
|
|
|
|
|
2021-09-06 05:13:53 +02:00
|
|
|
import (
|
2021-10-05 18:42:39 +02:00
|
|
|
"fmt"
|
|
|
|
|
2021-09-23 23:46:17 +02:00
|
|
|
"github.com/photoprism/photoprism/internal/classify"
|
2021-09-20 22:19:54 +02:00
|
|
|
"github.com/photoprism/photoprism/internal/face"
|
2021-09-06 05:13:53 +02:00
|
|
|
"github.com/photoprism/photoprism/pkg/txt"
|
|
|
|
)
|
2021-09-02 14:23:40 +02:00
|
|
|
|
2021-09-30 13:44:23 +02:00
|
|
|
// Markers represents a list of markers.
|
2021-05-26 14:41:59 +02:00
|
|
|
type Markers []Marker
|
|
|
|
|
|
|
|
// Save stores the markers in the database.
|
2021-09-22 19:33:41 +02:00
|
|
|
func (m Markers) Save(file *File) (count int, err error) {
|
2021-10-05 18:42:39 +02:00
|
|
|
if file == nil {
|
|
|
|
return 0, fmt.Errorf("file required for saving markers")
|
|
|
|
}
|
|
|
|
|
2021-09-30 13:44:23 +02:00
|
|
|
for i := range m {
|
2021-10-05 18:42:39 +02:00
|
|
|
if m[i].UpdateFile(file) {
|
|
|
|
continue
|
2021-05-31 15:40:52 +02:00
|
|
|
}
|
|
|
|
|
2021-10-05 18:42:39 +02:00
|
|
|
if created, err := CreateMarkerIfNotExists(&m[i]); err != nil {
|
2021-09-22 19:33:41 +02:00
|
|
|
log.Errorf("markers: %s (save)", err)
|
2021-10-05 18:42:39 +02:00
|
|
|
} else {
|
|
|
|
m[i] = *created
|
2021-05-26 14:41:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-22 19:33:41 +02:00
|
|
|
return file.UpdatePhotoFaceCount()
|
2021-05-26 14:41:59 +02:00
|
|
|
}
|
2021-05-31 15:40:52 +02:00
|
|
|
|
2021-09-23 23:46:17 +02:00
|
|
|
// Unsaved tests if any marker hasn't been saved yet.
|
|
|
|
func (m Markers) Unsaved() bool {
|
2021-09-30 13:44:23 +02:00
|
|
|
for i := range m {
|
|
|
|
if m[i].Unsaved() {
|
2021-09-23 23:46:17 +02:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-05-31 15:40:52 +02:00
|
|
|
// Contains returns true if a marker at the same position already exists.
|
2021-09-06 05:13:53 +02:00
|
|
|
func (m Markers) Contains(other Marker) bool {
|
2021-09-30 13:44:23 +02:00
|
|
|
for i := range m {
|
|
|
|
if m[i].OverlapPercent(other) > face.OverlapThreshold {
|
2021-05-31 15:40:52 +02:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-09-23 23:46:17 +02:00
|
|
|
// DetectedFaceCount returns the number of automatically detected face markers.
|
|
|
|
func (m Markers) DetectedFaceCount() (count int) {
|
2021-09-30 13:44:23 +02:00
|
|
|
for i := range m {
|
|
|
|
if m[i].DetectedFace() {
|
2021-09-23 23:46:17 +02:00
|
|
|
count++
|
2021-05-31 15:40:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-23 23:46:17 +02:00
|
|
|
return count
|
|
|
|
}
|
|
|
|
|
|
|
|
// ValidFaceCount returns the number of valid face markers.
|
|
|
|
func (m Markers) ValidFaceCount() (count int) {
|
2021-09-30 13:44:23 +02:00
|
|
|
for i := range m {
|
|
|
|
if m[i].ValidFace() {
|
2021-09-23 23:46:17 +02:00
|
|
|
count++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return count
|
2021-05-31 15:40:52 +02:00
|
|
|
}
|
|
|
|
|
2021-09-02 11:12:42 +02:00
|
|
|
// SubjectNames returns known subject names.
|
|
|
|
func (m Markers) SubjectNames() (names []string) {
|
2021-09-30 13:44:23 +02:00
|
|
|
for i := range m {
|
|
|
|
if m[i].MarkerInvalid || m[i].MarkerType != MarkerFace {
|
2021-09-02 11:12:42 +02:00
|
|
|
continue
|
2021-09-30 13:44:23 +02:00
|
|
|
} else if n := m[i].SubjectName(); n != "" {
|
2021-09-02 11:12:42 +02:00
|
|
|
names = append(names, n)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-02 14:23:40 +02:00
|
|
|
return txt.UniqueNames(names)
|
2021-09-02 11:12:42 +02:00
|
|
|
}
|
|
|
|
|
2021-09-23 23:46:17 +02:00
|
|
|
// Labels returns matching labels.
|
|
|
|
func (m Markers) Labels() (result classify.Labels) {
|
|
|
|
faceCount := 0
|
|
|
|
|
|
|
|
labelSrc := SrcImage
|
|
|
|
labelUncertainty := 100
|
|
|
|
|
2021-09-30 13:44:23 +02:00
|
|
|
for i := range m {
|
|
|
|
if m[i].ValidFace() {
|
2021-09-23 23:46:17 +02:00
|
|
|
faceCount++
|
|
|
|
|
2021-09-30 13:44:23 +02:00
|
|
|
if u := m[i].Uncertainty(); u < labelUncertainty {
|
2021-09-23 23:46:17 +02:00
|
|
|
labelUncertainty = u
|
|
|
|
}
|
|
|
|
|
2021-09-30 13:44:23 +02:00
|
|
|
if m[i].MarkerSrc != "" {
|
|
|
|
labelSrc = m[i].MarkerSrc
|
2021-09-23 23:46:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if faceCount < 1 {
|
|
|
|
return classify.Labels{}
|
|
|
|
}
|
|
|
|
|
|
|
|
var rule classify.LabelRule
|
|
|
|
|
|
|
|
if faceCount == 1 {
|
|
|
|
rule = classify.Rules["portrait"]
|
|
|
|
} else {
|
|
|
|
rule = classify.Rules["people"]
|
|
|
|
}
|
|
|
|
|
|
|
|
return classify.Labels{classify.Label{
|
|
|
|
Name: rule.Label,
|
|
|
|
Source: labelSrc,
|
|
|
|
Uncertainty: labelUncertainty,
|
|
|
|
Priority: rule.Priority,
|
|
|
|
Categories: rule.Categories,
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
2021-09-02 11:12:42 +02:00
|
|
|
// Append adds a marker.
|
|
|
|
func (m *Markers) Append(marker Marker) {
|
|
|
|
*m = append(*m, marker)
|
|
|
|
}
|
|
|
|
|
2021-09-30 13:44:23 +02:00
|
|
|
// AppendWithEmbedding adds a marker with face embedding.
|
|
|
|
func (m *Markers) AppendWithEmbedding(marker Marker) {
|
|
|
|
if !marker.Embeddings().One() {
|
|
|
|
// Ignore markers that don't have exactly one embedding.
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
m.Append(marker)
|
|
|
|
}
|
|
|
|
|
2021-09-01 20:46:15 +02:00
|
|
|
// FindMarkers returns up to 1000 markers for a given file uid.
|
|
|
|
func FindMarkers(fileUID string) (Markers, error) {
|
2021-05-31 15:40:52 +02:00
|
|
|
m := Markers{}
|
2021-09-01 20:46:15 +02:00
|
|
|
|
2021-08-12 17:49:48 +02:00
|
|
|
err := Db().
|
2021-09-01 20:46:15 +02:00
|
|
|
Where(`file_uid = ?`, fileUID).
|
2021-09-02 11:12:42 +02:00
|
|
|
Order("x").
|
2021-08-12 17:49:48 +02:00
|
|
|
Offset(0).Limit(1000).
|
|
|
|
Find(&m).Error
|
2021-05-31 15:40:52 +02:00
|
|
|
|
|
|
|
return m, err
|
|
|
|
}
|