4.人脸检测mtcnn+mobilenet戴口罩分类

2023-11-17

#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()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

4.人脸检测mtcnn+mobilenet戴口罩分类 的相关文章

随机推荐