当前位置:   article > 正文

Opencv-光流算法-实战

Opencv-光流算法-实战
0. 写在前面

        理论介绍篇在:图像处理算法--光流法-原理-CSDN博客

2. Main函数代码
  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3. #include <QFileDialog>
  4. #include <QLabel>
  5. #include <QDebug>
  6. MainWindow::MainWindow(QWidget *parent) :
  7. QMainWindow(parent),
  8. ui(new Ui::MainWindow)
  9. {
  10. ui->setupUi(this);
  11. //定时器
  12. showTimer = new QTimer(this);
  13. connect(showTimer,SIGNAL(timeout()),this,SLOT(ReadFrame()));
  14. //初始化状态栏
  15. QLabel *labelFile = new QLabel("暂时无文件",this);
  16. labelFile->setMinimumWidth(300);
  17. //将初始化的标签添加到底部状态上
  18. ui->statusBar->addWidget(labelFile);
  19. ui->centralWidget->setMouseTracking(true);
  20. this->setMouseTracking(true);
  21. }
  22. MainWindow::~MainWindow()
  23. {
  24. delete ui;
  25. }
  26. void MainWindow::tracking(Mat &frame, Mat &output)
  27. {
  28. cvtColor(frame, gray, COLOR_BGR2GRAY);
  29. frame.copyTo(output);
  30. //添加特征点
  31. if (addNewPoints())
  32. {
  33. goodFeaturesToTrack(gray, features, maxCount, qLevel, minDest);
  34. points[0].insert(points[0].end(), features.begin(), features.end());
  35. initial.insert(initial.end(), features.begin(), features.end());
  36. }
  37. if (gray_prev.empty())
  38. {
  39. gray.copyTo(gray_prev);
  40. }
  41. //l-k流光法运动估计
  42. calcOpticalFlowPyrLK(gray_prev, gray, points[0], points[1], status, err);
  43. //去掉一些不好的特征点
  44. int k = 0;
  45. for (size_t i = 0; i < points[1].size(); i++)
  46. {
  47. if (acceptTrackedPoint(i))
  48. {
  49. initial[k] = initial[i];
  50. points[1][k++] = points[1][i];
  51. }
  52. }
  53. points[1].resize(k);
  54. initial.resize(k);
  55. //显示特征点和运动轨迹
  56. for (size_t i = 0; i < points[1].size(); i++)
  57. {
  58. line(output, initial[i], points[1][i], Scalar(0, 0, 255));
  59. circle(output, points[1][i], 3, Scalar(0, 255, 0), -1);
  60. }
  61. //把当前跟踪结果作为下一次的参考
  62. swap(points[1],points[0]);
  63. swap(gray_prev,gray);
  64. imshow(window_name, output);
  65. }
  66. bool MainWindow::addNewPoints()
  67. {
  68. return points[0].size() <= 10; //points.size()求行数 points.size()求列数
  69. }
  70. bool MainWindow::acceptTrackedPoint(int i)
  71. {
  72. return status[i] && ((abs(points[0][i].x - points[1][i].x) + abs(points[0][i].y - points[1][i].y)) > 2);
  73. }
  74. //鼠标移动事件
  75. void MainWindow::mouseMoveEvent(QMouseEvent *event)
  76. {
  77. //if(event->buttons() & Qt::LeftButton)
  78. {
  79. QPoint sPoint1=event->globalPos();
  80. QPoint widgetPoint = ui->ShowLabel->mapFromGlobal(sPoint1);
  81. ui->TXTLabel_x->setNum((widgetPoint.x()));
  82. ui->TXTLabel_y->setNum((widgetPoint.y()));
  83. }
  84. }
  85. Mat MainWindow::moveCheck(Mat &forntFrame, Mat &afterFrame)
  86. {
  87. Mat frontGray,afterGray,diff;
  88. Mat resFrame=afterFrame.clone();
  89. //灰度处理
  90. cvtColor(forntFrame,frontGray,COLOR_BGR2GRAY);
  91. cvtColor(afterFrame,afterGray,COLOR_BGR2GRAY);
  92. //帧差处理 找到帧与帧之间运动物体差异
  93. absdiff(frontGray,afterGray,diff);
  94. //imshow("diff",diff);
  95. //二值化
  96. //threshold(diff,diff,15,255,THRESH_BINARY);
  97. adaptiveThreshold(diff,diff,255,ADAPTIVE_THRESH_GAUSSIAN_C,THRESH_BINARY,5,5,5);
  98. imshow("threashold",diff);
  99. waitKey(25);
  100. //腐蚀处理:
  101. Mat element=cv::getStructuringElement(MORPH_RECT,Size(3,3));
  102. erode(diff,diff,element);
  103. //imshow("erode",diff);
  104. //膨胀处理
  105. Mat element2=cv::getStructuringElement(MORPH_RECT,Size(20,20));
  106. dilate(diff,diff,element2);
  107. //imshow("dilate",diff);
  108. //动态物体标记
  109. vector<vector<Point>>contours;//保存关键点
  110. findContours(diff,contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE,Point(0,0));
  111. //提取关键点
  112. vector<vector<Point>>contour_poly(contours.size());
  113. vector<Rect>boundRect(contours.size());
  114. int x,y,w,h;
  115. int num=contours.size();
  116. for(int i=0;i<num;i++)
  117. {
  118. approxPolyDP(Mat(contours[i]),contour_poly[i],3,true);
  119. boundRect[i]=boundingRect(Mat(contour_poly[i]));
  120. x=boundRect[i].x;
  121. y=boundRect[i].y;
  122. w=boundRect[i].width;
  123. h=boundRect[i].height;
  124. //绘制
  125. rectangle(resFrame,Point(x,y),Point(x+w,y+h),Scalar(0,255,0),2);
  126. }
  127. return resFrame;
  128. }
  129. void MainWindow::on_pBtn_OpenFile_clicked()
  130. {
  131. //打开图片文件,选择图片
  132. QString filename = QFileDialog::getOpenFileName(this,tr("Open File"),QDir::homePath(),tr("所有文件(*.avi *.mp4 *.h624 *.mkv)\n(*.jpg)\n(*.bmp)\n(*.png)"));
  133. capture.open(filename.toStdString()); //.toStdString()
  134. if(!capture.isOpened())
  135. {
  136. ui->statusBar->showMessage(tr("Open Video Failed!"));
  137. }
  138. else
  139. {
  140. ui->statusBar->showMessage(tr("Open Video Success!"));
  141. }
  142. Mat frame;
  143. Mat temp;
  144. Mat res;
  145. int count = 0;
  146. while(capture.read(frame))
  147. {
  148. //frame = frame(cv::Rect(440, 260,200,200));
  149. count = count + 60;
  150. if(count==0)
  151. {
  152. res=moveCheck(frame,frame);
  153. }
  154. else
  155. {
  156. res=moveCheck(temp,frame);
  157. }
  158. temp=frame.clone();
  159. imshow("frame",frame);
  160. imshow("res",res);
  161. waitKey(2500);
  162. }
  163. #if 0
  164. Mat frame,gray;
  165. vector<Point2f> features; //检测出来的角点集合
  166. vector<Point2f> inPoints; //这个主要是为了画线用的
  167. vector<Point2f> fpts[2]; //[0],存入的是是二维特征向量,[1]输出的二维特征向量
  168. Mat pre_frame,pre_gray;
  169. vector<uchar> status; //光流输出状态
  170. vector<float> err; //光流输出错误
  171. //【2】循环读取视频
  172. while(capture.read(frame))
  173. {
  174. //循环读取视频中每一帧的图像
  175. //【3】将视频帧图像转为灰度图
  176. cvtColor(frame,gray,COLOR_BGR2GRAY); //ps:角点检测输入要求单通道
  177. cv::Mat imageRIO = gray(cv::Rect(440, 260,200,200));
  178. cv::imshow("ROI",imageRIO);
  179. //【4】如果特征向量(角点)小于40个我们就重新执行角点检测
  180. if(fpts[0].size()<25)
  181. {
  182. //如果小于40个角点就重新开始执行角点检测
  183. //执行角点检测
  184. goodFeaturesToTrack(imageRIO,features,1000,0.01,10,Mat(),3,false,0.04);
  185. //【5】将检测到的角点放入fpts[0]中作为,光流跟踪的输入特征向量
  186. //将检测到的角点插入vector
  187. fpts[0].insert(fpts[0].begin(),features.begin(),features.end());
  188. inPoints.insert(inPoints.end(),features.begin(),features.end());
  189. qDebug()<<"角点检测执行完成,角点个数为:"<<features.size();
  190. }else{
  191. qDebug()<<"正在跟踪...";
  192. }
  193. //【6】初始化的时候如果检测到前一帧为空,这个把当前帧的灰度图像给前一帧
  194. if(pre_gray.empty()){//如果前一帧为空就给前一帧赋值一次
  195. imageRIO.copyTo(pre_gray);
  196. }
  197. //执行光流跟踪
  198. qDebug()<<"开始执行光流跟踪";
  199. //【7】执行光流跟踪,并将输出的特征向量放入fpts[1]中
  200. calcOpticalFlowPyrLK(pre_gray,imageRIO,fpts[0],fpts[1],status,err);
  201. qDebug()<<"光流跟踪执行结束";
  202. //【8】遍历光流跟踪的输出特征向量,并得到距离和状态都符合预期的特征向量。让后将其重新填充到fpts[1]中备用
  203. int k =0;
  204. for(size_t i=0;i<fpts[1].size();i++)
  205. { //循环遍历二维输出向量
  206. double dist = abs(fpts[0][i].x - fpts[1][i].x) + abs(fpts[0][i].y - fpts[1][i].y); //特征向量移动距离
  207. if(dist>1&&status[i])
  208. { //如果距离大于2,status=true(正常)
  209. inPoints[k] = inPoints[i];
  210. fpts[1][k++] = fpts[1][i];
  211. }
  212. }
  213. //【9】重置集合大小(由于有错误/不符合条件的输出特征向量),只拿状态正确的
  214. //重新设置集合大小
  215. inPoints.resize(k);
  216. fpts[1].resize(k);
  217. //【10】绘制光流线,这一步要不要都行
  218. //绘制光流线
  219. if(true){
  220. for(size_t i = 0;i<fpts[1].size();i++){
  221. line(imageRIO,inPoints[i],fpts[1][i],Scalar(0,255,0),1,8,0);
  222. circle(imageRIO, fpts[1][i], 2, Scalar(0, 0, 255), 2, 8, 0);
  223. }
  224. }
  225. qDebug()<<"特征向量的输入输出交换数据";
  226. //【11】交换特征向量的输入和输出,(循环往复/进入下一个循环),此时特征向量的值会递减
  227. std::swap(fpts[1],fpts[0]);//交换特征向量的输入和输出,此处焦点的总数量会递减
  228. //【12】将用于跟踪的角点绘制出来
  229. //将角点绘制出来
  230. for(size_t i = 0;i<fpts[0].size();i++){
  231. circle(imageRIO,fpts[0][i],2,Scalar(0,0,255),2,8,0);
  232. }
  233. //【13】重置前一帧图像(每一个循环都要刷新)
  234. imageRIO.copyTo(pre_gray);
  235. imageRIO.copyTo(pre_frame);
  236. //【14】展示最终的效果
  237. imshow("imageRIO",imageRIO);
  238. int keyValue = waitKey(100);
  239. if(keyValue==27){//如果用户按ese键退出播放
  240. break;
  241. }
  242. }
  243. #endif
  244. #if 0
  245. //读取第一帧图像,进行初始化;
  246. Mat pre_image;
  247. capture.read(pre_image);
  248. cvtColor(pre_image, pre_image, COLOR_BGR2GRAY);
  249. //光流检测必须为浮点型坐标点
  250. vector<Point2f> prevPts; //定义上一帧图像的稀疏特征点集
  251. vector<Point2f> initpoint; //定义上一帧图像中保留的稀疏特征点集,用于绘制轨迹
  252. vector<Point2f> features; //用于存放从图像中获得的特征角点
  253. goodFeaturesToTrack(pre_image, features, 100, 0.3, 10, Mat(), 3, false); //获取第一帧图像的稀疏特征点集
  254. //insert(插入位置,插入对象的首地址,插入对象的尾地址)
  255. initpoint.insert(initpoint.end(), features.begin(), features.end()); //初始化当前帧的特征点集
  256. prevPts.insert(prevPts.end(), features.begin(), features.end()); //初始化第一帧的特征角点
  257. //Mat frame;
  258. while (capture.read(frame))
  259. {
  260. Mat next_image;
  261. flip(frame, frame, 1);
  262. cvtColor(frame, next_image, COLOR_BGR2GRAY);
  263. vector<Point2f> nextPts; //下一帧图像检测到的对应稀疏特征点集
  264. vector<uchar> status; //输出点的状态向量;如果某点在两帧图像之间存在光流,则该向量中对应该点的元素设置为1,否则设置为0。
  265. vector<float>err; //输出错误的向量; 向量的每个元素都设置为相应特征点的错误
  266. calcOpticalFlowPyrLK(pre_image, next_image, prevPts, nextPts, status, err, Size(31,31));
  267. RNG rng;
  268. int k = 0;
  269. for (int i = 0; i < nextPts.size(); i++) //遍历下一帧图像的稀疏特征点集
  270. {
  271. //计算两个对应特征点的(dx+dy)
  272. double dist = abs(double(prevPts[i].x) - double(nextPts[i].x)) + abs(double(prevPts[i].y) - double(nextPts[i].y));
  273. if (status[i] && dist > 2) //如果该点在两帧图像之间存在光流,且两帧图像中对应点的距离大于2,即非静止点
  274. {
  275. //将存在光流的非静止特征点保留起来
  276. prevPts[k] = prevPts[i];
  277. nextPts[k] = nextPts[i];
  278. initpoint[k] = initpoint[i];
  279. k++;
  280. //绘制保留的特征点
  281. int b = rng.uniform(0, 256);
  282. int g = rng.uniform(0, 256);
  283. int r = rng.uniform(0, 256);
  284. circle(frame, nextPts[i], 3, Scalar(b, g, r), -1, 8, 0);
  285. }
  286. }
  287. //将稀疏特征点集更新为现有的容量,也就是保存下来的特征点数
  288. prevPts.resize(k);
  289. nextPts.resize(k);
  290. initpoint.resize(k);
  291. //在每一帧图像中绘制当前特征点走过的整个路径
  292. for (int j = 0; j < initpoint.size(); j++)
  293. {
  294. int b = rng.uniform(0, 256);
  295. int g = rng.uniform(0, 256);
  296. int r = rng.uniform(0, 256);
  297. line(frame, initpoint[j], nextPts[j], Scalar(b, g, r), 1, 8, 0);
  298. }
  299. imshow("frame", frame);
  300. //swap()交换两个变量的数据
  301. swap(nextPts, prevPts); //将下一帧图像的稀疏特征点集,变为上一帧
  302. swap(pre_image, next_image); //将下一帧图像变为上一帧图像
  303. //当特征点的数量被筛选得低于阈值时,重新从下一帧图像中寻找特征角点;注意此时的上下两帧图像已经互换
  304. if (initpoint.size() < 10)
  305. {
  306. goodFeaturesToTrack(pre_image, features, 100, 0.01, 10, Mat(), 3, false);
  307. initpoint.insert(initpoint.end(), features.begin(), features.end());
  308. prevPts.insert(prevPts.end(), features.begin(), features.end());
  309. }
  310. }
  311. #endif
  312. #if 0
  313. Mat prevgray, gray, rgb, frame;
  314. Mat flow, flow_uv[2];
  315. Mat flow_Farneback;
  316. Mat flow_uv_Farneback[2];
  317. Mat mag, ang;
  318. Mat mag_Farneback, ang_Farneback;
  319. Mat hsv_split[3], hsv;
  320. Mat hsv_split_Farneback[3], hsv_Farneback;
  321. Mat rgb_Farneback;
  322. Ptr<DenseOpticalFlow> algorithm = DISOpticalFlow::create(DISOpticalFlow::PRESET_MEDIUM);
  323. int idx = 0;
  324. while(true)
  325. {
  326. capture >> frame;
  327. if (frame.empty())
  328. break;
  329. cv::resize(frame,frame,cv::Size(0.8*frame.cols,0.8*frame.rows),0,0,cv::INTER_LINEAR);
  330. idx++;
  331. cvtColor(frame, gray, COLOR_BGR2GRAY);
  332. cv::imshow("orig", frame);
  333. if (!prevgray.empty())
  334. {
  335. /*DISOpticalFlow*/
  336. /*main function of DISOpticalFlow*/
  337. algorithm->calc(prevgray, gray, flow);
  338. split(flow, flow_uv);
  339. multiply(flow_uv[1], -1, flow_uv[1]);
  340. cartToPolar(flow_uv[0], flow_uv[1], mag, ang, true);
  341. normalize(mag, mag, 0, 1, NORM_MINMAX);
  342. hsv_split[0] = ang;
  343. hsv_split[1] = mag;
  344. hsv_split[2] = Mat::ones(ang.size(), ang.type());
  345. merge(hsv_split, 3, hsv);
  346. cvtColor(hsv, rgb, COLOR_HSV2BGR);
  347. cv::Mat rgbU;
  348. rgb.convertTo(rgbU, CV_8UC3, 255, 0);
  349. cv::imshow("DISOpticalFlow", rgbU);
  350. Mat rgbU_b = rgbU.clone();
  351. Mat split_dis[3];
  352. split(rgbU_b, split_dis);
  353. split_dis[2] = prevgray;
  354. Mat merge_dis;
  355. merge(split_dis, 3, merge_dis);
  356. cv::imshow("DISOpticalFlow_mask", merge_dis);
  357. /*Farneback*/
  358. cv::calcOpticalFlowFarneback(prevgray, gray, flow_Farneback, 0.5, 3,15, 3, 5, 1.2, 0);
  359. split(flow_Farneback, flow_uv_Farneback);
  360. multiply(flow_uv_Farneback[1], -1, flow_uv_Farneback[1]);
  361. cartToPolar(flow_uv_Farneback[0], flow_uv_Farneback[1], mag_Farneback, ang_Farneback, true);
  362. normalize(mag_Farneback, mag_Farneback, 0, 1, NORM_MINMAX);
  363. hsv_split_Farneback[0] = ang_Farneback;
  364. hsv_split_Farneback[1] = mag_Farneback;
  365. hsv_split_Farneback[2] = Mat::ones(ang_Farneback.size(), ang_Farneback.type());
  366. merge(hsv_split_Farneback, 3, hsv_Farneback);
  367. cvtColor(hsv_Farneback, rgb_Farneback, COLOR_HSV2BGR);
  368. cv::Mat rgbU_Farneback;
  369. rgb_Farneback.convertTo(rgbU_Farneback, CV_8UC3, 255, 0);
  370. cv::imshow("FlowFarneback", rgbU_Farneback);
  371. Mat rgbU_Farneback_b = rgbU_Farneback.clone();
  372. Mat split_Fb[3];
  373. split(rgbU_Farneback_b, split_Fb);
  374. split_Fb[2] = prevgray;
  375. Mat merge_Fb;
  376. merge(split_Fb, 3, merge_Fb);
  377. cv::imshow("FlowFarneback_mask", merge_Fb);
  378. cv::waitKey(1);
  379. }
  380. std::swap(prevgray, gray);
  381. }
  382. #endif
  383. //showTimer->start(25);
  384. #if 0
  385. //TODO:后期优化,内部区分是读图片还是视频
  386. QImage image = QImage(filename);
  387. if(!image.isNull())
  388. {
  389. ui->statusBar->showMessage(tr("Open image Success!"));
  390. }
  391. else
  392. {
  393. ui->statusBar->showMessage(tr("Open image Failed!"));
  394. }
  395. #endif
  396. }
  397. void MainWindow::ReadFrame()
  398. {
  399. }
  400. void MainWindow::on_pBtn_CloseFile_clicked()
  401. {
  402. showTimer->stop();
  403. capture.release();
  404. frame.release();
  405. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/344987
推荐阅读
相关标签
  

闽ICP备14008679号