mirror of
https://github.com/dev6699/face.git
synced 2025-09-26 21:16:00 +08:00
feat: added gender age
This commit is contained in:
38
model/genderage/README.md
Normal file
38
model/genderage/README.md
Normal 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
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
41
model/genderage/genderage.go
Normal file
41
model/genderage/genderage.go
Normal 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
BIN
model/genderage/output.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
32
model/genderage/post.go
Normal file
32
model/genderage/post.go
Normal 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
39
model/genderage/pre.go
Normal 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
|
||||
}
|
@@ -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
66
model/util.go
Normal 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
|
||||
}
|
@@ -36,6 +36,4 @@ Model description
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
```
|
Reference in New Issue
Block a user