说起人脸识别,大家首先想到的实现形式应该是 Python 去做相干的解决,因为相干的机器学习框架,库都曾经封装得比拟好了。然而咱们明天探讨的实现形式换成 Golang,利用 Golang 去做动态图像和视频流人脸识别的相应解决。
动态图像人脸识别
首先咱们来进行动态的人脸识别,Golang 这边相较于 Python 社区来说绝对少一些,不过仍然有一些优良的库能够供咱们应用。明天咱们用到的就是 go-face 这个库。该库利用 dlib 去实现人脸识别,一个很受欢迎的机器学习工具集,它能够说是人脸识别中应用最多的软件包之一。在产学界有广泛应用,涵盖了机器人学,嵌入式设施,挪动设施等等。在它官网的文档中提到在 Wild 基准测试中辨认标记面部的准确度达到惊人的 99.4%,这也阐明为什么它能失去宽泛的利用。
在咱们开始码代码之前,首先须要装置 dlib。Windows 平台绝对麻烦一些,具体在官网有装置计划,这里我介绍两个平台。
Ubuntu 18.10+, Debian sid
最新版本的 Ubuntu 和 Debian 都提供适合的 dlib 包,所以只须要运行。
# 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
macOS
确保装置了 Homebrew。
brew install dlib
创立我的项目及筹备工作
在 GOPATH 的 src 目录下,创立我的项目文件,命令如下。
sudo makedir go-face-test
# 创立 main.go
sudo touch main.go
而后进入该目录下,生成 mod 文件。
sudo go mod init
调用该命令后,在 go-face-test 目录下应该曾经生成了 go.mod 文件。
该库须要三个模型 shape_predictor_5_face_landmarks.dat, mmod_human_face_detector.dat 和 dlib_face_recognition_resnet_model_v1.dat,在 go-face-test 目录下下载相应的测试数据。
git clone https://github.com/Kagami/go-face-testdata testdata
最终的我的项目构造应该如图。
代码实现
首先,咱们利用代码查看环境是否失常。初始化识别器,开释资源。
package main
import (
"fmt"
"github.com/Kagami/go-face"
)
const dataDir = "testdata"
// testdata 目录下两个对应的文件夹目录
const (
modelDir = dataDir + "/models"
imagesDir = dataDir + "/images"
)
func main() {fmt.Println("Face Recognition...")
// 初始化识别器
rec, err := face.NewRecognizer(modelDir)
if err != nil {fmt.Println("Cannot INItialize recognizer")
}
defer rec.Close()
fmt.Println("Recognizer Initialized")
}
编译而后运行代码。
sudo go run main.go
应该失去上面输入。
Face Recognition...
Recognizer Initialized
到这一步,咱们曾经胜利的设置好了须要的所有。
检测图片中人脸数量
首先筹备一张林俊杰的照片,放到任意目录下,为了演示不便,我放在了 main.go 同级目录下。
如你所见,当初什么都没有,只有一张图片,接下来咱们要让计算机计算图片中的人脸数量。
package main
import (
"fmt"
"log"
"github.com/Kagami/go-face"
)
const dataDir = "testdata"
// testdata 目录下两个对应的文件夹目录
const (
modelDir = dataDir + "/models"
imagesDir = dataDir + "/images"
)
func main() {fmt.Println("Face Recognition...")
// 初始化识别器
rec, err := face.NewRecognizer(modelDir)
if err != nil {fmt.Println("Cannot INItialize recognizer")
}
defer rec.Close()
fmt.Println("Recognizer Initialized")
// 调用该办法,传入门路。返回面部数量和任何谬误
faces, err := rec.RecognizeFile("linjunjie.jpeg")
if err != nil {log.Fatalf("无奈辨认: %v", err)
}
// 打印人脸数量
fmt.Println("图片人脸数量:", len(faces))
}
外围代码其实就是一行,go-face 封装进行辨认的办法,传入相应门路的图片文件,执行代码后后果如下。
Face Recognition...
Recognizer Initialized
图片人脸数量: 1
当初笨笨的计算机曾经会数人脸数量了。那 …. 如果一张照片外面有多人准不准呢,咱们试试看,筹备一张多人合照图片。
heyin.jpeg
咱们将第 31 行代码换成如下即可。
faces, err := rec.RecognizeFile("heyin.jpeg")
运行后的后果应该打印( 图片人脸数量: 6),接下来正式看展咱们的人脸识别。
人脸识别
首先咱们筹备一张合照,这里仍然沿用下面的 heyin.jpeg。
整个处理过程大抵分为以下几步。
1. 将合影中人物映射到惟一 ID, 而后将惟一 ID 和对应人物相关联。
var samples []face.Descriptor
var peoples []int32
for i, f := range faces {samples = append(samples, f.Descriptor)
// 每张脸惟一 id
peoples = append(peoples, int32(i))
}
// Pass samples to the recognizer.
rec.SetSamples(samples, peoples)
2. 接下来咱们封装一个人脸识别的办法,传入识别器和照片门路,打印对应人物 ID,人物名字。
func RecognizePeople(rec *face.Recognizer, file string) {people, err := rec.RecognizeSingleFile(file)
if err != nil {log.Fatalf("无奈辨认: %v", err)
}
if people == nil {log.Fatalf("图片上不是一张脸")
}
peopleID := rec.Classify(people.Descriptor)
if peopleID < 0 {log.Fatalf("无奈辨别")
}
fmt.Println(peopleID)
fmt.Println(labels[peopleID])
}
3. 最初咱们传入想要辨认的图片,目前传入了 3 张图片,感兴趣的小伙伴能够传入其余图片尝试。
jay.jpeg
linjunjie.jpeg
taozhe.jpeg
4. 调用三次。
RecognizePeople(rec, "jay.jpeg")
RecognizePeople(rec, "linjunjie.jpeg")
RecognizePeople(rec, "taozhe.jpeg")
代码如下
package main
import (
"fmt"
"log"
"github.com/Kagami/go-face"
)
const dataDir = "testdata"
// testdata 目录下两个对应的文件夹目录
const (
modelDir = dataDir + "/models"
imagesDir = dataDir + "/images"
)
// 图片中的人名
var labels = []string{
"萧敬腾",
"周杰伦",
"unknow",
"王力宏",
"陶喆",
"林俊杰",
}
func main() {fmt.Println("Face Recognition...")
// 初始化识别器
rec, err := face.NewRecognizer(modelDir)
if err != nil {fmt.Println("Cannot INItialize recognizer")
}
defer rec.Close()
fmt.Println("Recognizer Initialized")
// 调用该办法,传入门路。返回面部数量和任何谬误
faces, err := rec.RecognizeFile("heyin.jpeg")
if err != nil {log.Fatalf("无奈辨认: %v", err)
}
// 打印人脸数量
fmt.Println("图片人脸数量:", len(faces))
var samples []face.Descriptor
var peoples []int32
for i, f := range faces {samples = append(samples, f.Descriptor)
// 每张脸惟一 id
peoples = append(peoples, int32(i))
}
// 传入样例到识别器
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("无奈辨认: %v", err)
}
if people == nil {log.Fatalf("图片上不是一张脸")
}
peopleID := rec.Classify(people.Descriptor)
if peopleID < 0 {log.Fatalf("无奈辨别")
}
fmt.Println(peopleID)
fmt.Println(labels[peopleID])
}
运行后果
最初咱们运行代码。
go build main.go
./main
后果如下
图片人脸数量: 6
1
周杰伦
5
林俊杰
4
陶喆
祝贺你,你曾经胜利的辨认出这三张图片是谁了,到这一步,动态的图像人脸识别曾经实现了。
动态人脸识别总结
到这一步咱们曾经能够胜利的利用 Go 实现了动态人脸识别。将其使用到我的项目中也不是不可,不过它有诸多局限,应用的场景较为繁多,只能用在例如用户上传人脸身份辨认,繁多人脸识别等场景;图片格式较为繁多,临时不反对 PNG 格局等毛病。
视频流人脸识别
背景
动态的人脸识别利用场景较为局限,不可能放到比拟重要的环境中,例如金融,保险,安防等畛域,存在伪造等可能。而且单纯的动态人脸识别,意义不大。动静的视频流领有更加广大的利用空间,充沛利用在智能安防,手势辨认,美颜等畛域。5G 时代,泛滥业务将围绕视频这一块开展,如何将视频业务与外围业务实现解耦,声网的 RTE 组件做得不错,作为 RTE-PaaS 的开创者,声网曾经有较多的技术积攒,通过 RTE 组件的模式有很多益处。
RTE 长处
1. 利用无关性
能够在不同的我的项目间共享,实现复用,防止屡次开发的重复性工作
2. 平台无关性
广泛应用于操作系统,编程语言及各畛域
3. 丰盛的三方模块
可能提供例如白板教学,视频美颜,鉴黄等泛滥模块供开发者应用
代码实现
这里咱们来实现一下视频流的相干人脸识别,之前的动态辨认就是为了动静视频流人脸识别做铺垫。咱们来说一下视频流的人脸识别的实现思路,动态的图像人脸识别曾经实现,而视频是多帧的间断,咱们只须要抽取片段捕捉关键帧,辨认出人像,人后输入对应关联的人名。
筹备工作
这里咱们用到的是 gocv(底层应用 OpenCV),这里咱们临时略过具体的装置流程,依照官网文档装置即可。
1. 设置视频捕获的设施,一般来说默认 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()
2. 关上展现窗口
// open display window
window := gocv.NewWindow("Face Detect")
defer window.Close()
3. 筹备图像矩阵,检测到人脸时显示矩形框的配置
// prepare image matrix
img := gocv.NewMat()
defer img.Close()
// color for the rect when faces detected
blue := color.RGBA{0, 0, 255, 0}
4. 加载人脸识别分类器,用一个死循环,外面加上咱们的相干辨认服务
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)
}
其中有几个步骤须要将一下,目前来说 gocv.IMEncode 只反对将捕捉到的图片转成 PNG,JPG,GIF 三种格局。转换后的字节放逐在内存中,而后将字节流传入咱们的人脸识别函数中即可。
// 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
}
注意事项
因为 go-face 只反对 JPEG 的格局,所以咱们捕获的帧只能转换成 JPG 格局
而后简略的封装一个字符流的辨认函数。这里须要阐明一下,之所以将 log.Fatal 换成了 log.Println 的起因是在视频流级别的辨认中可能会呈现没有人脸的状况,这个时候程序该当是失常运行的,不能退出。
func RecognizePeopleFromMemory(rec *face.Recognizer, img []byte) {people, err := rec.RecognizeSingle(img)
if err != nil {log.Println("无奈辨认: %v", err)
return
}
if people == nil {log.Println("图片上不是一张脸")
return
}
peopleID := rec.Classify(people.Descriptor)
if peopleID < 0 {log.Println("无奈辨别")
return
}
fmt.Println(peopleID)
fmt.Println(labels[peopleID])
}
最初残缺代码如下
package main
import (
"fmt"
"image/color"
"log"
"github.com/Kagami/go-face"
"gocv.io/x/gocv"
)
const dataDir = "testdata"
// testdata 目录下两个对应的文件夹目录
const (
modelDir = dataDir + "/models"
imagesDir = dataDir + "/images"
)
// 图片中的人名
var labels = []string{
"萧敬腾",
"周杰伦",
"unknow",
"王力宏",
"陶喆",
"林俊杰",
}
func main() {
// 初始化识别器
rec, err := face.NewRecognizer(modelDir)
if err != nil {fmt.Println("Cannot INItialize recognizer")
}
defer rec.Close()
fmt.Println("Recognizer Initialized")
// 调用该办法,传入门路。返回面部数量和任何谬误
faces, err := rec.RecognizeFile("heyin.jpeg")
if err != nil {log.Fatalf("无奈辨认: %v", err)
}
// 打印人脸数量
fmt.Println("图片人脸数量:", len(faces))
var samples []face.Descriptor
var peoples []int32
for i, f := range faces {samples = append(samples, f.Descriptor)
// 每张脸惟一 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("无奈辨认: %v", err)
}
if people == nil {log.Fatalf("图片上不是一张脸")
}
peopleID := rec.Classify(people.Descriptor)
if peopleID < 0 {log.Fatalf("无奈辨别")
}
fmt.Println(peopleID)
fmt.Println(labels[peopleID])
}
func RecognizePeopleFromMemory(rec *face.Recognizer, img []byte) {people, err := rec.RecognizeSingle(img)
if err != nil {log.Println("无奈辨认: %v", err)
return
}
if people == nil {log.Println("图片上不是一张脸")
return
}
peopleID := rec.Classify(people.Descriptor)
if peopleID < 0 {log.Println("无奈辨别")
return
}
fmt.Println(peopleID)
fmt.Println(labels[peopleID])
}
接下来咱们运行代码,应该可能拉起摄像头,这个时候我手持林俊杰的照片进行辨认,咱们能够看到左下角曾经输入对应的人名了。
视频流人脸识别总结
到这一步,祝贺你,你曾经可能实现视频流人脸识别了。然而,这里要阐明一下,为了疾速的实现,咱们的样本集是比拟少的,辨认成功率相对来说比拟低。不过一个简略的动静人脸识别曾经搭好了。
总结
尽管咱们实现了动静的人脸识别,然而在更为简单的利用场景下难以实现相应的需要,而且存在图片格式等限度,不足人脸解决的其余模块,美颜,鉴黄等性能。不过通过第三方的 SDK,例如声网等平台去实现对应的需要,园区的人脸识别,视频会议,云课堂等场景,可能实现疾速搭建,可能几行代码就可能实现相应的接入,并围绕 RTE 等组件进行人脸识别的相干开发。为开发节约大量工夫和老本,能够将开发重心转移到更加外围的业务。