• 注册
  • Android博客 Android博客 关注:0 内容:1372

    人像抠图 + OpenGL ES 还能这样玩?没想到吧

  • 查看作者
  • 打赏作者
  • 当前位置: 职业司 > Android开发 > Android博客 > 正文
    • Android博客
    • OpenGL ES 利用抠图算法实现人像留色

      人像抠图 + OpenGL ES 还能这样玩?没想到吧

      人像抠图 + OpenGL ES 还能这样玩?没想到吧

      人像留色的原理

      现在人像分割技术就像当初的人脸检测算法一样,称为广泛使用的基础算法。

      今天本文介绍的人像留色其实就是三年前某 AI 巨头利用 video 分割技术展示的应用场景:人体区域保留彩色,人体区域之外灰度化。所以人像留色的关键技术在于高精度高性能的分割算法。

      人像抠图 + OpenGL ES 还能这样玩?没想到吧

      首先利用分割算法获取到人像的 mask 图(灰度图),其中人像区域的灰度值大于 0 ,非人像区域的灰度值等于 0 。在 shader 中,首先对 mask 图采样判断采样点是否位于人像区域,然后分别进行不同的处理。

      获取人像 mask 图

      那么如何获取人像 mask 图?Github 上已经有很多大神开源了相关的分割或者抠图算法。

      这里推荐 3 个比较受欢迎的开源项目

      Multi-Human-Parsing

      人像抠图 + OpenGL ES 还能这样玩?没想到吧

      Multi-Human-Parsing 的优势在于支持多人不同部位的分割,同时支持目标检测和人体部位分割。

      Multi-Human-Parsing 将人群场景图像划分为语义一致的属于身体部位或衣服物品的区域,从而为图像中的每个像素分配一个语义部位标签,以及它所属的身份。

      有很多应用场景,如虚拟现实、视频监控、群体行为分析等。 Multi-Human-Parsing 的分割精度对于人体关键点检测其实够用了。

      项目地址:
      github.com/ZhaoJ9014/M…

      BackgroundMattingV2

      人像抠图 + OpenGL ES 还能这样玩?没想到吧

      大名鼎鼎的 BackgroundMattingV2 算法,这也是本文所使用的抠图算法,主要特点就是实时、高分辨率、高精度的分割(Background Matting),项目免费可商用。

      但是要在移动端落地的话,性能将会是很大的瓶颈,需要进行大量的算法优化,这也是目前大部分 AI 算法面临的问题:如何将 AI 算法落地到低算力平台。

      项目地址:
      github.com/PeterL1n/Ba…

      TensorFlow Lite

      人像抠图 + OpenGL ES 还能这样玩?没想到吧

      TensorFlow Lite 是针对移动端的开源机器学习框架,支持 Android 和 iOS,提供了丰富的算法模型,包括图像分割、目标检测、图像分类、超分等模型。

      其中分割模型支持的场景比较丰富,包括人体、宠物和物体等。

      val labelsArrays = arrayOf(
      "background", "aeroplane", "bicycle", "bird", "boat", "bottle", "bus",
      "car", "cat", "chair", "cow", "dining table", "dog", "horse", "motorbike",
      "person", "potted plant", "sheep", "sofa", "train", "tv"
      )
      复制代码

      TensorFlow Lite 所提供的分割模型,使用 GPU 的话,单张 2K 的图处理时间在几百毫秒,基本上做不了 video 处理,另外分割精度也是 demo 级别的。

      项目地址:
      github.com/tensorflow/…

      人像留色的实现

      人像抠图 + OpenGL ES 还能这样玩?没想到吧

      用于实现人像留色的简单 shader :

      #version 300 es
      precision mediump float;
      in vec2 v_texCoord;
      layout(location = 0) out vec4 outColor;
      uniform sampler2D u_texture0;//rgba
      uniform sampler2D u_texture1;//人像灰度图
      uniform int u_renderType;
      uniform float u_offset;
      void main()
      {
      float gray = texture(u_texture1, v_texCoord).r;//对 mask 进行采样
      vec4 rgba  = texture(u_texture0, v_texCoord);
      if(gray > 0.01) {
      outColor = rgba;
      }
      else
      {
      float Y = 0.299 * rgba.r + 0.587 * rgba.g + 0.114 * rgba.b;//RGB 灰度化
      vec4 grayColor = vec4(vec3(Y), 1.0);
      outColor = mix(grayColor, rgba, u_offset);//混合渐变
      }
      }
      复制代码

      shader 中首先对 mask 采样,然后判断采样点是否位于人像区域(灰度值是否大于 0 ),若采样点位于人像区域外,对颜色进行灰度化。

      另外需要注意 OpenGL 访问的图像内存默认是 4 字节对齐,这样灰度 Mask 图的宽度不是 4 的整数倍的话,会有花屏现象,这里需要取消对齐设置:

      glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
      glActiveTexture(GL_TEXTURE1);
      glBindTexture(GL_TEXTURE_2D, m_GrayTexId);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_GrayImage.width, m_GrayImage.height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, m_GrayImage.ppPlane[0]);
      glBindTexture(GL_TEXTURE_2D, GL_NONE);
      复制代码

      完整实现代码见项目:
      github.com/githubhaoha…

      请登录之后再进行评论

      登录

      手机阅读天地(APP)

      • 微信公众号
      • 微信小程序
      • 安卓APP
      手机浏览,惊喜多多
      匿名树洞,说我想说!
      问答悬赏,VIP可见!
      密码可见,回复可见!
      即时聊天、群聊互动!
      宠物孵化,赠送礼物!
      动态像框,专属头衔!
      挑战/抽奖,金币送不停!
      赶紧体会下,不会让你失望!
    • 实时动态
    • 签到
    • 做任务
    • 发表内容
    • 偏好设置
    • 到底部
    • 帖子间隔 侧栏位置:
    • 还没有账号?点这里立即注册