#from keras.models import Model,Sequential
import tensorflow as tf
import numpy as np
import cv2
import os
from functools import partial
#from keras.layers import ZeroPadding2D,Depthwise,Conv2D,Activation,BatchNormalization,Concatenate,MaxPool2D,Conv2D,Dense,Dropout,GlobalAveragePooling2D
#from keras.layers import Input,Lambda,MaxPooling2D,add,Reshape,Permute,PReLU,Flatten
from keras import backend as K
import sys
from operator import itemgetter
import math
import matplotlib.pyplot as plt
from keras.models import *
from keras.layers import *
import keras
from keras.callbacks import TensorBoard,ModelCheckpoint,ReduceLROnPlateau,EarlyStopping
from keras.applications.imagenet_utils import preprocess_input
from keras.utils import np_utils,get_file
from PIL import Image
from keras.optimizers import Adam
from matplotlib.colors import rgb_to_hsv, hsv_to_rgb
1
#1.生成数据名+类别(0,1)的txt文件
classes=['mask','nomask']
with open('./data/train.txt','w') as f:
after_generate=os.listdir('./data/image/train')
for image in after_generate:
if image.endswith('jpg'):
f.write(image+';'+str(classes.index(image.split('_')[0]))+'\n')
2
#2.mtcnn检测出人脸
#2.1构建原图金字塔
def calculateScales(img):
copy_img=img.copy()
pr_scale=1.0
h,w,_=copy_img.shape
if min(w,h)>500:
pr_scale=500.0/min(h,w)
w=int(w*pr_scale)
h=int(h*pr_scale)
elif max(w,h)<500:
pr_scale=500.0/max(h,w)
w=int(w*pr_scale)
h=int(h*pr_scale)
scales=[]
factor=0.709
factor_count=0
minl=min(h,w)
while minl>=12:
scales.append(pr_scale*pow(factor,factor_count))
minl*=factor
factor_count+=1
return scales#返回图像金字塔的缩放比例列表
#2.2金字塔图像输入到Pnet大致预测人脸位置与概率
def create_Pnet(weight_path):
inp=Input(shape=[None,None,3])
x=Conv2D(10,(3,3),strides=1,padding='valid',name='conv1')(inp)
x=PReLU(shared_axes=[1,2],name='PReLU1')(x)
x=MaxPool2D(pool_size=2)(x)
x=Conv2D(16,(3,3),strides=1,padding='valid',name='conv2')(x)
x=PReLU(shared_axes=[1,2],name='PReLU2')(x)
x=Conv2D(32,(3,3),strides=1,padding='valid',name='conv3')(x)
x=PReLU(shared_axes=[1,2],name='PReLU3')(x)
classifier=Conv2D(2,(1,1),activation='softmax',name='conv4_1')(x)#类别预测值
bbox_regress=Conv2D(4,(1,1),name='conv4_2')(x)#人脸框的预测位置
model=Model([inp],[classifier,bbox_regress])#返回的是三维
model.load_weights(weight_path,by_name=True)
return model#返回带有与训练参数的Pnet模型
#2.3对Pnet的输出结果调整,width,height是Pnet输出时的图片大小,cls_prob对应输出图片的每个像素点由脸的概率
def detect_face_12net(cls_prob,roi,out_side,scale,width,height,threshold):
cls_prob=np.swapaxes(cls_prob,0,1)
roi=np.swapaxes(roi,0,2)
stride=0
if out_side!=1:#out_side是输出图片的最大边长
stride=float(2*out_side-1)/(out_side-1)
(x,y)=np.where(cls_prob>=threshold)#找出有人脸概率大于阈值的像素点的坐标
score=np.array([cls_prob[x,y]]).T#拿出他的概率值
boundingbox=np.array([x,y]).T
bb1=np.fix((stride*boundingbox+0)*scale)#将在pnet输出图像上的x,y映射到原图的坐标上
bb2=np.fix((stride*boundingbox+11)*scale)
boundingbox=np.concatenate((bb1,bb2),axis=1)
dx1=roi[0][x,y]#拿出人脸概率大于阈值的像素点的坐标预测模型的四个输出值,
dx2=roi[1][x,y]
dx3=roi[2][x,y]
dx4=roi[3][x,y]
offset=np.array([dx1,dx2,dx3,dx4]).T
boundingbox=boundingbox+offset*12.0*scale#将框的x,y映射到原图上
rectangles=np.concatenate((boundingbox,score),axis=1)
rectangles=rect2square(rectangles)
pick=[]
for i in range(len(rectangles)):#将框位置和概率值在原图上的映射经过非极大抑制等方法后输出
x1=int(max(0,rectangles[i][0]))
y1=int(max(0,rectangles[i][1]))
x2=int(min(width,rectangles[i][2]))
y2=int(min(height,rectangles[i][3]))
sc=rectangles[i][4]
if x2>x1 and y2>y1:
pick.append([x1,y1,x2,y2,sc])
return NMS(pick,0.3)
#2.4对pnet的结果处理后会裁剪下其预测框并resize到24,24,3输入到rnet中
def create_Rnet(weight_path):
inp=Input(shape=[24,24,3])
x=Conv2D(28,(3,3),strides=1,padding='valid',name='conv1')(inp)
x=PReLU(shared_axes=[1,2],name='prelu1')(x)
x=MaxPool2D(pool_size=3,strides=2,padding='same')(x)
x=Conv2D(48,(3,3),strides=1,padding='valid',name='conv2')(x)
x=PReLU(shared_axes=[1,2],name='prelu2')(x)
x=MaxPool2D(pool_size=3,strides=2)(x)
x=Conv2D(64,(2,2),strides=1,padding='valid',name='conv3')(x)
x=PReLU(shared_axes=[1,2],name='prelu3')(x)
x=Permute((3,2,1))(x)
x=Flatten()(x)
x=Dense(128,name='conv4')(x)
x=PReLU(name='prelu4')(x)
classifier=Dense(2,activation='softmax',name='conv5-1')(x)
bbox_regress=Dense(4,name='conv5-2')(x)#返回的是一维
model=Model([inp],[classifier,bbox_regress])
model.load_weights(weight_path,by_name=True)
return model
#2.5对rnet的输出结果进行调整
def filter_face_24net(cls_prob,roi,rectangles,width,height,threshold):
prob=cls_prob[:,1]
pick=np.where(prob>=threshold)
sc=np.array([prob[pick]]).T
rectangles=np.array(rectangles)
x1=rectangles[pick,0]
y1=rectangles[pick,1]
x2=rectangles[pick,2]
y2=rectangles[pick,3]#输入到Rnet的框中符合阈值条件的框的位置,即原图上的坐标
dx1=roi[pick,0]#从rnet输出的符合阈值的框网络预测输出位置
dx2=roi[pick,1]
dx3=roi[pick,2]
dx4=roi[pick,3]
w=x2-x1
h=y2-y1
x1=np.array([(x1+dx1*w)[0]]).T#符合条件的框在原图上的坐标位置
y1=np.array([(y1+dx2*h)[0]]).T
x2=np.array([(x2+dx3*w)[0]]).T
y2=np.array([(y2+dx4*h)[0]]).T
rectangles=np.concatenate((x1,y1,x2,y2,sc),axis=1)
rectangles=rect2square(rectangles)
pick=[]
for i in range(len(rectangles)):#确保选出来的矩形框的坐标点不超出范围
x1 = int(max(0 ,rectangles[i][0]))
y1 = int(max(0 ,rectangles[i][1]))
x2 = int(min(width ,rectangles[i][2]))
y2 = int(min(height,rectangles[i][3]))
sc = rectangles[i][4]
if x2>x1 and y2>y1:
pick.append([x1,y1,x2,y2,sc])
return NMS(pick,0.3)
#2.6将rnet的结果经过调整裁剪下来resize到48,48,输入到onet
def create_Onet(weight_path):
inp=Input(shape=[48,48,3])
x=Conv2D(32,(3,3),strides=1,padding='valid',name='conv1')(inp)
x=PReLU(shared_axes=[1,2],name='prelu')(x)
x=MaxPool2D(pool_size=3,strides=2,padding='same')(x)
x=Conv2D(64,(3,3),strides=1,padding='valid',name='conv2')(x)
x=PReLU(shared_axes=[1,2],name='prelu2')(x)
x=MaxPool2D(pool_size=3,strides=2)(x)
x=Conv2D(64,(3,3),strides=1,padding='valid',name='conv3')(x)
x=PReLU(shared_axes=[1,2],name='prelu3')(x)
x=MaxPool2D(pool_size=2)(x)
x=Conv2D(128,(2,2),strides=1,padding='valid',name='conv4')(x)
x=PReLU(shared_axes=[1,2],name='prelu4')(x)
x=Permute((3,2,1))(x)
x=Flatten()(x)
x=Dense(256,name='conv5')(x)
x=PReLU(name='prelu5')(x)
classifier=Dense(2,activation='softmax',name='conv6-1')(x)
bbox_regress=Dense(4,name='conv6-2')(x)
landmark_regress=Dense(10,name='conv6_3')(x)#五个人脸监测点的左上与右下坐标
model=Model([inp],[classifier,bbox_regress,landmark_regress])
model.load_weights(weight_path,by_name=True)
return model
#2.7对Onet的输出进行调整
def filter_face_48net(cls_prob,roi,pts,rectangles,width,height,threshold):
prob=cls_prob[:,1]#有人脸的概率值
pick=np.where(prob>=threshold)
sc=np.array([prob[pick]]).T
rectangles=np.array(rectangles)
x1=rectangles[pick,0]
y1=rectangles[pick,1]
x2=rectangles[pick,2]
y2=rectangles[pick,3]#输入到Onet的框中符合阈值条件的框的位置,即原图上的坐标
dx1=roi[pick,0]#从Onet输出的符合阈值的框网络预测输出位置
dx2=roi[pick,1]
dx3=roi[pick,2]
dx4=roi[pick,3]
w=x2-x1
h=y2-y1
pts0= np.array([(w*pts[pick,0]+x1)[0]]).T#人脸点坐标映射到原图上
pts1= np.array([(h*pts[pick,5]+y1)[0]]).T
pts2= np.array([(w*pts[pick,1]+x1)[0]]).T
pts3= np.array([(h*pts[pick,6]+y1)[0]]).T
pts4= np.array([(w*pts[pick,2]+x1)[0]]).T
pts5= np.array([(h*pts[pick,7]+y1)[0]]).T
pts6= np.array([(w*pts[pick,3]+x1)[0]]).T
pts7= np.array([(h*pts[pick,8]+y1)[0]]).T
pts8= np.array([(w*pts[pick,4]+x1)[0]]).T
pts9= np.array([(h*pts[pick,9]+y1)[0]]).T
x1 = np.array([(x1+dx1*w)[0]]).T
y1 = np.array([(y1+dx2*h)[0]]).T
x2 = np.array([(x2+dx3*w)[0]]).T
y2 = np.array([(y2+dx4*h)[0]]).T
rectangles=np.concatenate((x1,y1,x2,y2,sc,pts0,pts1,pts2,pts3,pts4,pts5,pts6,pts7,pts8,pts9),axis=1)
pick = []
for i in range(len(rectangles)):
x1 = int(max(0 ,rectangles[i][0]))
y1 = int(max(0 ,rectangles[i][1]))
x2 = int(min(width ,rectangles[i][2]))
y2 = int(min(height,rectangles[i][3]))
if x2>x1 and y2>y1:
pick.append([x1,y1,x2,y2,rectangles[i][4],
rectangles[i][5],rectangles[i][6],rectangles[i][7],rectangles[i][8],rectangles[i][9],rectangles[i][10],rectangles[i][11],rectangles[i][12],rectangles[i][13],rectangles[i][14]])
return NMS(pick,0.3)
#2.8矩形转正方形,非极大抑制
def rect2square(rectangles):
w = rectangles[:,2] - rectangles[:,0]
h = rectangles[:,3] - rectangles[:,1]
l = np.maximum(w,h).T
rectangles[:,0] = rectangles[:,0] + w*0.5 - l*0.5
rectangles[:,1] = rectangles[:,1] + h*0.5 - l*0.5
rectangles[:,2:4] = rectangles[:,0:2] + np.repeat([l], 2, axis = 0).T
return rectangles
def NMS(rectangles,threshold):
if len(rectangles)==0:
return rectangles
boxes = np.array(rectangles)
x1 = boxes[:,0]
y1 = boxes[:,1]
x2 = boxes[:,2]
y2 = boxes[:,3]
s = boxes[:,4]
area = np.multiply(x2-x1+1, y2-y1+1)
I = np.array(s.argsort())
pick = []
while len(I)>0:
xx1 = np.maximum(x1[I[-1]], x1[I[0:-1]]) #I[-1] have hightest prob score, I[0:-1]->others
yy1 = np.maximum(y1[I[-1]], y1[I[0:-1]])
xx2 = np.minimum(x2[I[-1]], x2[I[0:-1]])
yy2 = np.minimum(y2[I[-1]], y2[I[0:-1]])
w = np.maximum(0.0, xx2 - xx1 + 1)
h = np.maximum(0.0, yy2 - yy1 + 1)
inter = w * h
o = inter / (area[I[-1]] + area[I[0:-1]] - inter)
pick.append(I[-1])
I = I[np.where(o<=threshold)[0]]
result_rectangle = boxes[pick].tolist()
return result_rectangle
#2.9网络搭建构成mtcnn类
class mtcnn():
def __init__(self):
self.Pnet=create_Pnet('model_data/pnet.h5')
self.Rnet=create_Rnet('model_data/rnet.h5')
self.Onet=create_Onet('model_data/onet.h5')
def detectFace(self,img,threshold):
copy_img=(img.copy()-127.5)/127.5
origin_h,origin_w,_=copy_img.shape#原图大小
scales=calculateScales(img)
out=[]
for scale in scales:
hs=int(origin_h*scale)
ws=int(origin_w*scale)
scale_img=cv2.resize(copy_img,(ws,hs))
inputs=scale_img.reshape(1,*scale_img.shape)#对一个普通变量使用单星号前缀,能够将这个变量拆分成单个元素
output=self.Pnet.predict(inputs)#batch_size=1
out.append(output)
image_num=len(scales)
rectangles=[]
for i in range(image_num):
cls_prob=out[i][0][0][:,:,1]#经过psnet预测后的输出图片上每个像素上的人脸概率
roi=out[i][1][0]
out_h,out_w=cls_prob.shape#Pnet输出的图片的大小
out_size=max(out_h,out_w)#输出图片的最大边长
rectangle=detect_face_12net(cls_prob,roi,out_size,1/scales[i],origin_w,origin_h,threshold[0])
rectangles.extend(rectangle)#pnet的输出经过处理后的框
rectangles=NMS(rectangles,0.7)
if len(rectangles)==0:
return rectangles
predict_24_batch=[]
for rectangle in rectangles:
crop_img=copy_img[int(rectangle[1]):int(rectangle[3]),int(rectangle[0]):int(rectangle[2])]
scale_img=cv2.resize(crop_img,(24,24))#将pnet的预测裁剪下来,resize
predict_24_batch.append(scale_img)##将要输入到rnet网络的框
predict_24_batch=np.array(predict_24_batch)
out=self.Rnet.predict(predict_24_batch)
cls_prob=out[0]
cls_prob=np.array(cls_prob)
roi_prob=out[1]
roi_prob=np.array(roi_prob)
rectangles=filter_face_24net(cls_prob,roi_prob,rectangles,origin_w,origin_h,threshold[1])
if len(rectangles)==0:
return rectangles
predict_batch=[]
for rectangle in rectangles:
crop_img=copy_img[int(rectangle[1]):int(rectangle[3]),int(rectangle[0]):int(rectangle[2])]
scale_img=cv2.resize(crop_img,(48,48))
predict_batch.append(scale_img)
predict_batch=np.array(predict_batch)
output=self.Onet.predict(predict_batch)
cls_prob=output[0]
roi_prob=output[1]
pts_prob=output[2]
rectangles=filter_face_48net(cls_prob,roi_prob,pts_prob,rectangles, origin_w, origin_h, threshold[2])
return rectangles
3
#3.搭建mobilenet网络,对图片进行分类
IMAGE_ORDERING='channels_last'
channel_axis=1 if IMAGE_ORDERING=='channles_first' else -1 #获取图像的channels位置
def relu6(x):
return K.relu(x,max_value=6)
def _conv_block(inputs,filters,alpha,kernel=(3,3),strides=(1,1)):#普通卷积块
filters=int(filters*alpha)#设置卷积核的个数
x=ZeroPadding2D(padding=(1,1),name='conv1_pad',data_format=IMAGE_ORDERING)(inputs)
x=Conv2D(filters,kernel,data_format=IMAGE_ORDERING,padding='valid',use_bias=False,strides=strides,name='conv1')(x)
x=BatchNormalization(axis=channel_axis,name='conv1_bn')(x)
return Activation(relu6,name='conv1_relu')(x)
def _depthwise_conv_block(inputs,pointwise_conv_filters,alpha,depth_multiplier=1,strides=(1,1),block_id=1):#深度可分离卷积块
pointwise_conv_filters=int(pointwise_conv_filters*alpha)#卷积核个数
x=ZeroPadding2D((1,1),data_format=IMAGE_ORDERING,name='conv_pad_%d'%block_id)(inputs)
x=DepthwiseConv2D((3,3),data_format=IMAGE_ORDERING,padding='valid',depth_multiplier=depth_multiplier,strides=strides,use_bias=False,name='conv_dw_%d'%block_id)(x)
x=BatchNormalization(axis=channel_axis,name='conv_dw_%d_bn'%block_id)(x)
x=Activation(relu6,name='conv_dw_%d_relu'%block_id)(x)
x=Conv2D(pointwise_conv_filters,(1,1),data_format=IMAGE_ORDERING,padding='same',use_bias=False,strides=(1,1),name='conv_pw_%d'%block_id)(x)
x=BatchNormalization(axis=channel_axis,name='conv_pw_%d_bn'%block_id)(x)
return Activation(relu6,name='conv_pw_%d_relu'%block_id)(x)
def get_mobilenet_encoder(input_height=416,input_width=416,classes=1000):
alpha=1.0
depth_multiplier=1
dropout=1e-3
img_input=Input(shape=(input_height,input_width,3))
x=_conv_block(img_input,32,alpha,strides=(2,2))#普通卷积快,图像缩小一半
x=_depthwise_conv_block(x,64,alpha,depth_multiplier,block_id=1)#深度可分离,图像大小不变
f1=x
x=_depthwise_conv_block(x,128,alpha,depth_multiplier,strides=(2,2),block_id=2)#图像缩小一倍
x=_depthwise_conv_block(x,128,alpha,depth_multiplier,block_id=3)
f2=x
x=_depthwise_conv_block(x,256,alpha,depth_multiplier,strides=(2,2),block_id=4)#缩小一半
x=_depthwise_conv_block(x,256,alpha,depth_multiplier,block_id=5)
f3=x
x=_depthwise_conv_block(x,512,alpha,depth_multiplier,strides=(2,2),block_id=6)#缩小一半
x=_depthwise_conv_block(x,512,alpha,depth_multiplier,block_id=7)
x=_depthwise_conv_block(x,512,alpha,depth_multiplier,block_id=8)
x=_depthwise_conv_block(x,512,alpha,depth_multiplier,block_id=9)
x=_depthwise_conv_block(x,512,alpha,depth_multiplier,block_id=10)
x=_depthwise_conv_block(x,512,alpha,depth_multiplier,block_id=11)
f4=x
x=_depthwise_conv_block(x,1024,alpha,depth_multiplier,strides=(2,2),block_id=12)#缩小一半
x=_depthwise_conv_block(x,1024,alpha,depth_multiplier,block_id=13)
f5=x
x=GlobalAveragePooling2D()(x)
x=Reshape((1,1,1024))(x)
x=Dropout(1e-3)(x)
x=Conv2D(classes,(1,1),padding='same')(x)
x=Activation('softmax')(x)
x=Reshape((classes,))(x)
model=Model(img_input,x,name='mobilenet')
return model
4
#4.读取图片数据,训练mobilenet对口罩人脸的分类能力
def rand(a=0, b=1):
return np.random.rand()*(b-a) + a
def get_random_data(image, input_shape, random=True, jitter=.1, hue=.1, sat=1.2, val=1.2, proc_img=True):#数据增强函数
h, w = input_shape
new_ar = w/h * rand(1-jitter,1+jitter)/rand(1-jitter,1+jitter)
scale = rand(.7, 1.3)
if new_ar < 1:
nh = int(scale*h)
nw = int(nh*new_ar)
else:
nw = int(scale*w)
nh = int(nw/new_ar)
image = image.resize((nw,nh), Image.BICUBIC)
# place image
dx = int(rand(0, w-nw))
dy = int(rand(0, h-nh))
new_image = Image.new('RGB', (w,h), (0,0,0))
new_image.paste(image, (dx, dy))
image = new_image
# flip image or not
flip = rand()<.5
if flip: image = image.transpose(Image.FLIP_LEFT_RIGHT)
# distort image
hue = rand(-hue, hue)
sat = rand(1, sat) if rand()<.5 else 1/rand(1, sat)
val = rand(1, val) if rand()<.5 else 1/rand(1, val)
x = rgb_to_hsv(np.array(image)/255.)
x[..., 0] += hue
x[..., 0][x[..., 0]>1] -= 1
x[..., 0][x[..., 0]<0] += 1
x[..., 1] *= sat
x[..., 2] *= val
x[x>1] = 1
x[x<0] = 0
image_data = hsv_to_rgb(x)*255 # numpy array, 0 to 1
return image_data
K.set_image_dim_ordering('tf')
BASE_WEIGHT_PATH = ('https://github.com/fchollet/deep-learning-models/releases/download/v0.6/')
HEIGHT=160#mobilnet的输入尺寸
WIDTH=160
NUM_CLASSES=2
def letterbox_image(image,size):
iw,ih=image.size
w,h=size
scale=min(w/iw,h/ih)
nw=int(iw*scale)
nh=int(ih*scale)
image=image.resize((nw,nh),Image.BICUBIC)
new_image=Image.new('RGB',size,(0,0,0))#黑色的底儿
new_image.paste(image,((w-nw)//2,(h-nh)//2))
return new_image
#4.1读取batch个图片数据
def generate_arrays_from_file(lines,batch_size,train):
n=len(lines)
i=0
while 1:
x_train=[]
y_train=[]
for b in range(batch_size):
if i==0:
np.random.shuffle(lines)
name=lines[i].split(';')[0]#一行一行读,每读一个图片i会+1
img=Image.open('./data/image/train'+'/'+name)
if train==True:
img=np.array(get_random_data(img,[HEIGHT,WIDTH]),dtype=np.float64)#对训练数据进行数据增强
else:
img=np.array(letterbox_image(img,[HEIGHT,WIDTH]),dtype=np.float64)#对测试数据加上黑框变成正方形
x_train.append(img)
y_train.append(lines[i].split(';')[1])
i=(i+1)%n
x_train=preprocess_input(np.array(x_train).reshape(-1,HEIGHT,WIDTH,3))#x》》预处理image
y_train=keras.utils.to_categorical(np.array(y_train),num_classes=NUM_CLASSES)#y》》onehot
yield (x_train,y_train)
#4.2训练mobilenet
if __name__=='__main__':
log_dir='./logs/'
with open('./data/train.txt','r') as f:
lines=f.readlines()
num_val=int(len(lines)*0.1)#测试集数据的多少
num_train=len(lines)-num_val#训练集数据的多少
model= get_mobilenet_encoder(HEIGHT,WIDTH,classes=NUM_CLASSES)
model_name='mobilenet_1_0_224_tf_no_top.h5'
weight_path=BASE_WEIGHT_PATH+model_name
weights_path=get_file(model_name,weight_path,cache_subdir='models')
model.load_weights(weights_path,by_name=True)
checkpoint_period1=ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',monitor='acc',save_weights_only=False,save_best_only=True,period=3 )
reduce_lr = ReduceLROnPlateau(monitor='acc', factor=0.5, patience=3, verbose=1 )
early_stopping = EarlyStopping( monitor='val_loss', min_delta=0, patience=10, verbose=1)
model.compile(loss = 'categorical_crossentropy',optimizer = Adam(lr=1e-3), metrics = ['accuracy'])
batch_size=8
model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size, True),
steps_per_epoch=max(1, num_train//batch_size),
validation_data=generate_arrays_from_file(lines[num_train:], batch_size, False),
validation_steps=max(1, num_val//batch_size),
epochs=2,
initial_epoch=0,
callbacks=[checkpoint_period1, reduce_lr])
model.save_weights(log_dir+'middle_one.h5')
5
#5.链接mtcnn与mobilenet进行带口罩检测
def Alignment_1(img,landmark):#人脸对齐
if landmark.shape[0]==68:
x = landmark[36,0] - landmark[45,0]
y = landmark[36,1] - landmark[45,1]
elif landmark.shape[0]==5:
x = landmark[0,0] - landmark[1,0]
y = landmark[0,1] - landmark[1,1]
# 眼睛连线相对于水平线的倾斜角
if x==0:
angle = 0
else:
# 计算它的弧度制
angle = math.atan(y/x)*180/math.pi
center = (img.shape[1]//2, img.shape[0]//2)
RotationMatrix = cv2.getRotationMatrix2D(center, angle, 1)
# 仿射函数
new_img = cv2.warpAffine(img,RotationMatrix,(img.shape[1],img.shape[0]))
RotationMatrix = np.array(RotationMatrix)
new_landmark = []
for i in range(landmark.shape[0]):
pts = []
pts.append(RotationMatrix[0,0]*landmark[i,0]+RotationMatrix[0,1]*landmark[i,1]+RotationMatrix[0,2])
pts.append(RotationMatrix[1,0]*landmark[i,0]+RotationMatrix[1,1]*landmark[i,1]+RotationMatrix[1,2])
new_landmark.append(pts)
new_landmark = np.array(new_landmark)
return new_img, new_landmark
class face_rec():
def __init__(self):
self.mtcnn_model=mtcnn()
self.threshold=[0.5,0.6,0.8]
self.Crop_HEIGHT=160
self.Crop_WIDTH=160
self.classes_path='model_data/classes.txt'
self.NUM_CLASSES=2
self.mask_model=get_mobilenet_encoder(self.Crop_HEIGHT,self.Crop_WIDTH,classes=self.NUM_CLASSES)
self.mask_model.load_weights('./logs/middle_one.h5')
self.class_name=self._get_class()
def _get_class(self):
classes_path=os.path.expanduser(self.classes_path)
with open(classes_path) as f:
class_names=f.readlines()
class_names=[c.strip() for c in class_names]
return class_names
def recognize(self,draw):
height,width,_=np.shape(draw)
draw_rgb=cv2.cvtColor(draw,cv2.COLOR_BGR2RGB)
rectangles=self.mtcnn_model.detectFace(draw_rgb,self.threshold)#mtcnn进行人脸检测
if len(rectangles)==0:
return
rectangles=np.array(rectangles,dtype=np.int32)
rectangles[:,0]=np.clip(rectangles[:,0],0,width)#规定检测到的人脸框不会出图片的大小范围
rectangles[:,1]=np.clip(rectangles[:,1],0,height)
rectangles[:,2]=np.clip(rectangles[:,2],0,width)
rectangles[:,3]=np.clip(rectangles[:,3],0,height)
rectangles_temp=rect2square(np.array(rectangles,dtype=np.int32))#框规定成正方形
rectangles_temp[:,0]=np.clip(rectangles_temp[:,0],0,width)#化成正方形框后也不能出图像的大小范围
rectangles_temp[:,1] = np.clip(rectangles_temp[:,1],0,height)
rectangles_temp[:,2] = np.clip(rectangles_temp[:,2],0,width)
rectangles_temp[:,3] = np.clip(rectangles_temp[:,3],0,height)
classes_all=[]
for rectangle in rectangles_temp:
landmark=(np.reshape(rectangle[5:15],(5,2))-np.array([int(rectangle[0]),int(rectangle[1])]))/(rectangle[3]-rectangle[1])*160#5个关键点在160框的位置的坐标
crop_img=draw_rgb[int(rectangle[1]):int(rectangle[3]), int(rectangle[0]):int(rectangle[2])]#剪下框
crop_img = cv2.resize(crop_img,(self.Crop_HEIGHT,self.Crop_WIDTH))#resize到160框
new_img,_=Alignment_1(crop_img,landmark)#将160图像利用两个眼睛的坐标对齐
new_img=preprocess_input(np.reshape(np.array(new_img,np.float64),[1,self.Crop_HEIGHT,self.Crop_WIDTH,3]))#预处理160,batch=1
classes=self.class_name[np.argmax(self.mask_model.predict(new_img)[0])]
classes_all.append(classes)
rectangles=rectangles[:,0:4]
#画框
for (left, top, right, bottom), c in zip(rectangles,classes_all):
cv2.rectangle(draw, (left, top), (right, bottom), (0, 0, 255), 2)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(draw, c, (left , bottom - 15), font, 0.75, (255, 255, 255), 2)
return draw
6
#6主函数调用
if __name__=='__main__':
out=face_rec()
video_capture=cv2.VideoCapture(0)
while True:
ret,draw=video_capture.read()
out.recognize(draw)
cv2.imshow('video',draw)
if cv2.waitKey(20)&0xFF==ord('q'):
break
video_capture.release()
cv2.destroyAllWindows()