在 VapourSynth 中使用 OpenCV Python - WolframRhodium/muvsfunc GitHub Wiki

在 VapourSynth 中使用 OpenCV-Python

本教程讲述 OpenCV-Python 在 VapourSynth 中的安装与使用。

通过 pip 安装

安装部分针对 Windows 平台,不过其他平台上的安装方法应该是类似的。

  1. 安装 pip (如果你正常安装了 Python,它应该也被安装了) 并确保它能正确工作 (例如, 在 shell 中输入 pip -V 以检查 pip 的版本)。

  2. 编译 NumPy 和 OpenCV-Python, 或者从 NumPyOpenCV-Python 下载预编译的包。

  3. 在 shell 中输入 pip install *.whl 安装两个库。

  4. 在 Python 终端中输入

import numpy as np
print(np.__version__)

import cv2
print(cv2.__version__)

检查安装情况。

在 VapourSynth 中使用 OpenCV-Python

因为 OpenCV-Python 中的函数只作用于 NumPy 的数组,所以它们不能直接通过 clip = cv2.GaussianBlur(clip) 的形式来直接调用。 我已经写了支持脚本 muvsfunc_numpy.py 以简化调用过程。

为了让 OpenCV-Python 中的函数能在 VapourSynth 下使用,你只需要定义一个输入一个或多个 NumPy 数组、并输出一个 NumPy 数组的函数(流水线),正如下面的例子所展示的那样。

值得一提的是,不同的 OpenCV-Python 的函数对于输入的格式有不同的限制。使用前你最好在 OpenCV 官方文档 中确认这些限制。

在以下样例中,我假定已通过以下代码导入库

import numpy as np
import cv2
import muvsfunc_numpy as mufnp

在灰度图上的高斯模糊

纯 VapourSynth 版:

sigma = 1.0
output = core.tcanny.TCanny(gray, sigma=sigma, mode=-1)

OpenCV-Python 版:

sigma = 1.0
ksize = (round(sigma*3)*2+1, round(sigma*3)*2+1)
gaussian_core = lambda img: cv2.GaussianBlur(img, ksize=ksize, sigmaX=sigma)
output = mufnp.numpy_process(gray, gaussian_core)

我喜欢在函数名的后面加上 _core 来表示它的输入输出是 NumPy 中的数据。

使用 Domain Transform 增强 RGB 图像

据我所知,在 VapourSynth 上还没有 DT 这个函数的实现。 因此,有着大量函数的 OpenCV-Python 极大地增加了滤镜的选择。

OpenCV-Python 版:

# enhance_core = lambda img: cv2.detailEnhance(img)
enhace_core = cv2.detailEnhance # 使用别名
output = mufnp.numpy_process(rgb, gaussian_core, input_per_plane=False, output_per_plane=False)
# 因为我们同时处理三个平面,所以 ***_per_plane=False 是必要的

Canny 边缘检测算子

同属于 OpenCV-Python 的滤镜可以被放在单独的一个函数中。这提供了使用复杂流水线的方法,同时也可能提升性能。

纯 VapourSynth 版:

sigma = 1.0
output = core.tcanny.TCanny(gray, sigma=sigma, mode=0)

OpenCV-Python 版:

# 现在我们显式定义一个函数,而不是使用匿名函数来定义
def canny_core(img):
    blur = cv2.GaussianBlur(img, ksize=(7, 7), sigmaX=1)
    edge = cv2.Canny(blur, threshold1=1.0, threshold2=8.0)
    return edge

output = mufnp.numpy_process(gray, canny_core)

(两者的输出不同。我不知道也不关心为什么会这样。)

Resize

纯 VapourSynth 版:

output = core.resize.Bicubic(gray, 1280, 720)

对于 OpenCV-Python 中的 resize 等改变输出的格式的操作,需要对 mufnp.numpy_process 的输入进行特殊处理,就像下面所展示的那样,因为 VapourSynth 的 std.ModifyFrame 要求输入输出的格式必须相同。

OpenCV-Python v1版:

def resize_core1(blank, img):
    return cv2.resize(img, dsize=(blank.shape[1], blank.shape[0]), interpolation=cv2.INTER_CUBIC)

blank_clip = core.std.BlankClip(gray, width=1280, height=720)
output = mufnp.numpy_process([blank_clip, gray], resize_core1)

OpenCV-Python v2版:

def resize_core2(img, w, h):
    return cv2.resize(img, dsize=(w, h), interpolation=cv2.INTER_CUBIC)

blank_clip = core.std.BlankClip(gray, width=1280, height=720)
output = mufnp.numpy_process([blank_clip, gray], resize_core2, w=1280, h=720, omit_first_clip=True)

(再一次,两者的输出不同。)

铅笔画化

OpenCV-Python 版(灰度输出):

pencil_core1 = lambda img: cv2.pencilSketch(img)[0]
blank_clip = core.std.BlankClip(rgb, format=vs.GRAY8)
output = mufnp.numpy_process([blank_clip, rgb], pencil_core1, input_per_plane=False, omit_first_clip=True)

OpenCV-Python 版(彩色输出)

pencil_core2 = lambda img: cv2.pencilSketch(img)[1]
output = mufnp.numpy_process(rgb, pencil_core2, input_per_plane=False, output_per_plane=False)

引导滤波

OpenCV-Python 版(使用灰度图引导 RGB 上的处理):

gf_core = lambda img, guide: cv2.ximgproc.guidedFilter(guide, img, radius=4, eps=100)
output = mufnp.numpy_process([rgb, gray], gf_core, input_per_plane=False, output_per_plane=False)

OpenCV-Python 版(使用 RGB 引导 RGB 上的处理):

gf_core = lambda img, guide: cv2.ximgproc.guidedFilter(guide, img, radius=4, eps=100)
output = mufnp.numpy_process([rgb, rgb], gf_core, input_per_plane=False, output_per_plane=False)

图像修复

OpenCV-Python 版:

def inpaint_core(img, mask, radius=1, flags=cv2.INPAINT_NS):
    return cv2.inpaint(img, mask, inpaintRadius=radius, flags=flags)

inpainting = mufnp.numpy_process([gray, mask], inpaint_core, radius=1, flags=cv2.INPAINT_NS)
inpainting = core.std.MaskedMerge(gray, inpainting, mask)

限制

  1. 现在时域的函数不能正常工作。具体来说,我不知道怎么在 std.ModifyFrame 这个支持函数中使用它们。