feat: added gender age

This commit is contained in:
kweijack
2024-07-04 01:00:56 +00:00
parent 4e245c8fad
commit dcd03a450c
8 changed files with 221 additions and 3 deletions

38
model/genderage/README.md Normal file
View File

@@ -0,0 +1,38 @@
## Gender age detection
<img src="output.jpg">
---
Model description
[Get model](https://github.com/facefusion/facefusion-assets/releases/download/models/gender_age.onnx)
```
{
"name": "gender_age",
"versions": [
"1"
],
"platform": "onnxruntime_onnx",
"inputs": [
{
"name": "data",
"datatype": "FP32",
"shape": [
-1,
3,
96,
96
]
}
],
"outputs": [
{
"name": "fc1",
"datatype": "FP32",
"shape": [
1,
3
]
}
]
}
```

View File

@@ -0,0 +1,41 @@
package genderage
import (
"github.com/dev6699/face/model"
"gocv.io/x/gocv"
)
type Model struct{}
type Input struct {
Img gocv.Mat
BoundingBox model.BoundingBox
}
type Output struct {
Age int
// Gender: female=0, male=1
Gender int
}
type ModelT = model.Model[*Input, *Output]
var _ ModelT = &Model{}
func NewFactory() func() ModelT {
return func() ModelT {
return New()
}
}
func New() *Model {
return &Model{}
}
func (m *Model) ModelName() string {
return "gender_age"
}
func (m *Model) ModelVersion() string {
return "1"
}

BIN
model/genderage/output.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

32
model/genderage/post.go Normal file
View File

@@ -0,0 +1,32 @@
package genderage
import (
"math"
"github.com/dev6699/face/model"
)
func (m *Model) PostProcess(rawOutputContents [][]byte) (*Output, error) {
// "outputs": [
// {
// "name": "fc1",
// "datatype": "FP32",
// "shape": [
// 1,
// 3
// ]
// }
// ]
prediction, err := model.BytesToFloat32Slice(rawOutputContents[0])
if err != nil {
return nil, err
}
gender := model.Argmax(prediction[:2])
age := int(math.Round(float64(prediction[2] * 100)))
return &Output{
Gender: gender,
Age: age,
}, nil
}

39
model/genderage/pre.go Normal file
View File

@@ -0,0 +1,39 @@
package genderage
import (
"image"
"math"
"github.com/dev6699/face/model"
"github.com/dev6699/face/protobuf"
)
func (m *Model) PreProcess(i *Input) ([]*protobuf.InferTensorContents, error) {
boundingBox := i.BoundingBox
scale := 64.0 / max(math.Abs(boundingBox.X2-boundingBox.X1), math.Abs(boundingBox.Y2-boundingBox.Y1))
translation := model.Translation{
48.0 - (boundingBox.X1+boundingBox.X2)*scale*0.5,
48.0 - (boundingBox.Y1+boundingBox.Y2)*scale*0.5,
}
cropVisionFrame, affineMatrix := model.WarpFaceByTranslation(i.Img, translation, scale, image.Point{X: 96, Y: 96})
defer cropVisionFrame.Close()
defer affineMatrix.Close()
di, _ := cropVisionFrame.DataPtrUint8()
d := make([]float32, len(di))
idx := 0
for i := 0; i < len(di); i += 3 {
d[idx] = float32(di[i+2])
d[cropVisionFrame.Cols()*cropVisionFrame.Rows()+idx] = float32(di[i+1])
d[2*cropVisionFrame.Cols()*cropVisionFrame.Rows()+idx] = float32(di[i])
idx++
}
contents := &protobuf.InferTensorContents{
Fp32Contents: d,
}
return []*protobuf.InferTensorContents{contents}, nil
}

View File

@@ -14,3 +14,7 @@ type ModelMeta interface {
ModelName() string
ModelVersion() string
}
type BoundingBox struct {
X1, Y1, X2, Y2 float64
}

66
model/util.go Normal file
View File

@@ -0,0 +1,66 @@
package model
import (
"bytes"
"encoding/binary"
"image"
"io"
"math"
"gocv.io/x/gocv"
)
func BytesToFloat32Slice(data []byte) ([]float32, error) {
t := []float32{}
// Create a buffer from the input data
buffer := bytes.NewReader(data)
for {
// Read the binary data from the buffer
var binaryValue uint32
err := binary.Read(buffer, binary.LittleEndian, &binaryValue)
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
t = append(t, math.Float32frombits(binaryValue))
}
return t, nil
}
// Argmax return the index of the maximum value in a slice
func Argmax(slice []float32) int {
maxIndex := 0
maxValue := slice[0]
for i, value := range slice {
if value > maxValue {
maxIndex = i
maxValue = value
}
}
return maxIndex
}
// Translation represents the translation vector.
type Translation [2]float64
func WarpFaceByTranslation(visionFrame gocv.Mat, translation Translation, scale float64, cropSize image.Point) (gocv.Mat, gocv.Mat) {
affineMatrix := gocv.NewMatWithSize(2, 3, gocv.MatTypeCV64F)
affineMatrix.SetDoubleAt(0, 0, scale)
affineMatrix.SetDoubleAt(0, 1, 0.0)
affineMatrix.SetDoubleAt(0, 2, translation[0])
affineMatrix.SetDoubleAt(1, 0, 0.0)
affineMatrix.SetDoubleAt(1, 1, scale)
affineMatrix.SetDoubleAt(1, 2, translation[1])
cropVisionFrame := gocv.NewMat()
gocv.WarpAffine(visionFrame, &cropVisionFrame, affineMatrix, cropSize)
return cropVisionFrame, affineMatrix
}

View File

@@ -36,6 +36,4 @@ Model description
}
]
}
```
```