- 操作系统:ubuntu22.04
- OpenCV版本:OpenCV4.9
- IDE:Visual Studio Code
- 编程语言:C++11
算法描述
这个类实现了 Dense Inverse Search (DIS) 光流算法。更多关于该算法的细节可以在文献 146中找到。该实现包含了三个预设参数集,以提供速度和质量之间的合理折衷。然而,即使是速度最慢的预设仍然相对较快,如果你需要更好的质量和不关心速度的话,可以使用 DeepFlow。
与论文中描述的算法相比,这个实现还包含了几个附加特性,包括光流向量的空间传播(通过 getUseSpatialPropagation 控制),以及利用传递给 calc 方法的初始光流近似值的选项(如果传递前一帧的光流场,这基本上就是时间传播)。
cv::DISOpticalFlow 是 OpenCV 中用于计算密集光流(Dense Optical Flow)的一种方法,它基于半相关(displaced-phase correlation)技术。该方法特别适用于实时应用,因为它提供了较高的精度并且计算效率较高。
主要特点:
- 实时性:适合实时应用,因为计算速度快。
- 高精度:相较于传统的光流算法,如 Lucas-Kanade 或 Farneback 方法,DISOpticalFlow 提供了更高的精度。
- 密集光流:能够计算图像中每个像素的光流向量,而不是稀疏点。
成员函数
函数create()
cv::DISOpticalFlow::create 是一个静态工厂方法,用于创建 DISOpticalFlow 类的实例。这个方法允许你在创建对象时指定预设参数,这些参数会影响算法的速度和质量。
原型
static Ptr<DISOpticalFlow> cv::DISOpticalFlow::create
(
int preset = DISOpticalFlow::PRESET_FAST
)
参数
- 参数preset:这是一个可选参数,默认值为 DISOpticalFlow::PRESET_FAST。它指定了算法使用的预设配置。预设参数提供了速度和质量之间的不同权衡。
预设参数
DISOpticalFlow 类提供了几种预设参数,具体如下:
- DISOpticalFlow::PRESET_ULTRAFAST:最快的预设,牺牲了一部分质量以获得最高的速度。
- DISOpticalFlow::PRESET_FAST:较快的预设,默认值,提供了较好的速度和质量平衡。
- DISOpticalFlow::PRESET_MEDIUM:中等速度的预设,进一步提高了质量。
- DISOpticalFlow::PRESET_ULTRA:最慢的预设,提供了最高质量的结果。
函数getFinestScale()
v::DISOpticalFlow::getFinestScale() 是一个成员函数,用于获取当前 DISOpticalFlow 对象所使用的最精细尺度(finest scale)。尺度是指在计算光流时对输入图像进行金字塔分解的程度,尺度越大,图像分辨率越低;尺度越小,图像分辨率越高。
原型
virtual int cv::DISOpticalFlow::getFinestScale ( ) const
返回值
返回一个整数,表示当前设置的最精细尺度。
作用
在光流计算中,尺度的选择对算法的精度和速度有很大影响。较高的尺度意味着较低的分辨率,这样可以提高计算速度,但可能降低精度;较低的尺度则相反,可以提高精度,但会增加计算量。
函数getGradientDescentIterations()
cv::DISOpticalFlow::getGradientDescentIterations() 是一个成员函数,用于获取当前 DISOpticalFlow 对象在计算光流过程中执行梯度下降迭代的次数。这个参数对于算法的精度和性能有重要影响。
原型
virtual int cv::DISOpticalFlow::getGradientDescentIterations ( ) const
返回值
返回一个整数,表示当前设置的梯度下降迭代次数。
作用
在光流计算过程中,梯度下降迭代次数决定了算法在每一步中优化光流场的次数。更多的迭代次数通常会导致更精确的光流估计,但也增加了计算时间。因此,在实际应用中需要在精度和速度之间做出权衡。
函数getPatchSize()
cv::DISOpticalFlow::getPatchSize() 是一个成员函数,用于获取当前 DISOpticalFlow 对象在计算光流时所使用的补丁(patch)大小。补丁大小是指在光流计算过程中用来匹配像素块的窗口大小,它对算法的精度和计算效率有直接影响。
原型
virtual int cv::DISOpticalFlow::getPatchSize() const;
返回值
返回一个整数,表示当前设置的补丁大小。
作用
在光流计算过程中,补丁大小决定了用于匹配的像素块的尺寸。较大的补丁大小可能会提高匹配的鲁棒性和精度,但也会增加计算复杂度。较小的补丁大小则可以加快计算速度,但可能会降低精度。
函数getPatchStride()
cv::DISOpticalFlow::getPatchStride() 是一个成员函数,用于获取当前 DISOpticalFlow 对象在计算光流时所使用的补丁(patch)步长。补丁步长决定了在计算光流的过程中,算法如何在图像上滑动补丁进行匹配。
原型
virtual int cv::DISOpticalFlow::getPatchStride ( ) const
返回值
返回一个整数,表示当前设置的补丁步长。
作用
在光流计算过程中,补丁步长决定了补丁在图像上的移动距离。较大的步长可以减少计算的补丁数量,从而加快计算速度,但可能会导致精度下降。较小的步长则可以提高精度,但会增加计算量。
函数getUseMeanNormalization()
cv::DISOpticalFlow::getUseMeanNormalization() 是一个成员函数,用于查询当前 DISOpticalFlow 对象是否启用了均值归一化(mean normalization)。均值归一化是一种预处理技术,用于减小光照变化和其他环境因素对光流估计的影响。
原型
virtual bool cv::DISOpticalFlow::getUseMeanNormalization ( ) const
返回值
返回一个布尔值,指示是否启用了均值归一化。
作用
均值归一化有助于提高光流估计的准确性,特别是在存在光照变化的情况下。通过均值归一化,算法会对图像中的每个补丁(patch)进行处理,使其均值接近于零,从而减少环境变化对光流估计的影响。
函数getUseSpatialPropagation()
cv::DISOpticalFlow::getUseSpatialPropagation() 是一个成员函数,用于查询当前 DISOpticalFlow 对象是否启用了空间传播(spatial propagation)。空间传播是一种后处理技术,用于改进光流估计的一致性和平滑性。
原型
virtual bool cv::DISOpticalFlow::getUseSpatialPropagation ( ) const
返回值
返回一个布尔值,指示是否启用了空间传播。
作用
空间传播可以在光流估计之后,通过考虑周围像素的光流估计值来平滑和优化光流场。这有助于减少孤立的不一致估计,并且可以使最终的光流场更加一致和平滑。
函数getVariationalRefinementAlpha()
cv::DISOpticalFlow::getVariationalRefinementAlpha() 是一个成员函数,用于获取当前 DISOpticalFlow 对象在变分细化(variational refinement)过程中使用的 alpha 参数值。这个参数影响着细化过程中的平滑程度。
原型
virtual float cv::DISOpticalFlow::getVariationalRefinementAlpha ( ) const
返回值
返回一个浮点数,表示当前设置的 alpha 参数值。
作用
在光流计算过程中,变分细化是一种后处理步骤,用于提高光流场的质量。alpha 参数控制了细化过程中平滑项的权重。较大的 alpha 值会导致更平滑的光流场,而较小的 alpha 值则保留更多细节。
函数getVariationalRefinementDelta()
cv::DISOpticalFlow::getVariationalRefinementDelta() 是一个成员函数,用于获取当前 DISOpticalFlow 对象在变分细化(variational refinement)过程中使用的 delta 参数值。这个参数影响着细化过程中的亮度一致性约束。
原型
virtual float cv::DISOpticalFlow::getVariationalRefinementDelta ( ) const
返回值
返回一个浮点数,表示当前设置的 delta 参数值。
作用
在光流计算过程中,变分细化是一种后处理步骤,用于提高光流场的质量。delta 参数控制了亮度一致性约束的强度。较大的 delta 值意味着更高的容错性,即在亮度变化较大的情况下仍能保持较好的光流估计;较小的 delta 值则对亮度一致性要求更高,适用于亮度变化较小的情况。
函数getVariationalRefinementGamma()
cv::DISOpticalFlow::getVariationalRefinementGamma() 是一个成员函数,用于获取当前 DISOpticalFlow 对象在变分细化(variational refinement)过程中使用的 gamma 参数值。这个参数影响着细化过程中的平滑程度以及对噪声的敏感度。
原型
virtual float cv::DISOpticalFlow::getVariationalRefinementGamma ( ) const
返回值
返回一个浮点数,表示当前设置的 gamma 参数值。
作用
在光流计算过程中,变分细化是一种后处理步骤,用于提高光流场的质量。gamma 参数控制了细化过程中对光流场平滑性和对噪声敏感性的平衡。较高的 gamma 值会使光流场更加平滑,但可能丢失一些细节;较低的 gamma 值则保留更多的细节,但也可能导致更多的噪声。
代码示例
#include <iostream>
#include <opencv2/opencv.hpp>
// 自定义函数,用于将光流向量转换为彩色图像
/// 自定义函数,用于将光流向量转换为彩色图像
void flowToColor(const cv::Mat& flow, cv::Mat& colorFlow) {
const float maxMagnitude = 10.0f; // 可视化中的最大流速
int cols = flow.cols;
int rows = flow.rows;
colorFlow.create(rows, cols, CV_8UC3);
for (int y = 0; y < rows; ++y) {
for (int x = 0; x < cols; ++x) {
cv::Point2f fxy = flow.at<cv::Point2f>(y, x);
float magnitude = std::sqrt(fxy.x * fxy.x + fxy.y * fxy.y);
magnitude = std::min(magnitude / maxMagnitude, 1.0f); // 归一化
// 使用HSV颜色空间来表示方向
float angle = std::atan2(fxy.y, fxy.x);
float hue = (angle + M_PI) / (2 * M_PI); // 转换为0-1范围内的Hue值
hue = std::fmod(hue + 1.0f, 1.0f); // 确保Hue值在0-1之间
// 构造HSV颜色
cv::Vec3b hsv;
hsv[0] = static_cast<uchar>(hue * 180); // Hue
hsv[1] = static_cast<uchar>(magnitude * 255); // Saturation
hsv[2] = static_cast<uchar>(255); // Value
// 将HSV颜色转换为BGR颜色
cv::Vec3b bgr;
cv::Mat hsvPixel(1, 1, CV_8UC3, hsv);
cv::Mat bgrPixel;
cvtColor(hsvPixel, bgrPixel, cv::COLOR_HSV2BGR);
bgr = bgrPixel.at<cv::Vec3b>(0, 0);
// 设置颜色
colorFlow.at<cv::Vec3b>(y, x) = bgr;
}
}
}
int main( int argc, char** argv )
{
// 创建 DISOpticalFlow 对象
cv::Ptr< cv::DISOpticalFlow > dis = cv::DISOpticalFlow::create();
// 打开默认摄像头
cv::VideoCapture cap( 0 );
if ( !cap.isOpened() )
{
std::cout << "无法打开摄像头" << std::endl;
return -1;
}
cv::Mat frame, prevFrame, gray, flow, colorFlow;
// 读取第一帧作为初始帧
cap >> frame;
if ( frame.empty() )
{
std::cout << "结束读取" << std::endl;
return -1;
}
cv::cvtColor( frame, prevFrame, cv::COLOR_BGR2GRAY );
cv::namedWindow( "Optical Flow", cv::WINDOW_NORMAL );
while ( true )
{
cap >> frame; // 读取下一帧
if ( frame.empty() )
{
std::cout << "结束读取" << std::endl;
break;
}
cv::cvtColor( frame, gray, cv::COLOR_BGR2GRAY );
// 计算光流
dis->calc( prevFrame, gray, flow );
// 将光流向量转换为彩色图像
flowToColor( flow, colorFlow );
// 更新前一帧
prevFrame = gray.clone();
// 显示光流图像
cv::imshow( "Optical Flow", colorFlow );
if ( cv::waitKey( 30 ) >= 0 )
{
break;
}
}
return 0;
}