本文包括基于OpenCV的三种光流算法的实现源码及测试结果。具体为HS算法,LK算法,和ctfLK算法,算法的原实现作者是Eric Yuan,这里是作者的博客主页:http://eric-yuan.me。本文对这三种光流算法进行了相关调试及结果验证,供大家参考。
1. 第一种:HS光流法(作者HORN 和SCHUNCK)
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include
#include
#include
using namespace cv;
using namespace std;
#define ATD at
#define elif else if
#ifndef bool
#define bool int
#define false ((bool)0)
#define true ((bool)1)
#endif
Mat get_fx(Mat &src1, Mat &src2){
Mat fx;
Mat kernel = Mat::ones(2, 2, CV_64FC1);
kernel.ATD(0, 0) = -1.0;
kernel.ATD(1, 0) = -1.0;
Mat dst1, dst2;
filter2D(src1, dst1, -1, kernel);
filter2D(src2, dst2, -1, kernel);
fx = dst1 + dst2;
return fx;
}
Mat get_fy(Mat &src1, Mat &src2){
Mat fy;
Mat kernel = Mat::ones(2, 2, CV_64FC1);
kernel.ATD(0, 0) = -1.0;
kernel.ATD(0, 1) = -1.0;
Mat dst1, dst2;
filter2D(src1, dst1, -1, kernel);
filter2D(src2, dst2, -1, kernel);
fy = dst1 + dst2;
return fy;
}
Mat get_ft(Mat &src1, Mat &src2){
Mat ft;
Mat kernel = Mat::ones(2, 2, CV_64FC1);
kernel = kernel.mul(-1);
Mat dst1, dst2;
filter2D(src1, dst1, -1, kernel);
kernel = kernel.mul(-1);
filter2D(src2, dst2, -1, kernel);
ft = dst1 + dst2;
return ft;
}
bool isInsideImage(int y, int x, Mat &m){
int width = m.cols;
int height = m.rows;
if (x >= 0 && x < width && y >= 0 && y < height) return true;
else return false;
}
double get_Average4(Mat &m, int y, int x){
if (x < 0 || x >= m.cols) return 0;
if (y < 0 || y >= m.rows) return 0;
double val = 0.0;
int tmp = 0;
if (isInsideImage(y - 1, x, m)){
++tmp;
val += m.ATD(y - 1, x);
}
if (isInsideImage(y + 1, x, m)){
++tmp;
val += m.ATD(y + 1, x);
}
if (isInsideImage(y, x - 1, m)){
++tmp;
val += m.ATD(y, x - 1);
}
if (isInsideImage(y, x + 1, m)){
++tmp;
val += m.ATD(y, x + 1);
}
return val / tmp;
}
Mat get_Average4_Mat(Mat &m){
Mat res = Mat::zeros(m.rows, m.cols, CV_64FC1);
for (int i = 0; i < m.rows; i++){
for (int j = 0; j < m.cols; j++){
res.ATD(i, j) = get_Average4(m, i, j);
}
}
return res;
}
void saveMat(Mat &M, string s){
s += ".txt";
FILE *pOut = fopen(s.c_str(), "w+");
for (int i = 0; i
图1 HS光流法原始图像(之一)
图2:HS光流法计算结果:U
图3 HS光流法计算结果:V
1. 第二种:LK光流法(作者LUCAS 和KANADE)
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include
#include
#include
using namespace cv;
using namespace std;
#define ATD at
#define elif else if
#ifndef bool
#define bool int
#define false ((bool)0)
#define true ((bool)1)
#endif
Mat get_fx(Mat &src1, Mat &src2){
Mat fx;
Mat kernel = Mat::ones(2, 2, CV_64FC1);
kernel.ATD(0, 0) = -1.0;
kernel.ATD(1, 0) = -1.0;
Mat dst1, dst2;
filter2D(src1, dst1, -1, kernel);
filter2D(src2, dst2, -1, kernel);
fx = dst1 + dst2;
return fx;
}
Mat get_fy(Mat &src1, Mat &src2){
Mat fy;
Mat kernel = Mat::ones(2, 2, CV_64FC1);
kernel.ATD(0, 0) = -1.0;
kernel.ATD(0, 1) = -1.0;
Mat dst1, dst2;
filter2D(src1, dst1, -1, kernel);
filter2D(src2, dst2, -1, kernel);
fy = dst1 + dst2;
return fy;
}
Mat get_ft(Mat &src1, Mat &src2){
Mat ft;
Mat kernel = Mat::ones(2, 2, CV_64FC1);
kernel = kernel.mul(-1);
Mat dst1, dst2;
filter2D(src1, dst1, -1, kernel);
kernel = kernel.mul(-1);
filter2D(src2, dst2, -1, kernel);
ft = dst1 + dst2;
return ft;
}
bool isInsideImage(int y, int x, Mat &m){
int width = m.cols;
int height = m.rows;
if (x >= 0 && x < width && y >= 0 && y < height) return true;
else return false;
}
double get_Sum9(Mat &m, int y, int x){
if (x < 0 || x >= m.cols) return 0;
if (y < 0 || y >= m.rows) return 0;
double val = 0.0;
int tmp = 0;
if (isInsideImage(y - 1, x - 1, m)){
++tmp;
val += m.ATD(y - 1, x - 1);
}
if (isInsideImage(y - 1, x, m)){
++tmp;
val += m.ATD(y - 1, x);
}
if (isInsideImage(y - 1, x + 1, m)){
++tmp;
val += m.ATD(y - 1, x + 1);
}
if (isInsideImage(y, x - 1, m)){
++tmp;
val += m.ATD(y, x - 1);
}
if (isInsideImage(y, x, m)){
++tmp;
val += m.ATD(y, x);
}
if (isInsideImage(y, x + 1, m)){
++tmp;
val += m.ATD(y, x + 1);
}
if (isInsideImage(y + 1, x - 1, m)){
++tmp;
val += m.ATD(y + 1, x - 1);
}
if (isInsideImage(y + 1, x, m)){
++tmp;
val += m.ATD(y + 1, x);
}
if (isInsideImage(y + 1, x + 1, m)){
++tmp;
val += m.ATD(y + 1, x + 1);
}
if (tmp == 9) return val;
else return m.ATD(y, x) * 9;
}
Mat get_Sum9_Mat(Mat &m){
Mat res = Mat::zeros(m.rows, m.cols, CV_64FC1);
for (int i = 1; i < m.rows - 1; i++){
for (int j = 1; j < m.cols - 1; j++){
res.ATD(i, j) = get_Sum9(m, i, j);
}
}
return res;
}
void saveMat(Mat &M, string s){
s += ".txt";
FILE *pOut = fopen(s.c_str(), "w+");
for (int i = 0; i
图4 LK光流法原始图像(之一)
图5 LK光流法计算结果:U
图6 LK光流法计算结果:V
1. 第三种:ctfLK光流法(LK光流法的Coarse to fine版本)
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include
#include
#include
using namespace cv;
using namespace std;
#define ATD at
#define ATF at
#define elif else if
#ifndef bool
#define bool int
#define false ((bool)0)
#define true ((bool)1)
#endif
Mat get_fx(Mat &src1, Mat &src2){
Mat fx;
Mat kernel = Mat::ones(2, 2, CV_64FC1);
kernel.ATD(0, 0) = -1.0;
kernel.ATD(1, 0) = -1.0;
Mat dst1, dst2;
filter2D(src1, dst1, -1, kernel);
filter2D(src2, dst2, -1, kernel);
fx = dst1 + dst2;
return fx;
}
Mat get_fy(Mat &src1, Mat &src2){
Mat fy;
Mat kernel = Mat::ones(2, 2, CV_64FC1);
kernel.ATD(0, 0) = -1.0;
kernel.ATD(0, 1) = -1.0;
Mat dst1, dst2;
filter2D(src1, dst1, -1, kernel);
filter2D(src2, dst2, -1, kernel);
fy = dst1 + dst2;
return fy;
}
Mat get_ft(Mat &src1, Mat &src2){
Mat ft;
Mat kernel = Mat::ones(2, 2, CV_64FC1);
kernel = kernel.mul(-1);
Mat dst1, dst2;
filter2D(src1, dst1, -1, kernel);
kernel = kernel.mul(-1);
filter2D(src2, dst2, -1, kernel);
ft = dst1 + dst2;
return ft;
}
bool isInsideImage(int y, int x, Mat &m){
int width = m.cols;
int height = m.rows;
if (x >= 0 && x < width && y >= 0 && y < height) return true;
else return false;
}
double get_Sum9(Mat &m, int y, int x){
if (x < 0 || x >= m.cols) return 0;
if (y < 0 || y >= m.rows) return 0;
double val = 0.0;
int tmp = 0;
for (int i = -1; i <= 1; i++){
for (int j = -1; j <= 1; j++){
if (isInsideImage(y + i, x + j, m)){
++tmp;
val += m.ATD(y + i, x + j);
}
}
}
if (tmp == 9) return val;
else return m.ATD(y, x) * 9;
}
Mat get_Sum9_Mat(Mat &m){
Mat res = Mat::zeros(m.rows, m.cols, CV_64FC1);
for (int i = 1; i < m.rows - 1; i++){
for (int j = 1; j < m.cols - 1; j++){
res.ATD(i, j) = get_Sum9(m, i, j);
}
}
return res;
}
void saveMat(Mat &M, string s){
s += ".txt";
FILE *pOut = fopen(s.c_str(), "w+");
for (int i = 0; i getGaussianPyramid(Mat &img, int nLevels){
vector pyr;
pyr.push_back(img);
for (int i = 0; i < nLevels - 1; i++){
Mat tmp;
pyrDown(pyr[pyr.size() - 1], tmp);
pyr.push_back(tmp);
}
return pyr;
}
void coarseToFineEstimation(Mat &img1, Mat &img2, Mat &u, Mat &v, int nLevels){
vector pyr1 = getGaussianPyramid(img1, nLevels);
vector pyr2 = getGaussianPyramid(img2, nLevels);
Mat upu, upv;
for (int i = nLevels - 1; i >= 0; i--){
Mat tmpu = Mat::zeros(pyr1[i].rows, pyr1[i].cols, CV_64FC1);
Mat tmpv = Mat::zeros(pyr2[i].rows, pyr2[i].cols, CV_64FC1);
getLucasKanadeOpticalFlow(pyr1[i], pyr2[i], tmpu, tmpv);
if (i != nLevels - 1){
tmpu += upu;
tmpv += upv;
}
if (i == 0){
u = tmpu;
v = tmpv;
return;
}
pyrUp(tmpu, upu);
pyrUp(tmpv, upv);
Mat map1(upu.size(), CV_32FC2);
Mat map2(upu.size(), CV_32FC2);
for (int y = 0; y < map1.rows; ++y){
for (int x = 0; x < map1.cols; ++x){
Point2f f = Point2f((float)(upu.ATD(y, x)), (float)(upv.ATD(y, x)));
map1.at(y, x) = Point2f(x + f.x / 2, y + f.y / 2);
map2.at(y, x) = Point2f(x - f.x / 2, y - f.y / 2);
}
}
Mat warped1, warped2;
remap(pyr1[i - 1], warped1, map1, cv::Mat(), INTER_LINEAR);
remap(pyr2[i - 1], warped2, map2, cv::Mat(), INTER_LINEAR);
warped1.copyTo(pyr1[i - 1]);
warped2.copyTo(pyr2[i - 1]);
}
}
int getMaxLayer(Mat &img){
int width = img.cols;
int height = img.rows;
int res = 1;
int p = 1;
while (1){
int tmp = pow(2, p);
if (width % tmp == 0) ++p;
else break;
}
res = p;
p = 1;
while (1){
int tmp = pow(2, p);
if (height % tmp == 0) ++p;
else break;
}
res = res < p ? res : p;
return res;
}
int main(){
Mat ori1 = imread("table1.jpg", 0);
Mat ori2 = imread("table2.jpg", 0);
Mat img1 = ori1(Rect(0, 0, 640, 448));
Mat img2 = ori2(Rect(0, 0, 640, 448));
int maxLayer = getMaxLayer(img1);
cout << img1.rows << ", " << img1.cols << ", Max layer = " << maxLayer << endl;
img1.convertTo(img1, CV_64FC1, 1.0 / 255, 0);
img2.convertTo(img2, CV_64FC1, 1.0 / 255, 0);
Mat u = Mat::zeros(img1.rows, img1.cols, CV_64FC1);
Mat v = Mat::zeros(img1.rows, img1.cols, CV_64FC1);
Mat u2 = Mat::zeros(img1.rows, img1.cols, CV_64FC1);
Mat v2 = Mat::zeros(img1.rows, img1.cols, CV_64FC1);
if (maxLayer >= 1){
coarseToFineEstimation(img1, img2, u, v, maxLayer);
saveMat(u, "U");
saveMat(v, "V");
}
getLucasKanadeOpticalFlow(img1, img2, u2, v2);
saveMat(u2, "U2");
saveMat(v2, "V2");
imshow("U2", u2);
imshow("v2", v2);
waitKey(20000);
return 0;
}
图7 ctfLK光流法测试原始图像(之一)
图8 ctfLK光流法计算结果:U2
图9 ctfLK光流法计算结果:v2