美文网首页iOS进阶iOS技术资料iOS开发部落
GPUImage详细解析(十一)美颜+人脸识别

GPUImage详细解析(十一)美颜+人脸识别

作者: 落影loyinglin | 来源:发表于2016-12-25 14:07 被阅读6709次

    前言

    一个群友用琨君的美颜录制和讯飞离线人脸识别SDK做了一个demo,功能是录制视频,要求有美颜,并且能识别人脸并放置贴图。但是遇到一个问题:
    录制过程能过进行人脸识别,也有美颜效果;
    但是录制的视频,有美颜效果,但没有贴图;

    在帮忙查找bug的过程中,发现代码写得略复杂,不便于学习。
    于是,抽空把核心代码抽离出来,做成了本次的demo

    效果如下

    正文

    核心逻辑

    demo的流程图如下


    GPUImage详细解析(三)- 实时美颜滤镜的基础上,引入了IFlyFaceDetector类,用GPUImageUIElement来绘制人脸识别后的贴图,并用GPUImageAddBlendFilter把美颜后的图像(GPUImageBeautifyFilter)和贴图(GPUImageUIElement)合并,传给GPUImageMovieWriter写入文件。

    人脸识别相关

    IFlyFaceDetector
    IFlyFaceDetector是讯飞提供的本地人脸检测类,可以人脸检测、视频流检测功能。
    初始化代码如下

        self.faceDetector = [IFlyFaceDetector sharedInstance];
        if(self.faceDetector){
            [self.faceDetector setParameter:@"1" forKey:@"detect"];
            [self.faceDetector setParameter:@"1" forKey:@"align"];
        }
    

    demo会用到IFlyFaceDetector对NSData的识别接口

    
    /**
     *  检测frame视频帧中的人脸
     *
     *  @param frame   视频帧数据
     *  @param width  视频帧图像宽
     *  @param height 视频帧图像高
     *  @param dir    图像的方向
     *
     *  @return json格式人脸数组,没有检测到人脸则返回空
     */
    - (NSString*)trackFrame:(NSData*)frame withWidth:(int)width height:(int)height direction:(int)dir;
    

    CanvasView
    CanvasView是群友提供demo中的绘制贴图类,可以对头部、眼睛、鼻子、嘴巴、面部进行贴图,本demo会用到headMap头部贴图。

    //头部贴图
    @property (nonatomic,strong) UIImage *  headMap;
    //眼睛贴图
    @property (nonatomic,strong) UIImage * eyesMap;
    //鼻子贴图
    @property (nonatomic,strong) UIImage * noseMap;
    //嘴巴贴图
    @property (nonatomic,strong) UIImage * mouthMap;
    //面部贴图
    @property (nonatomic,strong) UIImage * facialTextureMap;
    

    GPUImage相关

    GPUImageAddBlendFilter
    继承类GPUImageTwoInputFilter用于合并两个图像,公式如下:

        float r; // 颜色的红色分量
         if (overlay.r * base.a + base.r * overlay.a >= overlay.a * base.a) {
             r = overlay.a * base.a + overlay.r * (1.0 - base.a) + base.r * (1.0 - overlay.a);
         } else {
             r = overlay.r + base.r;
         }
    

    GPUImageUIElement
    GPUImageUIElement继承GPUImageOutput类,作为响应链的源头。
    通过CoreGraphics把UIView渲染到图像,并通过glTexImage2D绑定到outputFramebuffer指定的纹理,最后通知targets纹理就绪。

    demo中的作用是把CanvasView转成纹理,并传递给GPUImageAddBlendFilter。

    遇到的问题

    1、贴图无法出现在录制的视频中

    启动群友提供的demo,预览正常,录制的视频确实没有贴图;
    检查响应链代码,发现代码的实现存在一个问题:
    预览的帧和写入视频的帧不是相同的,GPUImageUIElement的输出的结果是直接指向合并的filter,合并后的图像直接输给writer写入文件;屏幕的贴图预览效果是因为canvasView直接被addsubview到视图层中。
    怀疑是GPUImageUIElement绘制的纹理的为空。通过检查GPU的纹理,GPUImageUIElement对应纹理id的纹理预览为正常,排除这个问题。
    检查美颜filter的输出,同样正常。
    检查合并filter的输出,发现贴图消失。
    定位到是合并filter的问题,检查着色器代码,正常。
    检查初始化代码,找到问题所在:

    群友把合并的filter的mix=0.0;导致合并的filter只取第一个的图像。

    小结,在查找bug的过程,因为demo较为复杂,花费了较多时间熟悉代码;通过Xcode的工具,可以较快定位大多数GPUImage 的问题。

    2、贴图没有随着脸移动

    测试本demo的过程中,出现过贴图固定住不动的情况。
    通过检查人脸识别的输出结果,确定人脸识别的输出是正常;
    检查canvasView的更新,发现问题:
    canvasView没有更新

    解决方案是把canvasView添加到视图层。
    但不知道是否为[self.viewCanvas setNeedsDisplay];造成的影响。

    总结

    demo在这里,代码较短。
    因为是每帧识别,所以CPU的消耗较高。
    如果是实际应用,可以考虑3~5帧左右做一次人脸识别。
    还有另外一个简单的思路:把输入从摄像头变成视频,对视频进行逐帧人脸识别并吧贴图合并到视频中。

    相关文章

      网友评论

      • 写自己的代码:看君的博客醍醐灌顶
      • 残夜孤鸥:楼主你好,您的链接点进去无法下载,请问是现在设置了下载限制吗?
        落影loyinglin:@残夜孤鸥 git 命令
      • 皮皮蟹pipixie:你好,我用你的demo,把相机输出从AVCaptureSessionPreset640x480改为 AVCaptureSessionPreset1280x720,发现识别不到人脸了,怎么解决。
        黄善军Jackie:这个问题解决了吗?
      • littleDad:你好大神videoCamera设置session为AVCaptureSessionPreset1280x720。 GPUImageView的frame设置_filterView = [[GPUImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.gxWidth, self.view.gxWidth *(1280.0 / 720.0))];........为什么贴图就失效了? 好像科大讯飞连人脸都没有识别出来
      • 佘红响: 这个代理里面拿到的是灰度图, 怎样才能拿到RGBA的图呢
      • Zszen:"群友把合并的filter的mix=0.0;导致合并的filter只取第一个的图像。" 233333333
      • 叫我马小帅:请问一下,怎么设置3~5帧做一次人脸识别
      • 3dcc28a75a5f:CPU使用率太高 有什么好的办法吗
        3dcc28a75a5f:@落影loyinglin 我是根据你的demo逻辑做人脸识别的,但是用的是CIDetector 没有用讯飞的 通过GPUImageUIElement 实时捕捉。CPU 使用率在60% 用了十几秒明显发热。
        落影loyinglin:@bmcc 换个美颜识别SDK,用gpu美颜
      • 翰章哥哥:博主代码非常nice!!感谢~
        我把你的部分代码和库拿出来以后报以下错误,是什么原因?漏掉了什么操作吗?
        [IFLYTEK] face engine nil or resoure not init
        翰章哥哥:@落影lying-in 漏掉了哪里的初始化?怎么弄? 大神,很急很关键!
        落影loyinglin:@Slim_6e46 漏掉了初始化操作
        翰章哥哥:也不是报错,是一直打印这句话,然后识别不出人脸!
      • ab930aaf4440:你好,博主,我在使用这个demo的时候会出现崩溃的情况,提示是Tried to overrelease a framebuffer, did you forget to call -useNextFrameForImageCapture before using -imageFromCurrentFramebuffer?是不是因为没有加锁的缘故?或者要如何处理这情况?请指教,感激不尽
        ab930aaf4440:@落影loyinglin 我这边有截图。。。。
        ab930aaf4440:@落影loyinglin 就是在运行的时候会出现,偶尔会,不知道什么具体原因
        落影loyinglin:@xxf_113 什么情况下会出
      • o0阿拉斯加的狗0o:@落影loyinglin 博主有没有遇到这个问题,如果把这个demo中的self.videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionFront];设置成AVCaptureSessionPresetMedium或者high,皇冠的贴图就不显示了,能解答一下吗?谢谢了
      • akali:你试过带一定透明度的图片吗,会发黑,这种怎么解决,我修改了GPUImageAlphaBlendFilter里面的混合方法,不用mix,结果要贴的图不显示了,这种问题怎么解决
        守护地中海的花:大神 我看不懂这个 /Users/wangpanpan/Library/Developer/Xcode/DerivedData/LearnOpenGLESWithGPUImage-fbomjxftxnsauvgmeulfqhpbwucz/Build/Intermediates/GPUImage.build/Debug-iphonesimulator/Documentation.build/Script-BC552B3A1558C6FC001F3FFA.sh 报错怎么解决:grin:
        akali:@落影loyinglin 找到原因解决了,是写混合的时候写错了
        落影loyinglin:@akali 带透明度的混合是没有问题的。发黑是不是边界的情况?
      • ROB_YONG:请问一下,使用AVAssetWriter写入文件,文件大小一直为0。有这方面的资料可以指点下吗?
        落影loyinglin:@ROB_YONG 你看看权限有了吗,还有报错是什么。
      • 倪灏:博主,你好,不知道你遇到过这样的问题吗?我们需要在setFrameProcessingCompletionBlock回调中做一些其他的处理,但是就导致了帧率严重的丢失,我用了一下异步处理,但是效果任然不是很明显,不知道博主有没有其他的解决方案,或者想法什么的?谢谢了
        倪灏:@落影loyinglin 那,如果我还想做一些复杂的操作,有没有什么替代的方式?我这两天也一直在找解决方法,效果也不明显
        落影loyinglin:@倪灏 setFrameProcessingCompletionBlock里面不适合做太复杂的操作,因为本身是每帧都调用的。异步效果不明显
      • 0c0799024caf: 博主大大: 又要请教你了,可能和这篇文章关系不大 0.0
        _playerItem = [[AVPlayerItem alloc] initWithURL:videoURL]; // 因为要控制视频渲染,所以用的playItem
        _soundPlayer = [[AVPlayer alloc] initWithPlayerItem:_playerItem];
        _movieFile = [[GPUImageMovie alloc] initWithPlayerItem:_playerItem];
        //GPUImageMovie本身不播放音乐的嘛,就又弄了个播放器去播放音乐 ,也是以playItem初始化. 因为后面有需求要换背景音乐, 当我
        if (musicURL) {
        AVPlayerItem *musicItem = [AVPlayerItem playerItemWithURL:musicURL];
        [_soundPlayer replaceCurrentItemWithPlayerItem:musicItem];
        } 这样一换 画面就不动了,是不是因为两个playItem不一样? 刚开始播放背景音乐的播放器和GPUImage用的是同一个item 换了之后就不行了 = = :sob:
        0c0799024caf:@落影loyinglin 知道啦 , 要另外加一个AVAudioPlayer. 谢谢啦
        落影loyinglin:@GiveMeUHeart 看看API的描述
      • VincentHoover:学习了~
      • a3417a899b14:您好,请问我使用GPUImageStillCamera的子类录制视频的时候,还是会有一秒多的黑屏,请问这个怎么解决(我在子类初始化方法里调用过addAudioInputsAndOutputs), 如果我直接使用GPUImageMovieWriter,虽然在创建完movieWriter后直接调用了addAudioInputsAndOutputs,但还是会有一帧的黑屏,请问有什么好的方法解决么 :smile:
        倪灏:@a3417a899b14 我也遇到过这种情况,你先去掉音频试一试,可能是合成后音频比视频稍微长一点点,所以最后合成后第一帧就会黑屏
        a3417a899b14:@落影loyinglin 音频帧也没有丢失,GPUImage也没有警报的Log
        落影loyinglin:@a3417a899b14 看看音频帧
      • 落影loyinglin:GPUImageCamera 采集图像
        添加美颜滤镜
        预览层
        人脸识别sdk
        绘制皇冠
        合并到视频显示

      本文标题:GPUImage详细解析(十一)美颜+人脸识别

      本文链接:https://www.haomeiwen.com/subject/sjukvttx.html