Skip to content

Commit

Permalink
docs: examples
Browse files Browse the repository at this point in the history
  • Loading branch information
dev6699 committed Jul 9, 2024
1 parent 2b438a7 commit 8b6d769
Show file tree
Hide file tree
Showing 10 changed files with 308 additions and 13 deletions.
37 changes: 24 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# face

[![GoDoc](https://pkg.go.dev/badge/github.com/dev6699/face)](https://pkg.go.dev/github.com/dev6699/face)
[![Go Report Card](https://goreportcard.com/badge/github.com/dev6699/face)](https://goreportcard.com/report/github.com/dev6699/face)
[![License](https://img.shields.io/github/license/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
Expand All @@ -21,28 +26,34 @@ This repository contains a suite of face AI models designed for various applicat
- <b>Easy-to-Use Interface:</b> 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.

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:
### 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 <model_url>
```
Ensure to replace <model_url> 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 <model_url>
```
Ensure to replace <model_url> 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
Expand Down
4 changes: 4 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ services:
reservations:
devices:
- capabilities: [gpu]

networks:
default:
name: face_devcontainer_default
46 changes: 46 additions & 0 deletions examples/2dfan4/main.go
Original file line number Diff line number Diff line change
@@ -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)
}
52 changes: 52 additions & 0 deletions examples/arcface/main.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
51 changes: 51 additions & 0 deletions examples/genderage/main.go
Original file line number Diff line number Diff line change
@@ -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)
}
43 changes: 43 additions & 0 deletions examples/gfpgan/main.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
Binary file added examples/image.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions examples/util.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
40 changes: 40 additions & 0 deletions examples/yoloface/main.go
Original file line number Diff line number Diff line change
@@ -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)
}
22 changes: 22 additions & 0 deletions model/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

0 comments on commit 8b6d769

Please sign in to comment.