介绍
OpenCV中自带两个提取光流的函数:
cv2.calcOpticalFlowPyrLK()
cv2.calcOpticalFlowFarneback()
其中calcOpticalFlowPyrLK函数是提取稀疏光流的函数,而本文主要关注calcOpticalFlowFarneback函数,并将它应用在一个真实的问题中
真实问题是:
给定两张图像,一张是带形变矫正的图像(unwraped_img.png),一张是参照图像(reference_img.png)。目的是提取这两张图像之间的光流,并利用提取的光流对带形变矫正的图像进行矫正,得到矫正过后的图像(warped_img.png),矫正过后的图像更可能与参考图像对齐,即没有形变误差。
calcOpticalFlowFarneback
为了本文的完整,这里搬抄了知乎博客cv2.calcOpticalFlowFarneback函数,来对calcOpticalFlowFarneback函数进行简单的介绍
调用方式
flow = cv2.calcOpticalFlowFarneback(prevImg,
nextImg,
None,
pyr_scale,
levels,
winsize,
iterations,
poly_n,
poly_sigma,
flags)
参数解释
要注意第三个参数None,这是根据OpenCV版本决定的,有的需要这个参数,有的不需要这个参数,这需要根据报的错来判断
比如我的版本:Python=3.8.8,OpenCV=4.5.5
如果不带None这个参数,就会报一下错误:
Traceback (most recent call last):
File ".\singleImage.py", line 15, in <module>
flows = cv2.calcOpticalFlowFarneback(reference_img, unwraped_img,
cv2.error: OpenCV(4.5.5) :-1: error: (-5:Bad argument) in function 'calcOpticalFlowFarneback'
> Overload resolution failed:
> - calcOpticalFlowFarneback() missing required argument 'flow' (pos 3)
> - calcOpticalFlowFarneback() missing required argument 'flow' (pos 3)
根据报错的信息我们可以发现,这个参数其实就是flow参数,如果版本需要这个参数传入None就可以解决
CSDN博客OpenCV Python calcOpticalFlowFarneback也提出了这个问题
其他参数解释
prevImg:前一帧8-bit单通道图像,或者是参考图像
nextImg:当前帧图像,与前一帧保持同样的格式、尺寸,即期望与参考图像一致的待矫正图像
pyr_scale:金字塔上下两层之间的尺度关系,该参数一般设置为pyrScale=0.5,表示图像金字塔上一层是下一层的2倍降采样
levels:图像金字塔的层数
winsize:均值窗口大小,winsize越大,算法对图像噪声越鲁棒,并且能提升对快速运动目标的检测效果,但也会引起运动区域模糊。个人经验:相较于其他参数,这是一个更加重要的参数,如果设置得不合理将极大地影响提取的光流正确性,建议从小到大多调调这个参数
iterations:算法在图像金字塔每层的迭代次数
poly_n:用于在每个像素点处计算多项式展开的相邻像素点的个数。poly_n越大,图像的近似逼近越光滑,算法鲁棒性更好,也会带来更多的运动区域模糊。通常,poly_n=5 or 7
poly_sigma:标准差,poly_n=5时,poly_sigma = 1.1;poly_n=7时,poly_sigma = 1.5
flags:有两种选择:1)cv2.OPTFLOW_USE_INITIAL_FLOW,使用输入流量作为初始流量近似。2)cv2.OPTFLOW_FARNEBACK_GAUSSIAN,使用高斯过滤器,而不是相同尺寸的盒形滤波器。通常,这种选择比箱形过滤器提供z更准确的流量,但代价是速度较低。通常,一个高斯窗口的winsize应该设置为一个更大的值,以达到相同的鲁棒性水平。
完整代码
注意:flow_to_image函数可以从CSDN博客python 可视化光流中获取 image_warp函数可以从CSDN博客一文搞懂光流 光流的生成,可视化以及映射(warp)
import cv2
import numpy as np
from PIL import Image
from utils.flow_show import flow_to_image
from utils.image_warp_numpy import image_warp
unwraped_img = np.asarray(Image.open('./unwraped_img.png'))
reference_img = np.asarray(Image.open('./reference_img.png'))
####### estimate flows #######
# 注意第三个参数None
# 这个参数得根据OpenCV的版本来确定是否使用
# 我这里使用的是4.5.5版本
flows = cv2.calcOpticalFlowFarneback(reference_img,
unwraped_img,
None,
pyr_scale=0.5,
levels=3,
winsize=55,
iterations=3,
poly_n=7, # 5, 7
poly_sigma=1.5, # 1.1, 1.5
flags=cv2.OPTFLOW_FARNEBACK_GAUSSIAN)
# show flows
flow_img = flow_to_image(flows)
cv2.imwrite('./flows.png', flow_img)
# warp image
warped_img = image_warp(unwraped_img, flows)
cv2.imwrite('./warped_img.png', warped_img)
代码输入
unwraped_img.png
reference_img.png
代码输出
flows.png
warped_img.png