5 min read

短视频局部区域静态化

短视频局部区域静态化

.-..-..-.

潜水高清群,有群友需要将短视频画面中的部分区域静态化,看了一下最简单的处理办法就是找到矩形区域的相关坐标,然后用 ffmpeg 根据坐标值直接替换每一帧矩形区域内的像素点。通俗一点讲就是将一张图片盖在一段视频的某部分区域上,达到部分区域静态化的效果。

简单的,可以通过 ffmpegoverlay 参数达到这一效果。大概是这么写的命令?(我没实际使用过)

ffmpeg -i input -i logo -filter_complex 'overlay=10:main_h-overlay_h-10' output

这样是可以解决一些简单的需求,但是如果想要静态化某一个圆形区域或者不规则区域,使用命令就会变得非常的麻烦了。这个时候想着做一个 GUI 工具来用鼠标勾选某部分区域做静态化,于是 loop-roi 就诞生了。

https://github.com/moeoverflow/loop-roi

PS: loop-roi 这个名字只是一个临时的名字,表示这个项目主要的功能就是做循环视频的区域静态化。实际上以后还做了其他一些处理视频的小项目,可能就会把这些小项目全部合进一个主要的软件项目了。当然这是以后的事情了。


loop-roi 的使用方法很简单,打开一个视频文件(短视频,只有几秒的那种),然后在画面中用鼠标选中需要静态化的区域,如果觉得效果满意导出就行。


实现 loop-roi 并不太复杂,主要有三个步骤:

  1. 加载视频并在界面中循环播放
  2. 捕获鼠标选中的区域的像素点坐标
  3. 计算出所有包含在选中区域内的像素点
  4. 替换视频中每一帧区域内的像素值
  5. 导出结果到视频文件

捕获鼠标点击坐标,这个比较简单,Qt 5 提供了一组鼠标点击事件方法,可以非常方便的做到获取鼠标点击的像素点坐标。

virtual void mouseMoveEvent(QMouseEvent *event)
virtual void mousePressEvent(QMouseEvent *event)
virtual void mouseReleaseEvent(QMouseEvent *event)

计算选中区域内的点的实现稍微有一点复杂,需要用到一个解决 点和多边形的位置问题(Point In Polygon (PIP) Problem) 的算法。

这个算法的主要实现思想是:如果从点 C 出发作一条任意方向的射线,射线将与多边形 P 相交于若干交点 p0, p1, …, pn。交点的个数为奇数时点在多边形内(点在多边形上也算多边形内);反之点在多边形外。

https://en.wikipedia.org/wiki/Point_in_polygon

// based on https://stackoverflow.com/a/15599478
bool LoopRoi::pointInPolygon(QPoint point, QVector<QPoint> polygon) {
  int i, j, nvert = polygon.size();
  if (nvert == 0) return false;
  bool c = false;

  QPoint * points = polygon.data();
  for(i = 0, j = nvert - 1; i < nvert; j = i++) {
    if( ( (points[i].y() >= point.y() ) != (points[j].y() >= point.y()) ) &&
        (point.x() <= (points[j].x() - points[i].x()) * (point.y() - points[i].y()) / (points[j].y() - points[i].y()) + points[i].x())
      )
      c = !c;
  }

  return c;
}

替换像素点,现在我们知道了需要替换的像素点,简单的做法是直接遍历画面中的每一个点,如果这个点是在区域内就替换掉,如果不是就跳过。这里有个小的优化就是先根据区域内的点计算出这些点所在的最小矩形区域,然后再在这个缩小范围的区域内遍历替换。


这个程序对图像像素的操作使用了 OpenCV,所以在打包的时候,需要将 OpenCV 相关的库也一起打包进来。在这个过程中遇到了一个问题就是打包出来的程序包太大了,有 198MB 的。

通过 otool 查看 loop-roi 的动态链接库,并没有发现什么问题。

打开 loop-roi.app/Contents/Frameworks 文件夹之后就找到问题所在了。打包的时候不仅将 OpenCV 的相关库打包进来,还顺带带进来了 ffmpeg 的相关动态链接库。个人猜测可能是由于 brew install opencv@3 ,由 Homebrew 安装的预编译 opencv 包同时依赖 ffmpeg 造成的这种问题。删掉由 Homebrew 安装的 opencv 之后自己手动编译安装一份 opencv 就不会有这种问题了。

另外就是 Qt 的 .pro 配置文件里引入 opencv 的时候,如果只是平时做一些研究的话,可以直接引入全部的 opencv 组件,但是如果是要做一些正式的需要发布的软件,可能就得单独引入一些需要的组件,以减少打包后的软件包大小。

#### import all opencv components ####
QT_CONFIG -= no-pkg-config
CONFIG += link_pkgconfig
PKGCONFIG += opencv

macx {
    PKG_CONFIG = /usr/local/bin/pkg-config
}

# ----------------------------

#### import specific opencv components ####
macx {
    INCLUDEPATH += /usr/local/include
    LIBS += `/usr/local/bin/pkg-config opencv --libs-only-L` -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_videoio
    unix:QMAKE_RPATHDIR += @executable_path/../Frameworks
}


最后,问一下,有谁想要加入 Moeoverflow OpenCV team 吗?(小声)