说起人脸识别,大家首先想到的实现方式应该是 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.datdlib_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 只支持将捕获到的图片转成 PNGJPGGIF 三种格式。转换后的字节流放在内存中,然后将字节流传入我们的人脸识别函数中即可。

// 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 等组件进行人脸识别的相关开发。为开发节约大量时间和成本,可以将开发重心转移到更加核心的业务。

手把手 Golang 实现静态图像与视频流人脸识别的更多相关文章

  1. 10分钟手把手教你运用Python实现简单的人脸识别

    欲直接下载代码文件,关注我们的公众号哦!查看历史消息即可! 前言:让我的电脑认识我 我的电脑只有认识我,才配称之为我的电脑! 今天,我们用Python实现高大上的人脸识别技术! Python里,简单的 ...

  2. 手把手教你用1行代码实现人脸识别 --Python Face_recognition

    环境要求: Ubuntu17.10 Python 2.7.14 环境搭建: 1. 安装 Ubuntu17.10 > 安装步骤在这里 2. 安装 Python2.7.14 (Ubuntu17.10 ...

  3. 手把手教你如何用 OpenCV + Python 实现人脸识别

    下午的时候,配好了OpenCV的Python环境,OpenCV的Python环境搭建.于是迫不及待的想体验一下opencv的人脸识别,如下文. 必备知识 Haar-like 通俗的来讲,就是作为人脸特 ...

  4. 腾讯云技术专家卢萌凯手把手教你Demo一个人脸识别程序!

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文来自腾讯云技术沙龙,本次沙龙主题为Serverless架构开发与SCF部署实践 卢萌凯:毕业于东南大学,曾就职于华为,熟悉云行业解决方案 ...

  5. 学习笔记TF058:人脸识别

    人脸识别,基于人脸部特征信息识别身份的生物识别技术.摄像机.摄像头采集人脸图像或视频流,自动检测.跟踪图像中人脸,做脸部相关技术处理,人脸检测.人脸关键点检测.人脸验证等.<麻省理工科技评论&g ...

  6. jQuery 人脸识别插件,支持图片和视频

    jQuery Face Detection 是一款人脸检测插件,能够检测到图片,视频和画布中的人脸坐标.它跟踪人脸并输出人脸模型的坐标位置为一个数组.我们相信,面部识别技术能够给我们的 Web 应用带 ...

  7. opencv 人脸识别

      背景知识 OpenCV 是一个开源的计算机视觉和机器学习库.它包含成千上万优化过的算法,为各种计算机视觉应用提供了一个通用工具包.根据这个项目的关于页面,OpenCV 已被广泛运用在各种项目上,从 ...

  8. 介绍n款计算机视觉库/人脸识别开源库/软件

    计算机视觉库 OpenCV OpenCV是Intel®开源计算机视觉库.它由一系列 C 函数和少量 C++ 类构成,实现了图像处理和计算机视觉方面的很多通用算法. OpenCV 拥有包括 300 多个 ...

  9. OpenCV学习 物体检测 人脸识别 填充颜色

    介绍 OpenCV是开源计算机视觉和机器学习库.包含成千上万优化过的算法.项目地址:http://opencv.org/about.html.官方文档:http://docs.opencv.org/m ...

  10. 全栈工程师带你开发 ,node开发人脸识别门禁系统

    效果图:       知识点: 人脸识别SKD部署,  webRTC视频流处理,URL构建blob对象,Canvas映射截图,ajax数据交互,Node图像处理,跨域与413处理,base64解码,p ...

随机推荐

  1. Coursera Programming Languages, Part B 华盛顿大学 Week 2

    Datatype-programming in Racket without structs 在 ML 语言中,我们使用 datatype binding 来实现对 标签联合类型的构建:传送门 这是因 ...

  2. ssh反向通信

    ##先决条件为:一个有公网IP的VPS(虚拟主机),我使用的是国内的腾讯云,您也可以选择阿里云,亚马逊等各种厂商产品.这台机器的操作系统为 centos 7.0 ,IP 为 A.A.A.A #双内网主 ...

  3. 3MP/5MPNetwork-Camera摄像头默认口令

    网络资产搜索: shodan: 找到5MP-Network-Carema 登陆:admin/a***n End!!!

  4. MySQL Atlas 读写分离软件介绍

    MySQL Atlas介绍 目录 MySQL Atlas介绍 一.MySQL Atlas介绍 1.1.1 MySQL Atlas介绍 1.1.2 Atlas基本管理 一.MySQL Atlas介绍 1 ...

  5. delete、truncate、drop的区别

    delete:只删除数据,不删除结构.删除的数据存储在系统回滚段中,可以回滚.不会自动提交事务. 在InnoDB中,delete不会真的把数据删除,mysql实际上只是给删除的数据打了个标记为已删除, ...

  6. vue表单校验限制输入数字后小数点两位(包括避开通过中文输入法的那些坑)

    <el-form-item label="海运运费系数"> <el-input v-model.trim="ruleForm.oceanFreightC ...

  7. 【Android异常】关于静态注册BroadcastReceiver接收不到系统广播的问题

    如果你静态注册的广播无法接收到消息,请先检查下:你的安卓版本是不是8.0+ 前言Google官方声明:Beginning with Android 8.0 (API level 26), the sy ...

  8. C 语言 数制

    C 语言 数制 数制也称计数制,是指用一组固定的符号和统一的规则来表示数值的方法.计算机处理的信息必须转换成二进制形式数据后才能进行存储和传输.计算机中,经常使用的进制有二进制.八进制.十进制.十六进 ...

  9. SSD目标检测网络解读(含网络结构和内容解读)

    SSD实现思路 SSD具有如下主要特点: 从YOLO中继承了将detection转化为regression的思路,一次完成目标定位与分类 基于Faster RCNN中的Anchor,提出了相似的Pri ...

  10. Idea项目构建时解决方法

    java.lang.OutOfMemoryError: Java heap space java.lang.OutOfMemoryError: GC overhead limit exceeded 整 ...