1
Fork 0
photoview/api/graphql/models/face_detection.go

120 lines
2.8 KiB
Go
Raw Normal View History

2021-02-15 17:35:28 +01:00
package models
import (
"bytes"
"database/sql/driver"
"encoding/binary"
2021-02-16 11:27:28 +01:00
"fmt"
"image"
"strconv"
"strings"
2021-02-15 17:35:28 +01:00
"github.com/Kagami/go-face"
2021-02-16 11:27:28 +01:00
"github.com/photoview/photoview/api/scanner/image_helpers"
2021-02-15 17:35:28 +01:00
)
type FaceGroup struct {
Model
Label *string
ImageFaces []ImageFace `gorm:"constraint:OnDelete:CASCADE;"`
}
type ImageFace struct {
Model
FaceGroupID int `gorm:"not null;index"`
MediaID int `gorm:"not null;index"`
Media Media `gorm:"constraint:OnDelete:CASCADE;"`
Descriptor FaceDescriptor `gorm:"not null"`
2021-02-16 11:27:28 +01:00
Rectangle FaceRectangle `gorm:"not null"`
2021-02-15 17:35:28 +01:00
}
type FaceDescriptor face.Descriptor
// GormDataType datatype used in database
func (fd FaceDescriptor) GormDataType() string {
return "BLOB"
}
// Scan tells GORM how to convert database data to Go format
func (fd *FaceDescriptor) Scan(value interface{}) error {
byteValue := value.([]byte)
reader := bytes.NewReader(byteValue)
binary.Read(reader, binary.LittleEndian, fd)
return nil
}
// Value tells GORM how to save into the database
func (fd FaceDescriptor) Value() (driver.Value, error) {
buf := new(bytes.Buffer)
if err := binary.Write(buf, binary.LittleEndian, fd); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
2021-02-16 11:27:28 +01:00
type FaceRectangle struct {
2021-02-16 12:01:10 +01:00
MinX, MaxX float64
MinY, MaxY float64
2021-02-16 11:27:28 +01:00
}
// ToDBFaceRectangle converts a pixel absolute rectangle to a relative FaceRectangle to be saved in the database
func ToDBFaceRectangle(imgRec image.Rectangle, imagePath string) (*FaceRectangle, error) {
size, err := image_helpers.GetPhotoDimensions(imagePath)
if err != nil {
return nil, err
}
return &FaceRectangle{
2021-02-16 12:01:10 +01:00
MinX: float64(imgRec.Min.X) / float64(size.Width),
MaxX: float64(imgRec.Max.X) / float64(size.Width),
MinY: float64(imgRec.Min.Y) / float64(size.Height),
MaxY: float64(imgRec.Max.Y) / float64(size.Height),
2021-02-16 11:27:28 +01:00
}, nil
}
// GormDataType datatype used in database
func (fr FaceRectangle) GormDataType() string {
return "VARCHAR(64)"
}
// Scan tells GORM how to convert database data to Go format
func (fr *FaceRectangle) Scan(value interface{}) error {
byteArray := value.([]uint8)
slices := strings.Split(string(byteArray), ":")
if len(slices) != 4 {
return fmt.Errorf("Invalid face rectangle format, expected 4 values, got %d", len(slices))
}
2021-02-16 12:41:34 +01:00
var err error
fr.MinX, err = strconv.ParseFloat(slices[0], 32)
2021-02-16 11:27:28 +01:00
if err != nil {
return err
}
2021-02-16 12:41:34 +01:00
fr.MaxX, err = strconv.ParseFloat(slices[1], 32)
2021-02-16 11:27:28 +01:00
if err != nil {
return err
}
2021-02-16 12:41:34 +01:00
fr.MinY, err = strconv.ParseFloat(slices[2], 32)
2021-02-16 11:27:28 +01:00
if err != nil {
return err
}
2021-02-16 12:41:34 +01:00
fr.MaxY, err = strconv.ParseFloat(slices[3], 32)
2021-02-16 11:27:28 +01:00
if err != nil {
return err
}
return nil
}
// Value tells GORM how to save into the database
func (fr FaceRectangle) Value() (driver.Value, error) {
2021-02-16 12:01:10 +01:00
result := fmt.Sprintf("%f:%f:%f:%f", fr.MinX, fr.MaxX, fr.MinY, fr.MaxY)
2021-02-16 11:27:28 +01:00
return result, nil
}