diff --git a/README.md b/README.md
index d9023e2..cecdcbd 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,9 @@
# face
+
+[](https://pkg.go.dev/github.com/dev6699/face)
+[](https://goreportcard.com/report/github.com/dev6699/face)
+[](LICENSE)
+
A comprehensive collection of face AI models with integrated pre and post-processing steps, utilizing NVIDIA Triton Inference Server for seamless inference. This repository aims to provide easy-to-use face detection, recognition, and analysis tools.
## Table of Contents
@@ -21,28 +26,34 @@ This repository contains a suite of face AI models designed for various applicat
- Easy-to-Use Interface: Simple API for quick integration into various applications.
## Installation
-1. Clone the Repository:
+- ### Use `go get` to install this package:
+
+ ```bash
+ go get github.com/dev6699/face
+ ```
+
+- ### Clone the Repository:
```bash
git clone https://github.com/dev6699/face.git
cd face
```
-2. Open the repository in vscode devcontainer.
+### Download and Prepare Models:
+- Navigate to the [Available Models](#available-models) section to find the download links for each model.
+- Download each model and rename the file to `model.onnx`.
+- Place each `model.onnx` file into its respective directory within the model_repository folder.
+- Example: Setting up the YOLOFace model:
-3. Download and Prepare Models:
- - Navigate to the [Available Models](#available-models) section to find the download links for each model.
- - Download each model and rename the file to `model.onnx`.
- - Place each `model.onnx` file into its respective directory within the model_repository folder.
- - Example: Setting up the YOLOFace model:
-
- ```bash
- mkdir -p model_repository/yoloface/1
- wget -O model_repository/yoloface/1/model.onnx
- ```
- Ensure to replace with the actual URL provided in the [Available Models](#available-models) section.
+ ```bash
+ mkdir -p model_repository/yoloface/1
+ wget -O model_repository/yoloface/1/model.onnx
+ ```
+ Ensure to replace with the actual URL provided in the [Available Models](#available-models) section.
## Usage
+Please refer to the [examples](examples) folder for more information on how to use the models and run various tasks.
+
1. Start Triton Inference Server:
```bash
diff --git a/docker-compose.yml b/docker-compose.yml
index 735d0b0..5201a44 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -15,3 +15,7 @@ services:
reservations:
devices:
- capabilities: [gpu]
+
+networks:
+ default:
+ name: face_devcontainer_default
\ No newline at end of file
diff --git a/examples/2dfan4/main.go b/examples/2dfan4/main.go
new file mode 100644
index 0000000..2b73b74
--- /dev/null
+++ b/examples/2dfan4/main.go
@@ -0,0 +1,46 @@
+package main
+
+import (
+ "log"
+
+ "github.com/dev6699/face/client"
+ "github.com/dev6699/face/examples"
+ "github.com/dev6699/face/model"
+ _2dfan4 "github.com/dev6699/face/model/2dfan4"
+ "github.com/dev6699/face/model/yoloface"
+ "gocv.io/x/gocv"
+)
+
+func main() {
+ faceDetectorScore := float32(0.5)
+ iouThreshold := 0.4
+ yolofaceFactory := yoloface.NewFactory(faceDetectorScore, iouThreshold)
+ _2dfan4Factory := _2dfan4.NewFactory()
+ err := client.Init(
+ "tritonserver:8001",
+ []model.ModelMeta{
+ yolofaceFactory(),
+ _2dfan4Factory(),
+ },
+ )
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ img := gocv.IMRead("../image.jpg", gocv.IMReadColor)
+ yoloFaceOutput, err := client.Infer(yolofaceFactory, &yoloface.Input{Img: img})
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for _, d := range yoloFaceOutput.Detections {
+ fanOutput, err := client.Infer(_2dfan4Factory, &_2dfan4.Input{Img: img, BoundingBox: d.BoundingBox})
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ examples.DrawPoints(&img, fanOutput.FaceLandmark68.Data, examples.Red, 2)
+ }
+
+ gocv.IMWrite("output.jpg", img)
+}
diff --git a/examples/arcface/main.go b/examples/arcface/main.go
new file mode 100644
index 0000000..e9bff26
--- /dev/null
+++ b/examples/arcface/main.go
@@ -0,0 +1,52 @@
+package main
+
+import (
+ "fmt"
+ "log"
+
+ "github.com/dev6699/face/client"
+ "github.com/dev6699/face/model"
+ "github.com/dev6699/face/model/arcface"
+ "github.com/dev6699/face/model/yoloface"
+ "gocv.io/x/gocv"
+)
+
+func main() {
+ faceDetectorScore := float32(0.5)
+ iouThreshold := 0.4
+ yolofaceFactory := yoloface.NewFactory(faceDetectorScore, iouThreshold)
+ arcfaceFactory := arcface.NewFactory()
+ err := client.Init(
+ "tritonserver:8001",
+ []model.ModelMeta{
+ yolofaceFactory(),
+ arcfaceFactory(),
+ },
+ )
+ if err != nil {
+ log.Fatal(err)
+ }
+ img := gocv.IMRead("../image.jpg", gocv.IMReadColor)
+ yoloFaceOutput, err := client.Infer(yolofaceFactory, &yoloface.Input{Img: img})
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ faceEmbeddings := [][]float32{}
+ for _, d := range yoloFaceOutput.Detections {
+ arcfaceOutput, err := client.Infer(arcfaceFactory, &arcface.Input{Img: img, FaceLandmark5: d.FaceLandmark5})
+ if err != nil {
+ log.Fatal(err)
+ }
+ faceEmbeddings = append(faceEmbeddings, arcfaceOutput.NormedEmbedding)
+ }
+
+ similarDistance := 0.6
+ for i, f1 := range faceEmbeddings {
+ for j, f2 := range faceEmbeddings {
+ faceDistance := model.CalcFaceDistance(f1, f2)
+ isSimilar := faceDistance < similarDistance
+ fmt.Printf("Face %d & %d: %.2f %v\n", i, j, faceDistance, isSimilar)
+ }
+ }
+}
diff --git a/examples/genderage/main.go b/examples/genderage/main.go
new file mode 100644
index 0000000..1cdf053
--- /dev/null
+++ b/examples/genderage/main.go
@@ -0,0 +1,51 @@
+package main
+
+import (
+ "fmt"
+ "log"
+
+ "github.com/dev6699/face/client"
+ "github.com/dev6699/face/examples"
+ "github.com/dev6699/face/model"
+ "github.com/dev6699/face/model/genderage"
+ "github.com/dev6699/face/model/yoloface"
+ "gocv.io/x/gocv"
+)
+
+func main() {
+ faceDetectorScore := float32(0.5)
+ iouThreshold := 0.4
+ yolofaceFactory := yoloface.NewFactory(faceDetectorScore, iouThreshold)
+ genderAgeFactory := genderage.NewFactory()
+ err := client.Init(
+ "tritonserver:8001",
+ []model.ModelMeta{
+ yolofaceFactory(),
+ genderAgeFactory(),
+ },
+ )
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ img := gocv.IMRead("../image.jpg", gocv.IMReadColor)
+ yoloFaceOutput, err := client.Infer(yolofaceFactory, &yoloface.Input{Img: img})
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for _, d := range yoloFaceOutput.Detections {
+ genderAgeOutput, err := client.Infer(genderAgeFactory, &genderage.Input{Img: img, BoundingBox: d.BoundingBox})
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ genderString := "M"
+ if genderAgeOutput.Gender == 0 {
+ genderString = "F"
+ }
+ examples.DrawBoundingBoxes(&img, d.BoundingBox, fmt.Sprintf("%s %d", genderString, genderAgeOutput.Age), examples.Green, examples.Red)
+ }
+
+ gocv.IMWrite("output.jpg", img)
+}
diff --git a/examples/gfpgan/main.go b/examples/gfpgan/main.go
new file mode 100644
index 0000000..ea880db
--- /dev/null
+++ b/examples/gfpgan/main.go
@@ -0,0 +1,43 @@
+package main
+
+import (
+ "fmt"
+ "log"
+
+ "github.com/dev6699/face/client"
+ "github.com/dev6699/face/model"
+ "github.com/dev6699/face/model/gfpgan"
+ "github.com/dev6699/face/model/yoloface"
+ "gocv.io/x/gocv"
+)
+
+func main() {
+ faceDetectorScore := float32(0.5)
+ iouThreshold := 0.4
+ yolofaceFactory := yoloface.NewFactory(faceDetectorScore, iouThreshold)
+ gfpganFactory := gfpgan.NewFactory(80.0)
+ err := client.Init(
+ "tritonserver:8001",
+ []model.ModelMeta{
+ yolofaceFactory(),
+ gfpganFactory(),
+ },
+ )
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ img := gocv.IMRead("../image.jpg", gocv.IMReadColor)
+ yoloFaceOutput, err := client.Infer(yolofaceFactory, &yoloface.Input{Img: img})
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for i, d := range yoloFaceOutput.Detections {
+ gfpganOutput, err := client.Infer(gfpganFactory, &gfpgan.Input{Img: img, FaceLandmark5: d.FaceLandmark5})
+ if err != nil {
+ log.Fatal(err)
+ }
+ gocv.IMWrite(fmt.Sprintf("output%d.jpg", i+1), gfpganOutput.OutFrame)
+ }
+}
diff --git a/examples/image.jpg b/examples/image.jpg
new file mode 100644
index 0000000..024a125
Binary files /dev/null and b/examples/image.jpg differ
diff --git a/examples/util.go b/examples/util.go
new file mode 100644
index 0000000..975f2cb
--- /dev/null
+++ b/examples/util.go
@@ -0,0 +1,26 @@
+package examples
+
+import (
+ "image"
+ "image/color"
+
+ "github.com/dev6699/face/model"
+ "gocv.io/x/gocv"
+)
+
+var (
+ Red = color.RGBA{R: 255, G: 0, B: 0, A: 255}
+ Green = color.RGBA{R: 0, G: 255, B: 0, A: 255}
+ Blue = color.RGBA{R: 0, G: 0, B: 255, A: 255}
+)
+
+func DrawBoundingBoxes(img *gocv.Mat, box model.BoundingBox, text string, boxColor, textColor color.RGBA) {
+ gocv.Rectangle(img, image.Rectangle{Min: image.Point{X: int(box.X1), Y: int(box.Y1)}, Max: image.Point{X: int(box.X2), Y: int(box.Y2)}}, boxColor, 2)
+ gocv.PutText(img, text, image.Point{X: int(box.X1), Y: int(box.Y1) - 5}, gocv.FontHersheySimplex, 0.5, textColor, 2)
+}
+
+func DrawPoints(img *gocv.Mat, points []gocv.Point2f, col color.RGBA, radius int) {
+ for _, pt := range points {
+ gocv.Circle(img, image.Pt(int(pt.X), int(pt.Y)), radius, col, -1)
+ }
+}
diff --git a/examples/yoloface/main.go b/examples/yoloface/main.go
new file mode 100644
index 0000000..9126dca
--- /dev/null
+++ b/examples/yoloface/main.go
@@ -0,0 +1,40 @@
+package main
+
+import (
+ "fmt"
+ "log"
+
+ "github.com/dev6699/face/client"
+ "github.com/dev6699/face/examples"
+ "github.com/dev6699/face/model"
+ "github.com/dev6699/face/model/yoloface"
+ "gocv.io/x/gocv"
+)
+
+func main() {
+ faceDetectorScore := float32(0.5)
+ iouThreshold := 0.4
+ yolofaceFactory := yoloface.NewFactory(faceDetectorScore, iouThreshold)
+ err := client.Init(
+ "tritonserver:8001",
+ []model.ModelMeta{
+ yolofaceFactory(),
+ },
+ )
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ img := gocv.IMRead("../image.jpg", gocv.IMReadColor)
+ yoloFaceOutput, err := client.Infer(yolofaceFactory, &yoloface.Input{Img: img})
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ for _, d := range yoloFaceOutput.Detections {
+ examples.DrawBoundingBoxes(&img, d.BoundingBox, fmt.Sprintf("Score: %.2f", d.Confidence), examples.Green, examples.Green)
+ examples.DrawPoints(&img, d.FaceLandmark5, examples.Red, 3)
+ }
+
+ gocv.IMWrite("output.jpg", img)
+}
diff --git a/model/util.go b/model/util.go
index 0501db3..f000b51 100644
--- a/model/util.go
+++ b/model/util.go
@@ -287,3 +287,25 @@ func getInverseVisionFrame(cropVisionFrame gocv.Mat, inverseMatrix gocv.Mat, tem
inverseVisionFrame.ConvertTo(&inverseVisionFrame, gocv.MatTypeCV64F)
return inverseVisionFrame
}
+
+// CalcFaceDistance to calculate the distance between two face embeddings
+func CalcFaceDistance(faceEmbedding, referenceFaceEmbedding []float32) float64 {
+ if len(faceEmbedding) == 0 || len(referenceFaceEmbedding) == 0 || len(faceEmbedding) != len(referenceFaceEmbedding) {
+ return 0
+ }
+
+ dotProduct := dotProduct(faceEmbedding, referenceFaceEmbedding)
+ return 1.0 - float64(dotProduct)
+}
+
+func dotProduct(v1, v2 []float32) float32 {
+ if len(v1) != len(v2) {
+ return 0
+ }
+
+ var result float32
+ for i := 0; i < len(v1); i++ {
+ result += v1[i] * v2[i]
+ }
+ return result
+}