上一篇文章写了在板子上运行光流,但是这个光流的话是全局的光流,也就是整个屏幕的角点进行光流运算,现在把我之前弄得,一个可选追踪单点的单点式光流程序,整个程序的代码贴出来。
运行环境:
1,nvidia jetson tk1
2.opencv 2.4.9
3.linux 的 g++
#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <opencv2\opencv.hpp>
#include <iostream>
#include <ctype.h>
#include <stdio.h>
//#include <unistd.h>
#include <stdlib.h>
using namespace cv;
using namespace std;
vector<Point2f> point1, point2;
bool left_mouse = false;
Point2f point;
int pic_info[4];
Mat gray, prevGray, image;
const Scalar GREEN = Scalar(0, 255, 0);
int rect_width = 0, rect_height = 0;
Point tmpPoint;
static const double pi = 3.14159265358979323846;
static IplImage *frame1 = NULL;
static IplImage *framet = NULL;
static int line_thickness = 2 ;
CvPoint p, q;
inline static double square(int a)
{
return a * a;
}
static void onMouse(int event, int x, int y, int /*flags*/, void* /*param*/){
Mat mouse_show;
image.copyTo(mouse_show);
if (event == CV_EVENT_LBUTTONDOWN){
pic_info[0] = x;
pic_info[1] = y;
left_mouse = true;
}
else if (event == CV_EVENT_LBUTTONUP){
rectangle(mouse_show, Point(pic_info[0], pic_info[1]), Point(x, y), GREEN, 2);
x = (pic_info[0] + x) / 2;
y = (pic_info[1] + y) / 2;
point = Point2f((float)x, (float)y);
point1.clear();
point2.clear();
point1.push_back(point);
imshow("LK Demo", mouse_show);
left_mouse = false;
}
else if ((event == CV_EVENT_MOUSEMOVE) && (left_mouse == true)){
rectangle(mouse_show, Point(pic_info[0], pic_info[1]), Point(x, y), GREEN, 2);
imshow("LK Demo", mouse_show);
}
}
int main(int argc, char** argv)
{
//读取摄像头
VideoCapture cap(0);
//读取视频文件
//VideoCapture cap; cap.open("optical_flow_input.avi");
if (!cap.isOpened())
{
return -1;
}
TermCriteria termcrit(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, 0.03); //迭代算法的终止条件
Size winSize(32, 32);
CvSize frame_size;
frame_size.height = cap.get(CV_CAP_PROP_FRAME_HEIGHT);
frame_size.width = cap.get(CV_CAP_PROP_FRAME_WIDTH);
//allocateOnDemand(&frame1, frame_size, IPL_DEPTH_8U, 3);
namedWindow("LK Demo", CV_WINDOW_NORMAL);
setMouseCallback("LK Demo", onMouse, 0);
for (;;){
Mat frame;
cap.read(frame);
if (frame.empty())
break;
frame.copyTo(image);
cvtColor(image, gray, COLOR_BGR2GRAY);
framet = &IplImage(image);
if ((!point1.empty())){
vector<uchar> status;
vector<float> err;
if (prevGray.empty())
gray.copyTo(prevGray);
calcOpticalFlowPyrLK(prevGray, gray, point1, point2, status, err, winSize,5, termcrit, 0, 0.001); //使用金字塔Lucas&Kanade方法计算一个稀疏特征集的光流
tmpPoint = point2[0];
rectangle(image, Point(tmpPoint.x - 30, tmpPoint.y - 30), Point(tmpPoint.x + 30, tmpPoint.y + 30), GREEN, 2);
p.x = (int)point1[0].x;
p.y = (int)point1[0].y;
q.x = (int)point2[0].x;
q.y = (int)point2[0].y;
double angle;
angle = atan2((double)p.y - q.y, (double)p.x - q.x);
double hypotenuse;
hypotenuse = sqrt(square(p.y - q.y) + square(p.x - q.x));
CvScalar line_color;
line_color = CV_RGB(255, 0, 0);
/*执行缩放*/
q.x = (int)(p.x - 5 * hypotenuse * cos(angle));
q.y = (int)(p.y - 5 * hypotenuse * sin(angle));
/*画箭头主线*/
/* "frame1"要在frame1上作画. * "p" 线的开始点. * "q" 线的终止点. * "CV_AA" 反锯齿. * "0" 没有小数位. */
cvLine(framet, p, q, line_color, line_thickness, CV_AA, 0);
/* 画箭的头部*/
p.x = (int)(q.x + 9 * cos(angle + pi / 4));
p.y = (int)(q.y + 9 * sin(angle + pi / 4));
cvLine(framet, p, q, line_color, line_thickness, CV_AA, 0);
p.x = (int)(q.x + 9 * cos(angle - pi / 4));
p.y = (int)(q.y + 9 * sin(angle - pi / 4));
cvLine(framet, p, q, line_color, line_thickness, CV_AA, 0);
cvShowImage("LK Demo", framet);
}
imshow("LK Demo", image);
waitKey(100);
std::swap(point2, point1);
cv::swap(prevGray, gray);
}
return 0;
}
简单说一下这个代码,可以单点光流,因为用的是onmouse()这个函数监视鼠标的事件,鼠标左键单击就会返回一个x,y的坐标值,根据这个点的坐标,输入到光流函数里,光流的部分主要就是靠calcOpticalFlowPyrLK()这个函数实现的,来监视这个点的灰度值在整个过程中的位移变化,并且会移植在这个点周围画一个正方形,移动的速度和方向还用经典的光流向量箭头来表示。
在tk1上的使用方法:
1,首先把上面那段代码保存成比如mouse.cpp的文件拷到tk1的某个文件夹位置。
2,然后打开终端,cd进入那个文件夹的位置。
3,在终端中输入
g++ mouse.cpp -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_video -lopencv_contrib -lopencv_features2d -lopencv_flann -lopencv_gpu -lopencv_legacy -lopencv_ml -lopencv_objdetect -lopencv_photo -lopencv_stitching -lopencv_superres -lopencv_videostab -lopencv_calib3d -o mouse
4,能跑通的话,cpp文件应该会消失,在该文件夹中出现一个可执行的文件。
5,要运行这个程序的话,两种方式:
一个是接着上面那短代码接着输入
./mouse
这样会在终端中显示运行情况,如果有错误,或者运行不了,在终端中会有错误的提醒。
第二种方式就是直接点击该文件夹里的那个叫mouse的可执行文件,但不会显示运行的情况,只有一个对话框运行着光流代码。
问题一:运行时会报内存错误。
解决方法:后来查了一下资料,发现有些函数的接口格式与调用函数时输入进去的格式不符,方法
//先定义一个中间的值
static IplImage *framet = NULL;
//对于原来的值image
mat image;
//用&IplImage()这个小函数转换一下传给暂存值framet
framet = &IplImage(image);
//之后完美运行
cvShowImage("LK Demo", framet);
问题二:双击可执行文件方式运行程序,程序没法关闭,关了之后还往出冒
解决方法:
首先执行代码:ps -e
调出整个系统的所有进程
看叫 mouse 的这个进程,记住对应的前面四位编号是多少 假设是8888
然后执行 sudo kill 8888
输入密码
进程就结束了,再也不往出冒了
当时弄完这个之后,就开始研究双目视觉的东西,主要就是用两个摄像头来实现对图像深度的测算,目前的进展是:可以完全用一段opencv 的代码在电脑上实现视差图的输出,可以输出指定点的三维坐标,但是三维坐标不是很准,目前怀疑是因为视差图太粗犷缘故,目前在电脑上ok的代码,在tk1上可以编译,但是没法运行,两个摄像头死活启动不起来,目前还没调通。
目前,由于被分配了新的任务,估计opencv的东西不可避免的要稍微搁一下了,有时间再把双目视觉的我的理解,和一些代码传上来备份一下。
更多光流算法,可见我之前的文章: http://blog.csdn.net/hysteric314/article/details/48682327