美文网首页timeiOS专题资源__UI专题iOS工程实践
GPUImage详细解析(七)文字水印和动态图像水印

GPUImage详细解析(七)文字水印和动态图像水印

作者: 落影loyinglin | 来源:发表于2016-06-18 22:18 被阅读8113次

回顾

GPUImage源码解析、图片模糊、视频滤镜、视频水印都已经介绍过,这次带来的是给视频添加文字水印、动态图像水印

效果展示

“我是水印”的文字,还有心形气泡组成的水印。


处理中的动态图,上面是进度,下面是文字水印:“我是水印”,动态图像水印:心形气泡。

核心思路

  • 1、UIView上面有UILabel(文字水印)和UIImageView(图片水印),再通过GPUImageUIElement把UIView对象转换成纹理对象,进入响应链;
  • 2、视频文件的图像数据通过GPUImageMovie进入响应链;
  • 3、GPUImageDissolveBlenderFilter合并水印图像和视频,把数据传给响应链的终点GPUImageView以显示到UI和GPUImageMovieWriter以写入临时文件;
  • 4、视频文件的音频数据通过GPUImageMovie传给GPUImageMovieWriter以写入临时文件;
  • 5、最后临时文件通过ALAssetsLibrary写入系统库。


具体细节

1、GPUImageUIElement

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

2、GPUImageOutput和GPUImageFilter

本次demo主要用到了frameProcessingCompletionBlock属性,当GPUImageFilter渲染完纹理后,会调用frameProcessingCompletionBlock回调。

3、响应链解析

  • 1、当GPUImageMovie的纹理就绪时,会通知GPUImageFilter处理图像;
  • 2、GPUImageFilter会调用frameProcessingCompletionBlock回调;
  • 3、GPUImageUIElement在回调中渲染图像,纹理就绪后通知
    GPUImageDissolveBlendFilter;
  • 4、frameProcessingCompletionBlock回调结束后,通知
    GPUImageDissolveBlendFilter纹理就绪;
  • 5、GPUImageDissolveBlendFilter收到两个纹理后开始渲染,纹理就绪后通知GPUImageMovieWriter;
    如图


总结

本篇的内容与上一篇视频水印有类似的地方。GPUImageUIElement是新的知识点,但是如果对CoreGraphics和OpenGL ES熟悉可以秒懂。

附上代码

思考题

思考1:响应链解析中的GPUImageFilter有什么作用?是否可以去掉?
思考2:frameProcessingCompletionBlock里面需要做什么样的操作?为什么?
思考3:能否对图像水印进行复杂的位置变换?

答案

思考1:目的是每帧回调;去掉会导致图像无法显示。
思考2:回调需要调用update操作;因为update只会输出一次纹理信息,只适用于一帧。
思考3:在回调中对UIView进行操作即可;或者使用GPUImageTransformFilter。

相关文章

网友评论

  • 不辣先生:大佬,GitHub源码编译的时候脚本调用错误?
    落影loyinglin:@不辣先生 刚试过,这边正常运行。你检查下工程设置
    不辣先生:@落影loyinglin
    Showing Recent Messages
    /Users/will/Library/Developer/Xcode/DerivedData/LearnOpenGLESWithGPUImage-emtldcpktwiegbasnftesypspzac/Build/Intermediates.noindex/GPUImage.build/Debug-iphoneos/Documentation.build/Script-BC552B3A1558C6FC001F3FFA.sh: line 5: /usr/local/bin/appledoc: No such file or directory
    落影loyinglin:@不辣先生 错误内容是啥
  • a2ac431d9773:放弃简书吧,简书CEO公开支持简书的大V“饱醉豚”侮辱程序员
  • 骑老虎喊救命:你好,用你的这个方法添加完水印,图像会变暗,mix值越大,水印越清晰,但是画面越暗,有什么方法能让画面不变暗吗
    落影loyinglin:@骑老虎喊救命 用add的滤镜 不要用mix
  • 古子林:楼主我最近在写一个需求,用 GPUImageMovie 和 GPUImageMovieWriter 结合对本地视频进行读取--加水印--保存到本地的操作,发现保存的文件是空的。在网上搜的几个实现方法都不行,我也下载了你的demo运行也是不行的。你的demo根本就没有走GPUImageMovieWriter 的 setCompletionBlock 回调。我又尝试设置 GPUImageMovie的代理,发现 GPUImageMovie 并没有走 didCompletePlayingMovie 代理回调。以上用的是你自己的demo,除加了一个GPUImageMovie代理外,没做任何修改。Xcode的版本是9.2
    古子林:@我马上就变成胖子了 https://www.jianshu.com/p/91bafff116be
    我马上就变成胖子了:运行Demo,progress到0.32时候就卡主了,不知道是为什么。
    我马上就变成胖子了:兄弟,我也遇到了和你一样的问题,你有解决吗?
  • littleDad:这里的gpuimagemovie加载视频后,播放出来的不带声音 。 有好的处理方法嘛
    littleDad:@落影loyinglin 大神别走, 这个 播放已经成功了。 我想知道在录制的时候 加入背景音乐导出 如何实现
    落影loyinglin:@Little_Dad 用avplayer audioQueue audioUnit播放声音
  • 张芳涛:请问怎样添加GIF格式水印呢?
    落影loyinglin:@张芳涛 找一个gif解析成图像帧的方法,网上有。然后就是图像水印了
  • 机械师shijier:请教一下,我自己录的视频,加完水印,视频就逆向旋转了90度。不知大神遇到了没有。帮忙分析下是什么原因?
    心痛的独白:@机械师shijier 旋转90度解决了吗 我的也是咧
    机械师shijier:@落影loyinglin 就加了Tutorial-07 Demo中的那个水印动画。然后就找不到原因了
    落影loyinglin:@机械师shijier 你看看是不是有哪个滤镜设置了转置的矩阵
  • Lucifron:很好的文章,很是受益。
  • _海阔天空:你好,实时录像添加多个滤镜和图片水印,第一帧有图片水印,没滤镜效果,后面就只有滤镜效果没水印,能帮我看看是什么问题吗
  • ChineseBoy:大哥有Android版本的介绍没有咯
    Zszen:@ChineseBoy github上貌似有安卓版sdk
    ChineseBoy: @ChineseBoy 好吧
    落影loyinglin:@ChineseBoy 安卓的没有,可以仿照滤镜链实现一遍。
    glsl可以公用。
  • 刺客小生110:请问,我给静态的图添加水印一个lable的水印然后update element之后水印没有变化是怎么回事
  • 共田君:这个水印比视频更暗是为什么,怎么调整呢
    6310ffb20d0c:@Zszen 能在不变暗的情况下加吗
    Zszen:@共田君 打开photoshop,研究蒙板的作用
  • c58904600e17:实时视频可以加水印嘛吗
  • angBiu::sob: 也是啊。。用imageview 添加的图片都会比原图模糊,像GPUImage作者提问了 但是没回复。。╮(╯▽╰)╭
    angBiu:@落影loyinglin 恩是的呢~
    落影loyinglin:@angBiu 图片显示的大小是不是原图大小
  • wo不懂:__unsafe_unretained GPUImageUIElement *weakOverlay = uielement;

    runAsynchronouslyOnVideoProcessingQueue(^{
    [weakOverlay update];
    });
    这样写好后水印贴上去了视频奔溃在这里
    void runSynchronouslyOnVideoProcessingQueue(void (^block)(void))
    {
    dispatch_queue_t videoProcessingQueue = [GPUImageContext sharedContextQueue];
    #if !OS_OBJECT_USE_OBJC
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wdeprecated-declarations"
    if (dispatch_get_current_queue() == videoProcessingQueue)
    #pragma clang diagnostic pop
    #else
    if (dispatch_get_specific([GPUImageContext contextKey]))
    #endif
    {
    block();
    }
    else
    {
    dispatch_sync(videoProcessingQueue, block); //这个地方奔溃
    }
    }
    不知所以
  • CCloud:您好,demo的视频timescale 是600,都是正常的,为什么,我换别的视频timescale 是30000,在真机上会出错误,progress 0%的时候就出错*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Incomplete filter FBO: 36055’. 在模拟器上
    CCloud:发现好像是因为视频上带有透明通道的原因
  • Sias_Orange:请问楼主 如何添加多滤镜效果
  • Sias_Orange:楼主 你的Demo里的水印 也不是很清楚啊 有什么方法 可以解决吗?
  • Sias_Orange:为什么我加的label水印 感觉模糊 有锯齿
  • 5340f79ae3f2:@落影loyinglin 我用GPUImageUIElement加载一张View,因为需求我需要一帧对这个View旋转一定的角度,所以我的代码时这样的:
    if (size.width > 0 && size.height > 0)
    {
    _self.capImageView.frame = CGRectMake(rect.origin.x + (rect.size.width - size.width)/2, rect.origin.y - size.height, 160, 160);
    _self.capImageView.layer.transform = CATransform3DMakeRotation(-M_PI*(strongSelf.faceAnagle/180), 0, 0, 1);

    }
    [weakUIElementInput updateWithTimestamp:time];
    但是View确实发生想要的偏转但是View的宽高却发生了改变,这个应该咋办捏
  • 慧丫丫:视频贴图(gif)也是这种方式?gpuinageuielement?
    落影loyinglin:@慧丫丫 你可以发我一个demo 我记得不难。做过
    慧丫丫:@落影loyinglin 大概一个思路:加载gif图为一个集合,视频读成图片放一个集合,然后来图片合成视频的某一时间段,但这个相当耗内存。暂时没有找到其他办法了。如果你有好的方式交流一下。
    落影loyinglin:@慧丫丫 动态图 要。自己写切换的模式
  • e1d94c4636d7:屌炸天
  • 8afce4bcf1e6:我现在做直播也是用这种方法加的水印,但是这样直播方也可以看到水印。怎么让水印不显示,但还能推出去呢?
    落影loyinglin:@学无止境20152015 你不把水印添加到预览层
  • 0c0799024caf:博主,添加水印时遇到点问题,请教一下。
    _movieFile = [[GPUImageMovie alloc] initWithURL:videoURL];
    [_movieFile addTarget:_filterGroup]; // _filterGroup 里面有一些其他的滤镜

    GPUImageUIElement *uiElement = [[GPUImageUIElement alloc] initWithView:testLabel];
    GPUImageAlphaBlendFilter *blendFilter = [[GPUImageAlphaBlendFilter alloc] init];
    blendFilter.mix = 1.0f;

    [uiElement addTarget:blendFilter];
    [_filterGroup addTarget:blendFilter];
    [blendFilter addTarget:_movieWriter]; // _movieWriter 是往文件里写的实例

    还有些其他代码没贴 现在出来的视频总是空白的 但是把加ui元素那块代码去掉,滤镜组里的滤镜效果就出来了
    0c0799024caf:@落影loyinglin 好了,出来了,要update ,还有我没把uielement 引用起来,生命周期的问题……:sob:
    0c0799024caf:@落影loyinglin 改了还是白的,我感觉我少了几步,如果不需要动画,只是单纯显示一个label而已,需要 在setFrameCompletionBlock 里update吗
    落影loyinglin:@GiveMeUHeart mix改成0.5
  • 8afce4bcf1e6:是不只有用GPUImageUIElement加水印才清晰,加在CALayer上水印图片会变得有点模糊。。。
    8afce4bcf1e6:@落影loyinglin 那请问加带图片的字幕,用哪种方法好呢?
    落影loyinglin:@学无止境20152015 没有尝试过CALayer的水印。这个demo的水印质量还可以。
  • 43f75b1bd847:请问实时水印的文本为什么会这么模糊或者说没有正常UIView显示出来的清晰,按道理GPU效率更高呢?或者说有没有更简单的纹理,只是叠加文本让文本高亮显示或部分区域透明的图片?期待大神回复 :smile:
    0fb0420863cc:在官方demo里面我尝试给label添加了外层的view发现也会出现水印label色差模糊的问题 所以我的处理方法是把view生成image保证单个层级,效果还不错
    43f75b1bd847:好的,我试试,非常感谢!
    落影loyinglin:@dragonview 模糊可能是你没有调整好大小比例,导致水印纹理拉伸或者压缩。
  • 小黄人写代码:你好,我试着在你的demo上面添加一个带有.gif的ImageView,但是那个ImageView却变成静态的,不动了。请问要怎么解决?
    小黄人写代码:@落影loyinglin 我也试过用UIWebView去加载gif的。但是那个UIWebView是空白的,加载不出数据。
    小黄人写代码:@落影lying-in 好的。谢谢。
    落影loyinglin:@小黄人写代码 根据时间改变图片的帧
  • Dismon:请问,我用GPUImage 录制视频,然后添加实时的水印,但是发现添加上去的水印好模糊,还有锯齿,这是什么原因呢
    a650f4eccd9f:@Dismon 麻烦问一下怎么实现在录制视频实时加水印,能给个简单的demo吗?
    北冥风尘:@Dismon 水印怎么添加上去啊, :pray:
    落影loyinglin:@Dismon 我这边测试都可以接受。你用的什么测试环境
  • 北冥风尘:录制视频的时候添加水印,这个处理是怎样的呢?
    落影loyinglin:@北冥风尘 GPUImageUIElement 你看看这个。
    北冥风尘:@落影loyinglin 我想要视频上方一层view,是动态的,录制视频的时候,需要将上面一层动态的view也添加进去,也是这个原理吗?
    落影loyinglin:@北冥风尘 你可以选择用gpuimage提供的摄像头 ,可以直接添加水印。也可以先用自己的方法录制完视频后自己添加水印
  • 3e45403769fd:如果只想视频的某一小段添加水印,要怎么样呢?麻烦回复,谢谢
    落影loyinglin:@qinxx CMTime 对应的是帧的时间戳。
    3e45403769fd:@落影loyinglin 请问是哪里的CMTime,能不能具体一些,谢谢了
    落影loyinglin:@qinxx 根据cmtime 决定是否添加水印
  • 暴走大牙: // 播放
    NSURL *sampleURL = [[NSBundle mainBundle] URLForResource:@"abc" withExtension:@"mp4"];
    AVAsset *asset = [AVAsset assetWithURL:sampleURL];
    CGSize size = self.view.bounds.size;
    movieFile = [[GPUImageMovie alloc] initWithAsset:asset];
    movieFile.runBenchmark = YES;
    movieFile.playAtActualSpeed = YES;

    // 水印
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    label.text = @"我是水印";
    label.font = [UIFont systemFontOfSize:30];
    label.textColor = [UIColor redColor];
    [label sizeToFit];
    UIImage *image = [UIImage imageNamed:@"watermark.png"];
    UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
    UIView *subView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, size.width, size.height)];
    subView.backgroundColor = [UIColor clearColor];
    imageView.center = CGPointMake(subView.bounds.size.width / 2, subView.bounds.size.height / 2);
    [subView addSubview:imageView];
    [subView addSubview:label];

    GPUImageUIElement *uielement = [[GPUImageUIElement alloc] initWithView:subView];

    这里,GPUImage的 blend filter 有局限性,例如你DEMO的代码,水印subView size 跟视频的size是一样,,那如果size不一样而且宽高比也不一样,就会出问题了。
    GPUImage的 需要高级点的 blend filter,,干脆叫 MCU filter,哈哈,可以支持多个输入画面布局变动
    狂奔的兔子:@暴走大牙 只能拉伸到一样的尺寸,不能指定位置和大小,不知道有支持这种方式的修改,用起来确实不方便
  • 暴走大牙:一开始我也是在跟你这个做法一样
    2、GPUImageFilter会调用frameProcessingCompletionBlock回调;
    3、GPUImageUIElement在回调中渲染图像,纹理就绪后通知
    GPUImageDissolveBlendFilter;

    // __unsafe_unretained GPUImageUIElement *weakOverlay = overlay;
    // [dummyFilter setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime){
    // [weakOverlay update];
    // }];

    但是这样会导致CPU变高,另外考虑到水印(可能是静态图像可能是动态的gif),摄像头输出是的帧率比较高30+,所以setFrameProcessingCompletionBlock会被回调很多次,后来我修改注释掉,而是水印需要更新的时候才去update
    runAsynchronouslyOnVideoProcessingQueue(^{
    [overlay update];
    });

    狂奔的兔子:@暴走大牙 我在现有的代码里面添加了[filter disableSecondFrameCheck]; 和[outputFramebuffer disableReferenceCounting];水印就无法更新了,只能显示最开始一个画面,
    暴走大牙:@落影loyinglin 水印overlay作为第二个输入端,所以增加
    [blendFilter disableSecondFrameCheck];就可以,这样不检查就一直用上一次的画面。
    另外在外面update 有个坑,见:https://github.com/BradLarson/GPUImage/issues/2211
    后面回复我有贴到代码
    落影loyinglin:@暴走大牙 你修改后,水印不更新,就只输出一次纹理,而视频每帧都会输出,GPUImageDissolveBlendFilter必须受到两帧才会进行处理,你是如何解决的的?
  • 22e9827bf641:vv
    22e9827bf641: @落影loyinglin very good
    落影loyinglin:@vv暮 ??
  • 萧城x:也什么不用那个disk play 那个 60一秒
    落影loyinglin:@低调做事 为了保证输出频率和响应链一致,响应链的频率和帧率不一定一致

本文标题:GPUImage详细解析(七)文字水印和动态图像水印

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