opencv实现人脸检测,tensorflow利用cnn实现人脸识别,python完成
github地址: https://github.com/wangdxh/tensorflow-learn
基础知识
- 微积分求导,求偏导(线代吴恩达的课程会介绍)
- 吴恩达 机器学习 b站链接
- 台大李宏毅 b站链接
- tensorflow Building Machine Learning Projects with TensorFlow paswd:nasm
获得人脸数据
tensorflow_face_camera.py
def getfacefromcamera(outdir):
createdir(outdir)
camera = cv2.VideoCapture(0)
haar = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
n = 1
while 1:
if (n <= 200):
print('It`s processing %s image.' % n)
# 读帧
success, img = camera.read()
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = haar.detectMultiScale(gray_img, 1.3, 5)
for f_x, f_y, f_w, f_h in faces:
face = img[f_y:f_y+f_h, f_x:f_x+f_w]
face = cv2.resize(face, (IMGSIZE, IMGSIZE))
#could deal with face to train
face = relight(face, random.uniform(0.5, 1.5), random.randint(-50, 50))
cv2.imwrite(os.path.join(outdir, str(n)+'.jpg'), face)
cv2.putText(img, 'haha', (f_x, f_y - 20), cv2.FONT_HERSHEY_SIMPLEX, 1, 255, 2) #显示名字
img = cv2.rectangle(img, (f_x, f_y), (f_x + f_w, f_y + f_h), (255, 0, 0), 2)
n+=1
cv2.imshow('img', img)
key = cv2.waitKey(30) & 0xff
if key == 27:
break
else:
break
camera.release()
cv2.destroyAllWindows()
name = input('please input yourename: ')
getfacefromcamera(os.path.join('./image/trainfaces', name))
- 根据输入的名字在./image/trainfaces目录下面创建子目录,将本次采集的头像保存在该目录之下
- 使用opencv打开摄像头,获取头像
- 检测出人脸的区域,调整一下亮暗度,将图片保存
- 保存200张之后,采集结束
创建cnn网络
具体在tensorflow_face_conv.py
def cnnLayer(classnum):
''' create cnn layer'''
# 第一层
W1 = weightVariable([3, 3, 3, 32]) # 卷积核大小(3,3), 输入通道(3), 输出通道(32)
b1 = biasVariable([32])
conv1 = tf.nn.relu(conv2d(x_data, W1) + b1)
pool1 = maxPool(conv1)
# 减少过拟合,随机让某些权重不更新
drop1 = dropout(pool1, keep_prob_5) # 32 * 32 * 32 多个输入channel 被filter内积掉了
# 第二层
W2 = weightVariable([3, 3, 32, 64])
b2 = biasVariable([64])
conv2 = tf.nn.relu(conv2d(drop1, W2) + b2)
pool2 = maxPool(conv2)
drop2 = dropout(pool2, keep_prob_5) # 64 * 16 * 16
# 第三层
W3 = weightVariable([3, 3, 64, 64])
b3 = biasVariable([64])
conv3 = tf.nn.relu(conv2d(drop2, W3) + b3)
pool3 = maxPool(conv3)
drop3 = dropout(pool3, keep_prob_5) # 64 * 8 * 8
# 全连接层
Wf = weightVariable([8*16*32, 512])
bf = biasVariable([512])
drop3_flat = tf.reshape(drop3, [-1, 8*16*32])
dense = tf.nn.relu(tf.matmul(drop3_flat, Wf) + bf)
dropf = dropout(dense, keep_prob_75)
# 输出层
Wout = weightVariable([512, classnum])
bout = weightVariable([classnum])
#out = tf.matmul(dropf, Wout) + bout
out = tf.add(tf.matmul(dropf, Wout), bout)
return out
使用tf创建3层cnn,3 * 3的filter,输入为rgb所以:
- 第一层的channel是3,图像宽高为64,输出32个filter,maxpooling是缩放一倍
- 第二层的输入为32个channel,宽高是32,输出为64个filter,maxpooling是缩放一倍
- 第三层的输入为64个channel,宽高是16,输出为64个filter,maxpooling是缩放一倍
所以最后输入的图像是8 * 8 * 64,卷积层和全连接层都设置了dropout参数
将输入的8 * 8 * 64的多维度,进行flatten,映射到512个数据上,然后进行softmax,输出到onehot类别上,类别的输入根据采集的人员的个数来确定。
识别人脸分类
tensorflow_face.py
训练神经网络
def getfileandlabel(filedir):
''' get path and host paire and class index to name'''
dictdir = dict([[name, os.path.join(filedir, name)] \
for name in os.listdir(filedir) if os.path.isdir(os.path.join(filedir, name))])
#for (path, dirnames, _) in os.walk(filedir) for dirname in dirnames])
dirnamelist, dirpathlist = dictdir.keys(), dictdir.values()
indexlist = list(range(len(dirnamelist)))
return list(zip(dirpathlist, onehot(indexlist))), dict(zip(indexlist, dirnamelist))
pathlabelpair, indextoname = getfileandlabel('./image/trainfaces')
train_x, train_y = readimage(pathlabelpair)
train_x = train_x.astype(np.float32) / 255.0
myconv.train(train_x, train_y, savepath)
- 将人脸从子目录内读出来,根据不同的人名,分配不同的onehot值,这里是按照遍历的顺序分配序号,然后训练,完成之后会保存checkpoint
- 图像识别之前将像素值转换为0到1的范围
- 需要多次训练的话,把checkpoint下面的上次训练结果删除,代码有个判断,有上一次的训练结果,就不会再训练了
识别图像
def testfromcamera(chkpoint):
camera = cv2.VideoCapture(0)
haar = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
pathlabelpair, indextoname = getfileandlabel('./image/trainfaces')
output = myconv.cnnLayer(len(pathlabelpair))
#predict = tf.equal(tf.argmax(output, 1), tf.argmax(y_data, 1))
predict = output
saver = tf.train.Saver()
with tf.Session() as sess:
#sess.run(tf.global_variables_initializer())
saver.restore(sess, chkpoint)
n = 1
while 1:
if (n <= 20000):
print('It`s processing %s image.' % n)
# 读帧
success, img = camera.read()
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = haar.detectMultiScale(gray_img, 1.3, 5)
for f_x, f_y, f_w, f_h in faces:
face = img[f_y:f_y+f_h, f_x:f_x+f_w]
face = cv2.resize(face, (IMGSIZE, IMGSIZE))
#could deal with face to train
test_x = np.array([face])
test_x = test_x.astype(np.float32) / 255.0
res = sess.run([predict, tf.argmax(output, 1)],\
feed_dict={myconv.x_data: test_x,\
myconv.keep_prob_5:1.0, myconv.keep_prob_75: 1.0})
print(res)
cv2.putText(img, indextoname[res[1][0]], (f_x, f_y - 20), cv2.FONT_HERSHEY_SIMPLEX, 1, 255, 2) #显示名字
img = cv2.rectangle(img, (f_x, f_y), (f_x + f_w, f_y + f_h), (255, 0, 0), 2)
n+=1
cv2.imshow('img', img)
key = cv2.waitKey(30) & 0xff
if key == 27:
break
else:
break
camera.release()
cv2.destroyAllWindows()
- 从训练的结果中恢复训练识别的参数,然后用于新的识别判断
- 打开摄像头,采集到图片之后,进行人脸检测,检测出来之后,进行人脸识别,根据结果对应到人员名字,显示在图片中人脸的上面
遗留问题
weight 和 bias 的初始化好像有些问题,随机初始化会造成在某些情况下cost很大,梯度下不去,导致train结果很差。重新跑一次命中率又搞了,随机这里可以使用truncated_normal再测试测试。
输出的种类数目是根据采集的人数去动态变化,但是没有给陌生人预留class,所以结果肯定在某个采集的人中,区别不出陌生人来,可以在onehot的个数加1,增加一个陌生人类别,再进行测试。