全套 | 人脸检测 & 人脸关键点检测 & 人脸卡通化

2020-08-28 16:23:25 浏览数 (1)

重磅干货,第一时间送达

来源:CVPy

人脸检测历险记

可能跟我一样,人脸检测是很多人学习图像处理的第一个自驱动型的任务,OpenCV刚上手没几天可能就想先跑一跑人脸检测,然后一个坑接着一个坑的往里跳。我个人对人脸检测的大概历程应该是下面这样的:

  1. 找一个直接能执行的OpenCV的人脸检测程序,直接执行看效果。虽然这貌似是最简单的一步,但是由于最初水平实在太低,所以这一步可能是耗时最长的。当时初学的时候还在用C ,想要直接跑程序,首先你要先配置好环境!曾经因为环境问题尝试了不同版本OpenCV未果、重装VS未果、重装Windows系统仍未果...当年的环境配置简直是不堪回首,说多了都是泪。而且根据我的经验,不论是因为版本还是还是系统问题,在网上找一个直接拿来就能跑的代码实在是太难了...
  2. 按照千辛万苦跑起来的程序,自己敲一遍。明明感觉没啥差别,为啥就是跑不起来呢...所有的坑都会自己跳进去,所有的低级错误都会犯不止一遍。
  3. 尝试找数据或者标注数据,尝试自己训练一个人脸检测模型,OpenCV提供了用来训练的exe,参数超多,一直感觉很难用...当时可谓是屡败屡战,屡战屡败。
  4. 找一个能直接执行的深度学习的人脸检测程序,直接执行看效果。(虽然这貌似是最简单的一步,但是由于最初水平实在太低,所以这一步可能是耗时最长的。当时初学的时候还在用Caffe,想要直接跑程序,首先你要先配置好环境!Caffe的环境配置...当年的环境配置简直是不堪回首,说多了都是泪。???好像在哪里听过?)
  5. 同2.
  6. 尝试找数据或者标注数据,尝试自己训练一个人脸检测模型。Caffe...对,我当时用的是TensorFlow Object Detection API(当时这个环境配置过程也很感人)。
  7. 找一个能直接能用的人脸检测模型,直接跑起来看看效果....如果不能跑,尝试改改,也许就该好了呢。
  8. 跑起来一个人脸检测模型,加点其他效果?

要不就先看看效果吧,毕竟看到效果才更有动力走下去。【视频有声提示!】

上面用的是深度学习模型的人脸检测,但是在此之前还是稍微回顾下OpenCV自带的人脸检测器。

OpenCV自带的人脸检测

OpenCV自带了基于级联分类器的人脸检测模型,只能检测正脸,在前深度学习时代,效果已经是很好的了。OpenCV中的使用方式是用级联分类器加载人脸检测模型,大家一般默认用haarcascade_frontalface_default.xml。这个文件在C 源码路径下很容易找到,大概在opencv/sources/data/haarcascades路径下,Python的话,我用的Anaconda,所以路径在/Anaconda3/Lib/site-packages/cv2/data/。不同版本想来差异不会太大。

然后用多尺度滑窗法去检测人脸,函数参数含义和代码示例如下:

直接【执行】看效果:【视频有声提示!】

基于深度学习的人脸检测

想要深入学习的小伙伴可以尝试自己训练一个人脸检测模型练手,这里直接在Github上找一个能跑的模型CenterFace。代码和模型都来自这里https://github.com/Star-Clouds/CenterFace。由于模型是ONNX格式的,所以用OpenCVreadNetFromONNX函数加载模型centerface.onnx

摘取主体代码并注释如下:

代码语言:javascript复制
class CenterFace(object):
    def __init__(self):
        # readNetFromONNX 加载模型 
        self.net = cv2.dnn.readNetFromONNX(os.path.join(model_path, 'centerface.onnx'))
        self.img_h_new, self.img_w_new, self.scale_h, self.scale_w = 0, 0, 0, 0

    def __call__(self, img, height, width, threshold=0.5, landmarks=True):
        # 圆整图片大小为32的倍数
        self.img_h_new, self.img_w_new, self.scale_h, self.scale_w = self.transform(height, width)
        # 推理,即前向传播一次
        return self.inference_opencv(img, threshold, landmarks)

    def inference_opencv(self, img, threshold, landmarks=True):
        # 图像预处理,这里主要是 Reisze 和 BGR2RGB 
        blob = cv2.dnn.blobFromImage(img, scalefactor=1.0, size=(self.img_w_new, self.img_h_new), mean=(0, 0, 0), swapRB=True, crop=False)
        self.net.setInput(blob)
        begin = time.time()
        # 前向传播一次
        heatmap, scale, offset, lms = self.net.forward(["537", "538", "539", '540'])
        end = time.time()
        print("dtime = ", end - begin)
        # 后处理,主要是根据阈值 threshold 从输出获取人脸框和人脸关键点的位置
        return self.postprocess(heatmap, lms, offset, scale, threshold, landmarks)

    def transform(self, h, w):
        # 圆整图片大小为32的倍数
        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 / w
        return img_h_new, img_w_new, scale_h, scale_w

这一步模型可以同时给出人脸框和人脸关键点的位置。【视频有声提示!】

人脸卡通化

仅仅是人脸检测,显得略微有些没意思,所以在人脸检测的基础上,加点其他的更有意思的东西,比如上次刚玩过的卡通化。这是国内大佬的一篇文章,开源了代码和模型,在这里:https://github.com/SystemErrorWang/White-box-Cartoonization/。代码细节可以到这个链接查看,这里只给出人脸检测之后和卡通化结合部分的代码:

代码语言:javascript复制
    def face_detect(self, image, landmarks: bool, cartoon: bool):
        # 加载模型
        centerface = CenterFace()
        h, w = image.shape[:2]
        # 人脸检测
        dets, lms = centerface(image, h, w, threshold=0.5, landmarks=landmarks)
        print("检测到的人脸:", dets)
        if len(dets):
            for det in dets:
                boxes, score = det[:4], det[4]
                x1, y1, x2, y2 = int(boxes[0]), int(boxes[1]), int(boxes[2]), int(boxes[3])
                box_w, box_h = x2 - x1, y2 - y1
                # 人脸框稍微往外扩一点,个人感觉这么会好一点
                face_box = image[max(0, y1 - int(box_h * 0.1)): min(y2   int(box_h * 0.1), h), max(
                        0, x1 - int(box_w * 0.1)):min(x2   int(box_w * 0.1), w)]
                if cartoon:
                    # 人脸卡通化之后
                    cartoonized_face = cartoonize(face_box)
                    # 贴回原图,覆盖原图人脸部分
                    image[max(0, y1 - int(box_h * 0.1)): min(y2   int(box_h * 0.1), h), max(
                            0, x1 - int(box_w * 0.1)):min(x2   int(box_w * 0.1), w)] = cartoonized_face
      # 画出人脸框框
                draw_box(image, [x1, y1, x2, y2], color=(0, 255, 255))
                # cv2.rectangle(image, (x1, y1), (x2, y2), (0, 255, 255), min(h, w) // 200)

            if landmarks:
                for lm in lms:
                    for i in range(0, 5):
                        cv2.circle(image, (int(lm[i * 2]), int(lm[i * 2   1])),
                                   min(h, w) // 100, (0, 0, 255), -1)
        else:
            cv2.putText(image, "No Face?", (int(w * 0.1), int(h * 0.1)), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), min(h, w) // 200)
        return image

流程如下:

  1. 先加载CenterFace的模型(参考上一节的CenterFace__init__
  2. 进行人脸检测,获取人脸框和人脸关键点的位置
  3. 稍微扩充下人脸框,进行卡通化操作
  4. 把卡通化后的人脸贴回原图中人脸的位置

完整效果

看一下完整的效果吧:【视频有声提示!】

结语-玩一下

想要自己尝试实现的小伙伴,可以按照我给的链接找到源码位置自己跑跑试试。如果想要立刻就体验一下效果的话,想必你已经看出来了,这个人脸检测 & 卡通化已经被我做成了一个体验网页了。手机和电脑均可访问。

  • 电脑:http://cvpy.net/studio/cv/func/DeepLearning/face/face_detection/page/
  • 手机:扫码进入体验,觉得不错的话,可以给个在看哦。

0 人点赞