Speaking of face recognition, we first think of the implementation should be Python to do the relevant processing, because the relevant machine learning framework, libraries have been packaged better. However, the implementation method we discussed today is changed to Golang, and Golang is used to do the corresponding processing of face recognition in static images and video streams.

Face recognition in static images

Let’s start with static face recognition, which is relatively rare on Golang’s side compared to the Python community, but there are still some good libraries to work with. Today we’re going to use the Go-Face library. Face recognition is implemented using Dlib, a popular set of machine learning tools that is arguably one of the most used software packages in face recognition. It is widely used in industry and academia, covering robotics, embedded devices, mobile devices and so on. The documentation on its website states that it was able to identify tagged faces with an astonishing 99.4% accuracy in the Wild Benchmark, which explains why it is so widely used.

Before we start coding, we need to install the Dlib. The Windows platform is relatively troublesome, and the specific installation scheme is available on the official website. Here I will introduce the two platforms.

Ubuntu 18.10 +, Debian sid

The latest versions of Ubuntu and Debian both provide suitable dlib packages, so you just need to run them.

# Ubuntu
sudo apt-get install libdlib-dev libblas-dev liblapack-dev libjpeg-turbo8-dev
# Debian
sudo apt-get install libdlib-dev libblas-dev liblapack-dev libjpeg62-turbo-dev
Copy the code

macOS

Make sure Homebrew is installed.

brew install dlib
Copy the code

Project creation and preparation

In the SRC directory of GOPATH, create the project file as follows.

Sudo makedir go-face-test # create main.go sudo touch main.goCopy the code

Then go to the directory and generate the mod file.

sudo go mod init
Copy the code

After the command is executed, the go.mod file should be generated in the go-face-test directory.

Dat, mmod_human_face_detector_detector. Dat, and dlib_face_recognition_resnet_model_v1.dat, Download the corresponding test data in the go-face-test directory.

git clone https://github.com/Kagami/go-face-testdata testdata
Copy the code

The final project structure should be as shown.

Code implementation

First, we use the code to check that the environment is healthy. Initialize the recognizer and release the resource.

Package main import (" FMT ""github.com/Kagami/go-face") const dataDir =" testData" modelDir = dataDir + "/models" imagesDir = dataDir + "/images" ) func main() { fmt.Println("Face Recognition..." ) // Initialize recognizer rec, err := face.NewRecognizer(modelDir) if err! = nil { fmt.Println("Cannot INItialize recognizer") } defer rec.Close() fmt.Println("Recognizer Initialized") }Copy the code

Compile and then run the code.

sudo go run main.go
Copy the code

You should get the following output.

Face Recognition...
Recognizer Initialized
Copy the code

At this point, we’ve successfully set up everything we need.

Detect the number of faces in the image

First, prepare a photo of Lin Junjie and put it in any directory. For demonstration convenience, I put it in the same directory as Main. go.

As you can see, now there’s nothing but a picture, and what we’re going to do is ask the computer to count the faces in the picture.

Package main import (" FMT ""log" "github.com/Kagami/go-face") const dataDir =" testData "// two corresponding directories in the testData directory const ( modelDir = dataDir + "/models" imagesDir = dataDir + "/images" ) func main() { fmt.Println("Face Recognition..." ) // Initialize recognizer rec, err := face.NewRecognizer(modelDir) if err! = nil {fmt.println ("Cannot INItialize Initialized")} defer reco.close () fmt.println (" recognizer Initialized") // Call the method, Incoming path. RecognizeFile("linjunjie.jpeg") returns the number of faces and any wrong faces, err := reco.recognizefile ("linjunjie.jpeg") if err! Fatalf(" unidentifiable: %v", err)} // Println(" Faces: ", len(faces))}Copy the code

The core code is actually a line, go-face encapsulates the identification method, and passes in the image file of the corresponding path. After executing the code, the result is as follows.

Face Recognition... Recognizer Initialized Number of Recognizer faces: 1Copy the code

Clumsy computers can already count faces. That… If there are more than one person in a photo, let’s try to prepare a photo of more than one person.

heyin.jpeg

Let’s replace line 31 with the following.

faces, err := rec.RecognizeFile("heyin.jpeg")
Copy the code

After running the results should be printed (picture face number: 6), then formally look at our face recognition.

Face recognition

First we prepare a group photo, heyin.jpeg as above.

The whole process is roughly divided into the following steps.

1. Map the person in the group photo to a unique ID, and then associate the unique ID with the corresponding person.

var samples []face.Descriptor var peoples []int32 for i, f := range faces { samples = append(samples, F. deccriptor) // Each face unique id peoples = append(peoples, int32(i)) } // Pass samples to the recognizer. rec.SetSamples(samples, peoples)Copy the code

2. Next we package a face recognition method, into the recognizer and photo path, print the corresponding character ID, character name.

func RecognizePeople(rec *face.Recognizer, file string) { people, err := rec.RecognizeSingleFile(file) if err ! = nil {log.fatalf (" unidentifiable: %v", Err)} If people == nil {log.fatalf (" false face ")} peopleID := rec.classify (people.descriptor) if peopleID < 0 { Log.fatalf (" Unrecognizable ")} FMT.Println(peopleID) FMT.Println(peopleID)}Copy the code

3. Finally, we passed in the pictures we want to recognize. So far, we have passed in three pictures.

jay.jpeg

linjunjie.jpeg

taozhe.jpeg

4. Call three times.

RecognizePeople(rec, "jay.jpeg")
	RecognizePeople(rec, "linjunjie.jpeg")
	RecognizePeople(rec, "taozhe.jpeg")
Copy the code

The following code

Package main import (" FMT ""log" "github.com/Kagami/go-face") const dataDir =" testData "// two corresponding directories in the testData directory Const (modelDir = dataDir + "/models" imagesDir = dataDir + "/images") var labels = []string{" } func main() {FMT.Println("Face Recognition..." ) // Initialize recognizer rec, err := face.NewRecognizer(modelDir) if err! = nil {fmt.println ("Cannot INItialize Initialized")} defer reco.close () fmt.println (" recognizer Initialized") // Call the method, Incoming path. RecognizeFile("heyin.jpeg") returns the number of faces and any wrong faces, err := reco.recognizefile ("heyin.jpeg") if err! Fatalf(" unrecognisable: %v", err)} // Print the number of faces fmt.Println(" Picture number of faces: %v", err) ", len(faces)) var samples []face.Descriptor var peoples []int32 for i, f := range faces { samples = append(samples, F. dexcriptor) // Unique id for each face peoples = append(peoples, int32(I))} // Pass the samples to the recognizer rec.setsamples (samples, peoples) RecognizePeople(rec, "jay.jpeg") RecognizePeople(rec, "linjunjie.jpeg") RecognizePeople(rec, "taozhe.jpeg") } func RecognizePeople(rec *face.Recognizer, file string) { people, err := rec.RecognizeSingleFile(file) if err ! = nil {log.fatalf (" unidentifiable: %v", Err)} If people == nil {log.fatalf (" false face ")} peopleID := rec.classify (people.descriptor) if peopleID < 0 { Log.fatalf (" Unrecognizable ")} FMT.Println(peopleID) FMT.Println(peopleID)}Copy the code

The results

Finally, we run the code.

go build main.go
./main
Copy the code

The results are as follows

Number of faces in pictures: 6 1 Jay Chou 5 Jj Lin 4 Zhe TaoCopy the code

Congratulations, you have successfully identified the three images. At this point, the static image face recognition is complete.

Summary of static face recognition

At this point we have been able to successfully use Go to achieve static face recognition. It is not impossible to use it in the project, but it has many limitations, the use of the scene is relatively single, can only be used in such as user upload face identification, single face recognition and other scenes; Image format is relatively simple, does not support PNG format and other shortcomings.

Video streaming face recognition

background

Static face recognition application scenarios are more limited, can not be put in the more important environment, such as finance, insurance, security and other fields, there is the possibility of counterfeiting. And simple static face recognition, not much meaning. Dynamic video stream has a broader application space, fully used in intelligent security, gesture recognition, beauty and other fields. In the 5G era, many businesses will be carried out around video. How to decouple video business from core business? The RTE component of Sonnet has done a good job.

RTE advantages

1. Application independence

It can be shared between different projects to achieve reuse and avoid the repetitive work of multiple development

2. Platform independence

Widely used in operating systems, programming languages and various fields

3. Rich three-party modules

Can provide such as whiteboard teaching, video beauty, yellow and other modules for developers to use

Code implementation

Here we will realize the video stream related face recognition, before the static recognition is to dynamic video stream face recognition pave the way. Let’s talk about the realization of video stream face recognition ideas, static image face recognition has been completed, and the video is a continuous multiple frames, we only need to extract snippet capture key frames, identify the portrait, after the output of the corresponding associated name.

The preparatory work

Here we use goCV (OpenCV is used at the bottom), here we temporarily skip the specific installation process, according to the official document can be installed.

1. Set the device for capturing videos. The default value is 0

// set to use a video capture device 0 deviceID := 0 // open webcam webcam, err := gocv.OpenVideoCapture(deviceID) if err ! = nil { fmt.Println(err) return } defer webcam.Close()Copy the code

2. Open the display window

// open display window
	window := gocv.NewWindow("Face Detect")
	defer window.Close()
Copy the code

3. Prepare the image matrix to display the configuration of the rectangular box when the face is detected

// prepare image matrix
	img := gocv.NewMat()
	defer img.Close()

	// color for the rect when faces detected
	blue := color.RGBA{0, 0, 255, 0}
Copy the code

4 load face recognition classifier, with an infinite loop, which plus our related recognition services

for { if ok := webcam.Read(&img); ! ok { fmt.Printf("cannot read device %v\n", deviceID) return } if img.Empty() { continue } // detect faces rects := classifier.DetectMultiScale(img) fmt.Printf("found %d faces\n", len(rects)) // draw a rectangle around each face on the original image for _, r := range rects { gocv.Rectangle(&img, r, blue, 3) imgFace := img.Region(r) buff, err:=gocv.IMEncode(".jpg",imgFace) if err ! = nil { fmt.Println("encoding to jpg err:%v", err) break } RecognizePeopleFromMemory(rec, buff) } // show the image in the window, and wait 1 millisecond window.IMShow(img) window.WaitKey(1) }Copy the code

There are several steps that need to be done. For now, GoCV.IMEncode only supports converting captured images into PNG, JPG and GIF formats. The converted byte stream is placed in memory, and then the byte stream is passed into our face recognition function.

// RecognizeSingle returns face if it's the only face on the image or // nil otherwise. Only JPEG format is currently supported. Thread-safe. func (rec *Recognizer) RecognizeSingle(imgData []byte) (face *Face, err error) { faces, err := rec.recognize(0, imgData, 1) if err ! = nil || len(faces) ! = 1 { return } face = &faces[0] return }Copy the code

Matters needing attention

Since Go-Face only supports JPEG format, the frames we capture can only be converted to JPG format

Then simply encapsulate a character stream recognition function. The reason why log.fatal was changed to log.println is that there may be no face in video stream level identification, and the program should be running properly and not exit.

func RecognizePeopleFromMemory(rec *face.Recognizer, img []byte) { people, err := rec.RecognizeSingle(img) if err ! = nil {log.Println(" unrecognized: %v", Err) return} if people == nil {log.println (" image is not a face ") return} peopleID := rec.classify (people.descriptor) if PeopleID < 0 {log.println (" unmarked ") return} ftt.println (peopleID) ftt.println (peopleID) labels[peopleID])}Copy the code

The final complete code is as follows

package main import ( "fmt" "image/color" "log" "github.com/Kagami/go-face" "gocv.io/x/gocv" ) const dataDir = Const (modelDir = dataDir + "/models" imagesDir = dataDir + "/images") // Name of the image Var labels = []string{" xiao ", "Jay "," Unknow ", "Lihom "," Joh ", "Joh "," Joh ", "joh "," joh ", "joh "," joh ", "joh "," joh ",} err := face.NewRecognizer(modelDir) if err ! = nil {fmt.println ("Cannot INItialize Initialized")} defer reco.close () fmt.println (" recognizer Initialized") // Call the method, Incoming path. RecognizeFile("heyin.jpeg") returns the number of faces and any wrong faces, err := reco.recognizefile ("heyin.jpeg") if err! Fatalf(" unrecognisable: %v", err)} // Print the number of faces fmt.Println(" Picture number of faces: %v", err) ", len(faces)) var samples []face.Descriptor var peoples []int32 for i, f := range faces { samples = append(samples, F. deccriptor) // Each face unique id peoples = append(peoples, int32(i)) } // Pass samples to the recognizer. rec.SetSamples(samples, peoples) RecognizePeople(rec, "jay.jpeg") RecognizePeople(rec, "linjunjie.jpeg") RecognizePeople(rec, "taozhe.jpeg") // set to use a video capture device 0 deviceID := 0 // open webcam webcam, err := gocv.OpenVideoCapture(deviceID) if err ! = nil { fmt.Println(err) return } defer webcam.Close() // open display window window := gocv.NewWindow("Face Detect") defer window.Close() // prepare image matrix img := gocv.NewMat() defer img.Close() // color for the rect when faces detected blue := color.RGBA{0, 0, 255, 0} // load classifier to recognize faces classifier := gocv.NewCascadeClassifier() defer classifier.Close() if ! classifier.Load("./haarcascade_frontalface_default.xml") { fmt.Println("Error reading cascade file: data/haarcascade_frontalface_default.xml") return } fmt.Printf("start reading camera device: %v\n", deviceID) for { if ok := webcam.Read(&img); ! ok { fmt.Printf("cannot read device %v\n", deviceID) return } if img.Empty() { continue } // detect faces rects := classifier.DetectMultiScale(img) if len(rects) == 0 { continue } fmt.Printf("found %d faces\n", len(rects)) // draw a rectangle around each face on the original image for _, r := range rects { gocv.Rectangle(&img, r, blue, 3) imgFace := img.Region(r) buff, err:=gocv.IMEncode(".jpg",imgFace) if err ! = nil { fmt.Println("encoding to jpg err:%v", err) break } RecognizePeopleFromMemory(rec, buff) } // show the image in the window, and wait 1 millisecond window.IMShow(img) window.WaitKey(1) } } func RecognizePeople(rec *face.Recognizer, file string) { people, err := rec.RecognizeSingleFile(file) if err ! = nil {log.fatalf (" unidentifiable: %v", Err)} If people == nil {log.fatalf (" false face ")} peopleID := rec.classify (people.descriptor) if peopleID < 0 { The Fatalf (" unable to distinguish between ")} FMT. Println (peopleID) FMT. Println (labels/peopleID)} func RecognizePeopleFromMemory (rec *face.Recognizer, img []byte) { people, err := rec.RecognizeSingle(img) if err ! = nil {log.Println(" unrecognized: %v", Err) return} if people == nil {log.println (" image is not a face ") return} peopleID := rec.classify (people.descriptor) if PeopleID < 0 {log.println (" unmarked ") return} ftt.println (peopleID) ftt.println (peopleID) labels[peopleID])}Copy the code

Next, we run the code, which should be able to pull up the camera. At this time, I hold Lin Junjie’s photo for identification. We can see that the corresponding name has been output in the lower left corner.

Summary of video stream face recognition

At this point, congratulations, you have completed video streaming face recognition. However, it should be noted here that in order to achieve fast, our sample set is relatively small, and the recognition success rate is relatively low. But a simple dynamic facial recognition has been built.

conclusion

Although we have achieved dynamic face recognition, but in more complex application scenarios it is difficult to achieve the corresponding requirements, and there are limitations such as picture format, lack of other modules of face processing, beauty, yellow and other functions. However, the third-party SDK, such as sound network and other platforms, can be used to realize the corresponding requirements. The face recognition, video conference, cloud classroom and other scenes in the park can be set up quickly, and the corresponding access can be completed in just a few lines of code, and the related development of face recognition can be carried out around RTE and other components. To save a lot of time and cost, you can shift the focus of development to more core businesses.