Centerface + Facenet实现视频人脸识别

Facenet的pytorch版本:GitHub地址:https://github.com/timesler/facenet-pytorch
CenterFace:GitHub地址:https://github.com/Star-Clouds/CenterFace

我是先跑通Centerface做人脸检测,发现效果还可以,然后跑人脸识别项目facenet,发现其中的人脸检测模型MTCNN效果好像没有centerface好,然后就用centerface替换了Facenet中的MTCNN人脸检测算法,发现效果确实更好一点。
两个项目跑通都比较容易,可以去网上找博客或者好好读读官方文档即可。当然,你可以用你自己喜欢的人脸检测和识别算法,自己组合,但前提是你首先得理解读懂别人的项目,然后才能开始改。
这里主要是对用Centerface做检测和Facenet做识别的新组合做解释。(项目是在Facenet原有基础上做改动)

项目结构介绍

改动后的项目结构和Facenet原有项目结构一致,不同的是将Centerface中的用于检测的脚本文件cnterface.py和与训练模型centerface.onnx复制到该项目下。我把centerface.py放到examples文件夹下,centerface.onnx放到models文件夹下。

data文件夹下是自己建的一些数据库,可以用爬虫脚本自动创建,后面会给出代码。
以DB1为例

  • DB1_images是存放的原始人脸数据,就是直接从网上下载的人脸图片;
  • crooped存放的是剪切后的人脸图片,大小为160*160(这是facenet项目中要求的大小,想改的话你可以去好好看看项目代码,找到在哪改,我没改)
  • features下保存的是人脸特征向量和人脸标签(就是你的名字)
  • test下保存的是用来测试的图片,你建好一个人脸数据库之后,总要拿一些新的图片看看效果嘛
  • test_results保存测试结果
    这样子把数据库标准化以后,对后续的操作非常方便。比如你要增加一个新的人脸数据库,或者向现有的书库中添加人脸(本项目中想做但还没做的,应该也不难,就是求出新人脸的特征向量,然后将新的人脸特征添加到对应数据库的文件中即可)

项目重要脚本介绍

face_database.py
该脚本是用于创建人脸特征数据库的,即对一个人脸数据集,计算出每个人脸的特征向量并保存,用于后续识别。

from facenet_pytorch import MTCNN, InceptionResnetV1
import torch
from torch.utils.data import DataLoader
from torchvision import datasets
import numpy as np
import pandas as pd
import os
from centerface import CenterFace, FaceExtract# creat a new database
# 要保证数据库中的图片只有当前人脸
def CreatNewDB(DB_images_path, feature_path, cropped_save_pth=None):# 定义了一个数据集# 读取路径下的所有文件夹,每一个文件夹为一个类别,有对应的标签index# 所有文件夹下的图片按顺序都存储在dataset中,dataset为可遍历对象,每张图片的格式为元祖(data,label)dataset = datasets.ImageFolder(DB_images_path)# dataset.class_to_idx将类别转化为数字索引dataset.idx_to_class = {i: c for c, i in dataset.class_to_idx.items()}# 定义一个数据加载器,一张图片表示为(data,label)的元祖loader = DataLoader(dataset, collate_fn=collate_fn, num_workers=workers)names = []for name in dataset.class_to_idx.keys():names.append(name)# 保存所有剪裁过后的人脸,用于求特征向量cropped_faces = []# 每个类别的人脸数num_each_class = 10# image为图片,index为name对应的标签j = 0for image, index in loader:centerface = CenterFace()w, h = image.sizeboxes, lms = centerface(image, h, w, threshold=0.6)print(f"{names[index]} 检测人脸数:{len(boxes)}")extractFace = FaceExtract()resnet = InceptionResnetV1(pretrained='vggface2').eval().to(device)# j表示一个name下的第几张图片# i表示第几个人脸框for i, box in enumerate(boxes):box, score = box[:4], box[4]# 存储cropped face,创建文件夹path = cropped_save_pth + dataset.idx_to_class[index]if not os.path.exists(path):os.makedirs(path)save_path = path + f'/{j}.jpg'# face为tensorface = extractFace(image, box=box, save_path=save_path)cropped_faces.append(face)# tensor([[...]])#face_embedding = resnet(face.unsqueeze(0)).detach()# 每张图片只保存一个人脸向量#embedings.append(face_embedding[0])j += 1# breakif j % num_each_class == 0:j = 0aligned = torch.stack(cropped_faces).to(device)# 返回所有人脸的特征向量,每个向量的长度是512维embedings = resnet(aligned).detach().cpu()# 一个人有多个人脸,求平均特征向量# [tensor([...])]mean_embedings = []for i in range(len(dataset.idx_to_class)):emd = embedings[i * num_each_class:(i + 1) * num_each_class].mean(0)        # ???mean_embedings.append(emd)# dicts = [[(e1 - e2).norm().item() for e2 in mean_embedings] for e1 in mean_embedings]# print(pd.DataFrame(dicts, columns=names, index=names))if not os.path.exists(feature_path):os.makedirs(feature_path)# 将人脸特征向量和标签保存下来# 人脸特征向量和标签的index保持一致# names中的顺序应该和数据集类别的顺序相同torch.save(mean_embedings, feature_path + 'features.pt')torch.save(names, feature_path + 'names.pt')# add a new face to existing database
def AddNewFace2DB():a = 1# 将一个list的sample组成一个mini-batch的函数
def collate_fn(x):return x[0]if __name__ == "__main__":# os.name返回当前操作系统名字,nt为Windows,posix为linux# 线程数workers = 0 if os.name == 'nt' else 4device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")print(f"Running on {device}")DB = 3DB_path = f"../data/DB{DB}_images_school/"cropped_path = f"../data/DB{DB}_images_cropped/"feature_path = f"../data/DB{DB}_features/"CreatNewDB(DB_path, feature_path, cropped_path)

test_new_face.py
该脚本用于识别测试集中的人脸。实现了三个函数:detect_one用于识别单张图片,detect_multi识别一个数据集,detect_videos用于视频人脸识别。

from facenet_pytorch import MTCNN, InceptionResnetV1
import torch
from PIL import ImageDraw, Image, ImageFont
from torchvision import datasets
from torch.utils.data import DataLoader
import os
import time
import cv2
import numpy as np
from centerface import CenterFace, FaceExtract# mtcnn = MTCNN(keep_all=True)# 识别单张图片
def detect_one(img, names, embeddings):# 定义检测和识别人脸模型centerface = CenterFace(landmarks=True)extractFace = FaceExtract()resnet = InceptionResnetV1(pretrained='vggface2').eval()# 自定义字体格式fontStyle = ImageFont.truetype("SIMYOU.TTF", 24)# Image对象读大小w, h = img.sizeboxes, lms = centerface(img, h, w, threshold=0.5)# face = mtcnn(img)# boxes, prob = mtcnn.detect(img)  # 人脸检测器,直接返回人脸的位置坐标和概率frame_draw = img.copy()draw = ImageDraw.Draw(frame_draw)print("检测人脸数目:", len(boxes))for i, box in enumerate(boxes):# 人脸框,分数box, score = box[:4], box[4]x1, y1, x2, y2 = boxdraw.rectangle(box, outline=(255, 0, 0), width=2)# 人脸提取,返回的是标准化后的tensorface = extractFace(frame_draw, box=box)# 增加tensor维度,resnet输入参数为4维face_embedding = resnet(face.unsqueeze(0))# 求两个向量间的欧氏距离,tensor.norm()用于求2范数,即为欧氏距离probs = [(face_embedding - embeddings[i]).norm().item() for i in range(len(embeddings))]# 我们可以认为距离最近的那个就是最有可能的人,但也有可能出问题,数据库中可以存放一个人的多视角多姿态数据,对比的时候可以采用其他方法,如投票机制决定最后的识别人脸# 这里仅仅是简单地将向量归类为距离最近的那个向量,未设定阈值。# 改进思路:设定一个阈值范围,然后使用交叉验证计算训练集在不同阈值下的准确率,得到表现最好的阈值后,再利用该阈值在测试集上进行测试。该阈值将是我们区分不同人脸的重要阈值# 对应的索引就是判断的人脸# 保存最小的距离,保留三位小数distance = min(probs)# 设置一个阈值,超过阈值即为数据库中不存在的人脸# 这里的0.9只是根据部分结果观察得到的一个大概的阈值,并不是最准确的if distance > 0.9:name = 'unkonwn'else:index = probs.index(min(probs))name = names[index]# 打印出识别结果draw.text((x1 + 10, y1 + 5), str(name) + str(i + 1), fill='red', font=fontStyle, align='center')# 在左上角打印出每个人与识别出的类别之间的距离draw.text((0, i * 24), f"({i + 1}: {distance:.3f})", fill='red', font=fontStyle)return frame_drawdef collate_fn(x):return x[0]# detect a face dataset and save its result
def detect_multi(dataset_path, names, embeddings, save_path=None):dataset = datasets.ImageFolder(dataset_path)# 数据集总数total_nums = len(dataset)# 人脸类别数class_nums = len(dataset.class_to_idx.items())# 每个类别中的图片数,此例中每类中图片数相等才可这样计算# 用于图片命名num_one_class = total_nums / class_nums# dic类型dataset.idx_to_class = {i: c for c, i in dataset.class_to_idx.items()}loader = DataLoader(dataset, collate_fn=collate_fn)i = 0# iamge类型为<'PIL.Image.Image'>for image, index in loader:name = dataset.idx_to_class[index]path = save_path + name + '/'if not os.path.exists(path):os.makedirs(path)# img = Image.open(image)img = detect_one(image, names=names, embeddings=embeddings)img.save(path + f"{name}_{i}.jpg")i += 1if i % num_one_class == 0:i = 0# img.show()# breakdef detect_videos(video_path, names, embeddings, video_save_path=None):cap = cv2.VideoCapture(video_path)# frame是每一帧的图像,ret的类型为bool,表示是否读到图片ret, frame = cap.read()# 获取原视频帧数fps = cap.get(propId=cv2.CAP_PROP_FPS)print(fps)h, w = frame.shape[:2]fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')out = cv2.VideoWriter(video_save_path, fourcc, fps, (w, h), True)while ret:# opencv转Imageimg = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))img = detect_one(img=img, names=names, embeddings=embeddings)# Image转Opencvframe = cv2.cvtColor(np.asarray(img),cv2.COLOR_RGB2BGR)out.write(frame)ret, frame = cap.read()cap.release()out.release()# another way to read images dataset
# 可以替换ImageFolder的一种方法,最终未采用
def read_files(dataset_path, save_path):set_dirs = os.listdir(dataset_path)print(set_dirs)for class_name in set_dirs:dir_path = data_path + class_name + '/'imgs_dir = os.listdir(dir_path)for img_name in imgs_dir:img_path = dir_path + img_nameimg = Image.open(img_path)img = detect_one(img)path = save_path + class_name + '/'if not os.path.exists(path):os.makedirs(path)# img.save(path + f"{class_name}_{img_name}")print(path + f"{class_name}_{img_name}")breakif __name__ == '__main__':DB = 3# listnames = torch.load(f"../data/DB{DB}_features/names.pt")print(len(names))# 元素为tensor的list,每一个tensor包含一个list,表示一个人脸embeddings = torch.load(f"../data/DB{DB}_features/features.pt")print(len(embeddings))# print(names)# print(embeddings)img = Image.open("../data/DB3_images_school/刘瑶瑶/刘瑶瑶202022021932.jpg")#test_img = detect_one(img, names, embeddings)#test_img.show()# test_img.save("result.jpg")data_path = f"../data/DB{DB}_images_test/"save_path = f"../data/DB{DB}_images_test_results/"vs_path = "../data/videos/"video_name = "school"video_path = vs_path + video_name + ".mp4"video_save_path = vs_path + video_name + "_result.mp4"start_time = time.time()# detect_multi(dataset_path=data_path, save_path=save_path, names=names, embeddings=embeddings)detect_videos(video_path=video_path, video_save_path=video_save_path, names=names, embeddings=embeddings)end_time = time.time()print(f"time(s): {end_time - start_time:.2f}")

clawer.py
用于从网页上爬取人脸图片并建立数据文件夹。

import requests
import re
import os
import time
import randomdef get_parse_page(name, action, page=1, DB=1):for i in range(int(page)):# print(f"正在获取第{i + 1}页")# 百度搜索图片时的链接# 只需传入word和pn参数# word为查询关键字,pn为页数# flip参数为翻页查看,还有index为下滑查看,翻页利于爬取图片url = f"https://image.baidu.com/search/flip?tn=baiduimage&ie=utf-8&word={name}&pn={page}"headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36"}# 发送请求,获取响应response = requests.get(url, headers=headers)html = response.content.decode()# 正则表达式获取属性为objURL的value# 返回一个列表,元素为url地址,大小为一页60个results = re.findall('"objURL":"(.*?)",', html)# 这里的action根据用户的输入来下载图片到不同文件夹# origin:用于求人脸特征向量的原始图片# test:用于测试的数据集# single:用于测试的单张图片# save_path = Noneif action == 'origin':# 取每页的第一个10张results = results[:10]save_path = f"../data/DB{DB}_images/" + nameelif action == 'test':# 取每页的第二个10张results = results[11:20]save_path = f"../data/DB{DB}_images_test/" + nameelif action == 'single':# 随机取一张t = random.randint(0, 60)results = results[t]save_path = "../data/test_faces_single/" + namesaveImages(results, save_path, i)def saveImages(results, save_path, i):# 创建文件夹if not os.path.exists(save_path):os.makedirs(save_path)# 下载图片for j, result in enumerate(results):try:image = requests.get(result, timeout=5)# 随机休眠0~1秒钟time.sleep(random.random())# time.sleep(1)except:print(f"当前图片无法下载:\n{result}")continue# 这里应该要判断一下图片的大小,太大的图片不能要。# 图片按数字顺序命名:i为页数,j为偏移量image_name = save_path + f'/{i * 60 + j}.jpg'with open(image_name, 'wb') as f:f.write(image.content)if __name__ == '__main__':names =['鹿晗', '关晓彤']# origin表示图片是用于计算特征向量的# test用于识别测试actions = ['origin', 'test']DB = 2for name in names:for action in actions:# page = input("请输入下载页数(1页有60张图片):")# action = input("请输入图片的用途(origin/test/single):")get_parse_page(name=name, action=action, DB=DB)print(f"{name}+{action} done!")

centerface.py
原有centerface.py只是检测出人脸,然后返回人脸框和关键点坐标。但是人脸识别需要提取图片中固定大小的人脸,因此在该脚本中增加了一个提取人脸的类,该类的方法来自于MTCNN中提取人脸的方法。

import numpy as np
import cv2
import datetime
import torch
from torchvision.transforms import functional as F
from torch.nn.functional import interpolate
from PIL import Image
import io# 检测人脸
class CenterFace(object):def __init__(self, landmarks=True):self.landmarks = landmarks# 读取网络模型self.net = cv2.dnn.readNetFromONNX('../models/centerface-onnx/centerface.onnx')self.img_h_new, self.img_w_new, self.scale_h, self.scale_w = 0, 0, 1, 1# 1def __call__(self, img, height, width, threshold=0.5):# print(img.shape)self.img_h_new, self.img_w_new, self.scale_h, self.scale_w = self.transform(height, width)# print(self.img_h_new,self.img_w_new,self.scale_h,self.scale_w)# 将Image对象转为ndarray对象if not isinstance(img, np.ndarray):img = cv2.cvtColor(np.asarray(img),cv2.COLOR_RGB2BGR)# cv2.imshow("1",img)return self.inference_opencv(img, threshold)# 3def inference_opencv(self, img, threshold):# 对图像进行预处理:1 减均值;2 缩放;3 通道交换# mean为减去的均值————swapRB,OpenCV图像的通道顺序为BGR,要转换成RGBblob = cv2.dnn.blobFromImage(img, scalefactor=1.0, size=(self.img_w_new, self.img_h_new), mean=(0, 0, 0),swapRB=True, crop=False)# print(blob.shape)# 预训练模型中的函数?self.net.setInput(blob)begin = datetime.datetime.now()if self.landmarks:# 都是ndarray类型heatmap, scale, offset, lms = self.net.forward(["537", "538", "539", '540'])else:heatmap, scale, offset = self.net.forward(["535", "536", "537"])end = datetime.datetime.now()# print("cpu times = ", end - begin)return self.postprocess(heatmap, lms, offset, scale, threshold)# 2 这个函数的作用是???def transform(self, h, w):img_h_new, img_w_new = int(np.ceil(h / 32) * 32), int(np.ceil(w / 32) * 32)scale_h, scale_w = img_h_new / h, img_w_new / wreturn img_h_new, img_w_new, scale_h, scale_w# 4def postprocess(self, heatmap, lms, offset, scale, threshold):if self.landmarks:dets, lms = self.decode(heatmap, scale, offset, lms, (self.img_h_new, self.img_w_new), threshold=threshold)else:dets = self.decode(heatmap, scale, offset, None, (self.img_h_new, self.img_w_new), threshold=threshold)if len(dets) > 0:dets[:, 0:4:2], dets[:, 1:4:2] = dets[:, 0:4:2] / self.scale_w, dets[:, 1:4:2] / self.scale_hif self.landmarks:lms[:, 0:10:2], lms[:, 1:10:2] = lms[:, 0:10:2] / self.scale_w, lms[:, 1:10:2] / self.scale_helse:dets = np.empty(shape=[0, 5], dtype=np.float32)if self.landmarks:lms = np.empty(shape=[0, 10], dtype=np.float32)if self.landmarks:return dets, lmselse:return dets# 5def decode(self, heatmap, scale, offset, landmark, size, threshold=0.1):heatmap = np.squeeze(heatmap)scale0, scale1 = scale[0, 0, :, :], scale[0, 1, :, :]offset0, offset1 = offset[0, 0, :, :], offset[0, 1, :, :]c0, c1 = np.where(heatmap > threshold)if self.landmarks:boxes, lms = [], []else:boxes = []if len(c0) > 0:for i in range(len(c0)):s0, s1 = np.exp(scale0[c0[i], c1[i]]) * 4, np.exp(scale1[c0[i], c1[i]]) * 4o0, o1 = offset0[c0[i], c1[i]], offset1[c0[i], c1[i]]s = heatmap[c0[i], c1[i]]x1, y1 = max(0, (c1[i] + o1 + 0.5) * 4 - s1 / 2), max(0, (c0[i] + o0 + 0.5) * 4 - s0 / 2)x1, y1 = min(x1, size[1]), min(y1, size[0])boxes.append([x1, y1, min(x1 + s1, size[1]), min(y1 + s0, size[0]), s])if self.landmarks:lm = []for j in range(5):lm.append(landmark[0, j * 2 + 1, c0[i], c1[i]] * s1 + x1)lm.append(landmark[0, j * 2, c0[i], c1[i]] * s0 + y1)lms.append(lm)boxes = np.asarray(boxes, dtype=np.float32)keep = self.nms(boxes[:, :4], boxes[:, 4], 0.3)boxes = boxes[keep, :]if self.landmarks:lms = np.asarray(lms, dtype=np.float32)lms = lms[keep, :]if self.landmarks:return boxes, lmselse:return boxes# 6def nms(self, boxes, scores, nms_thresh):x1 = boxes[:, 0]y1 = boxes[:, 1]x2 = boxes[:, 2]y2 = boxes[:, 3]areas = (x2 - x1 + 1) * (y2 - y1 + 1)order = np.argsort(scores)[::-1]num_detections = boxes.shape[0]suppressed = np.zeros((num_detections,), dtype=np.bool)keep = []for _i in range(num_detections):i = order[_i]if suppressed[i]:continuekeep.append(i)ix1 = x1[i]iy1 = y1[i]ix2 = x2[i]iy2 = y2[i]iarea = areas[i]for _j in range(_i + 1, num_detections):j = order[_j]if suppressed[j]:continuexx1 = max(ix1, x1[j])yy1 = max(iy1, y1[j])xx2 = min(ix2, x2[j])yy2 = min(iy2, y2[j])w = max(0, xx2 - xx1 + 1)h = max(0, yy2 - yy1 + 1)inter = w * hovr = inter / (iarea + areas[j] - inter)if ovr >= nms_thresh:suppressed[j] = Truereturn keep# 提取人脸
class FaceExtract(object):def __init__(self):# 无意义,只是为了让构造函数非空self.empty = 1def __call__(self, img, box, target_size=160, margin=0, save_path=None, post_process=True):return self.extract_face(img, box, target_size=target_size, margin=margin, save_path=save_path,post_process=post_process)def extract_face(self, img, box, target_size, margin, save_path, post_process):"""Extract face + margin from PIL Image given bounding box.Arguments:img {PIL.Image} -- A PIL Image.box {numpy.ndarray} -- Four-element bounding box.target_size {int} -- Output image size in pixels. The image will be square.margin {int} -- Margin to add to bounding box, in terms of pixels in the final image.Note that the application of the margin differs slightly from the davidsandberg/facenetrepo, which applies the margin to the original image before resizing, making the margindependent on the original image size.save_path {str} -- Save path for extracted face image. (default: {None})Returns:torch.tensor -- tensor representing the extracted face."""# face = extract_face(img, box, target_size, margin, save_path)margin = [margin * (box[2] - box[0]) / (target_size - margin),margin * (box[3] - box[1]) / (target_size - margin),]raw_img_size = self.get_image_size(img)# ???box = [int(max(box[0] - margin[0] / 2, 0)),int(max(box[1] - margin[1] / 2, 0)),int(min(box[2] + margin[0] / 2, raw_img_size[0])),int(min(box[3] + margin[1] / 2, raw_img_size[1])),]# cv2读的图像为ndarray对象,(160,160,3)# PIL读的为Image对象face = self.crop_resize(img, box, target_size)if save_path is not None:self.save_img(face, save_path)# tensorface = F.to_tensor(np.float32(face))# 标准化if post_process:face = (face - 127.5) / 128.0return facedef get_image_size(self, img):if isinstance(img, (np.ndarray, torch.Tensor)):return img.shape[1::-1]else:return img.sizedef crop_resize(self, img, box, target_size):"""resize the shape of a face"""if isinstance(img, np.ndarray):img = img[box[1]:box[3], box[0]:box[2]]out = cv2.resize(img,(target_size, target_size),interpolation=cv2.INTER_AREA).copy()elif isinstance(img, torch.Tensor):img = img[box[1]:box[3], box[0]:box[2]]out = self.imresample(img.permute(2, 0, 1).unsqueeze(0).float(),(target_size, target_size)).byte().squeeze(0).permute(1, 2, 0)else:out = img.crop(box).copy().resize((target_size, target_size), Image.BILINEAR)return outdef save_img(self, img, path):"""cv2读取的图片为 ndarray 格式,读图片时使用的是cv2,保存时直接imwritePIL读取的为 Image 对象格式,读图片时用的是"""if isinstance(img, np.ndarray):cv2.imwrite(path, img)# cv2.imwrite(path, cv2.cvtColor(img, cv2.COLOR_RGB2BGR))else:img.save(path)def imresample(self, img, sz):im_data = interpolate(img, size=sz, mode="area")return im_data

注意
(1)以上脚本需要在跑通原本facenet项目基础上做改动,才能正确运行
(2)由于centerface读取图片采用的是opencv库,facenet用的是PIL库,两个库读取出的图片格式不相同,我曾尝试将其中一种换成另外一种,但是后面太麻烦了,放弃了。然后找到了一种两者之间相互转化的方法,很方便。还有就是需要在centerface.py脚本的__call__函数中也要注意格式转化。这些在上述代码中都有体现,读者读到了重点注意即可。
(3)上述项目代码都是自己一步一步写的,不容易啊,转载请注明出处!

项目结果展示

这是centerface的检测结果

这是MTCNN的检测结果

这里只用一张图片只是为了简单说明centerface的检测效果优于MTCNN(只在此项目中,不适用于其他项目),识别都是用facenet中的方法。

视频人脸识别
一个视频识别demo(爱情公寓5片段),本来想传到B站,但是审核未通过,就百度云分享
地址链接:https://pan.baidu.com/s/17rAoOYzwFaX47C1gaoiTHw 提取码:1v3v

最后

我是一名不知道入没入门的CV小菜鸟,欢迎大家一起讨论!!!
有问题请留言,看到会回复的。

Centerface + Facenet实现视频人脸识别(附代码)相关推荐

  1. MTCNN+ArcFaceloss实现视频人脸识别理论与实践(附代码)

    一.人脸识别的概念 人脸识别的官方解释和通俗解释 人脸识别,是基于人的脸部特征信息(角点,凸起,轮廓等信息,AI提取的特征向量)进行身份识别的一种生物识别技术.用摄像机或摄像头采集含有人脸的图像或视频 ...

  2. 麦子学院深度学习视频SVM人脸识别课程代码修改及实现

    1.麦子学院深度学习SVM人脸识别原代码对应修改 2.代码实现 1.麦子学院深度学习SVM人脸识别原代码对应修改 1.1 from sklearn.cross_validation import tr ...

  3. C++ OpenCV相片视频人脸识别统计人数

    C++ OpenCV相片视频人脸识别统计人数 如需远程调试,可加QQ905733049由专业技术人员远程协助! 运行代码如下: #include <iostream> #include&l ...

  4. 调用笔记本的摄像头实现基于opencv的视频人脸识别(中文显示和英文显示)以及 index 480 is out of bounds for axis 0 with size 480错误的解决

    @人脸识别代码和一些常见错误 基于opencv的视频人脸识别(中文显示)以及 index 480 is out of bounds for axis 0 with size 480错误的解决 参考了 ...

  5. 实战演示:使用Python编写人脸识别测试代码,让你的应用拥有智能感知能力

    人脸识别是计算机视觉领域的一个重要应用.它使用计算机算法来识别和验证面部特征,通常用于安全认证.视频监控.人脸比对等方面.近年来,随着深度学习技术的发展,人脸识别的性能得到了极大的提升,成为了智能感知 ...

  6. 深度学习之视频人脸识别系列一:介绍

    作者 | 东田应子 [导读]本文是深度学习之视频人脸识别系列的第一篇文章,介绍了人脸识别领域的一些基本概念,分析了深度学习在人脸识别的基本流程,并总结了近年来科研领域的研究进展,最后分析了静态数据与视 ...

  7. 视频人脸识别和图片人脸识别的关系

    首先解释下视频人脸识别和图片人脸识别的区别,视频人脸识别是基于视频流进行人脸识别,用户的感觉就是直接在视频中就可以识别出人脸,而图片人脸识别,是用户直接上传图片,输出识别结果. 图片人脸识别可以描述为 ...

  8. 深度学习之视频人脸识别系列(一):简介

    阅读时间约4分钟 [介绍]本文是深度学习之视频人脸识别系列的第一篇文章,介绍了人脸识别领域的一些基本概念,分析了深度学习在人脸识别的基本流程,并总结了近年来科研领域的研究进展,最后分析了静态数据与视频 ...

  9. opencv联合dlib视频人脸识别例子

    本篇文章是在上一篇文章opencv联合dlib人脸识别例子 的基础上做了一个实时视频人脸识别功能. 原理是利用opencv实时提取视频中的视频流,然后进入人脸检测步骤,步骤类似上篇文章. 本篇文章中的 ...

最新文章

  1. 完整恢复模式下的备份/恢复
  2. Java Day 13
  3. require php 5.3.0,PHP 5.3.0 安装分析心得
  4. 第四章例题、心得及问题。
  5. linux虚拟中断virq,一种微内核操作系统的分区多核方法与流程
  6. FbinstTool最简单制作U盘启动ISO格式(金测)
  7. python 方差齐性检验_SPSS方差齐性检验(图文+视频教程)
  8. 华为hcna认证考完多久考hcip?华为认证的发展就业前景怎么样?
  9. abd串口工具使用教程_adb调试工具包(adb调试程序)
  10. window下内网远程控制工具
  11. 数据分析-PART1--数据获取和步骤
  12. 如何VUE写桌面应用(electron)
  13. 使用 Jenkins 创建微服务应用的持续集成
  14. 秦刚推荐:做流量的本质就是做用户
  15. 笔记本win10正在更新怎么关闭计算机,教你win10如何关闭自动更新
  16. 【微服务】如何实现微服务集群的高可靠?
  17. SAP 银企直连 通过 Http Get 方式下载交易明细文件
  18. EXCEL提示APPCRASH的解决方案
  19. 电脑提示vcruntime140_1.dll丢失怎么安装?
  20. Dockerfile实践指南之RUN命令使用

热门文章

  1. 苏强SN系列服务器说明书,SN2000交流伺服驱动器使用手册.pdf
  2. win 7 与 virtualbox ubuntu 共享文件夹
  3. 华为服务器cpu位置,服务器cpu参数详解
  4. 5.18 对表格按行进行排序 [原创Excel教程]
  5. ORAN专题系列-19:5G O-RAN FrontHaul前传接口M Plane互操作性测试IOT规范
  6. 股票入门浅学20210721
  7. 《麦田里的守望者》阅读笔记
  8. Data too long for column ‘password‘ at row 1“
  9. win10安装红警运行出现FATALString Manager failed to initilaized properly
  10. C++ 的算术、自增自减、位运算、关系与逻辑运算符(数据的操作)