opencv学习之路(37)、运动物体检测(二)
一、运动物体轮廓椭圆拟合及中心
1 #include "opencv2/opencv.hpp" 2 #include<iostream> 3 using namespace std; 4 using namespace cv; 5 6 Mat MoveDetect(Mat frame1, Mat frame2) 7 { 8 Mat result = frame2.clone(); 9 Mat gray1, gray2; 10 cvtColor(frame1, gray1, CV_BGR2GRAY); 11 cvtColor(frame2, gray2, CV_BGR2GRAY); 12 13 Mat diff; 14 absdiff(gray1, gray2, diff); 15 imshow("absdiss", diff); 16 threshold(diff, diff, 45, 255, CV_THRESH_BINARY); 17 imshow("threshold", diff); 18 19 Mat element = getStructuringElement(MORPH_RECT, Size(3, 3)); 20 Mat element2 = getStructuringElement(MORPH_RECT, Size(25, 25)); 21 erode(diff, diff, element); 22 imshow("erode", diff); 23 24 dilate(diff, diff, element2); 25 imshow("dilate", diff); 26 27 vector<vector<Point>> contours; 28 vector<Vec4i> hierarcy; 29 //画椭圆及中心 30 findContours(diff, contours, hierarcy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); 31 cout<<"num="<<contours.size()<<endl; 32 vector<RotatedRect> box(contours.size()); 33 for(int i=0; i<contours.size(); i++) 34 { 35 box[i] = fitEllipse(Mat(contours[i])); 36 ellipse(result, box[i], Scalar(0, 255, 0), 2, 8); 37 circle(result, box[i].center, 3, Scalar(0, 0, 255), -1, 8); 38 } 39 return result; 40 } 41 42 void main() 43 { 44 VideoCapture cap("E://man.avi"); 45 if(!cap.isOpened()) //检查打开是否成功 46 return; 47 Mat frame; 48 Mat result; 49 Mat background; 50 int count=0; 51 while(1) 52 { 53 cap>>frame; 54 if(frame.empty()) 55 break; 56 else{ 57 count++; 58 if(count==1) 59 background = frame.clone(); //提取第一帧为背景帧 60 imshow("video", frame); 61 result = MoveDetect(background, frame); 62 imshow("result", result); 63 if(waitKey(50)==27) 64 break; 65 } 66 } 67 cap.release(); 68 }
和上一篇文章代码的不同点在30-38行,天台行人视频适合用背景减法处理,自行车视频适合帧差法处理
二、滤波方法去除噪声
上篇文章中使用腐蚀膨胀消除噪声,这次使用滤波方法去除噪声
中值滤波
//二值化后使用中值滤波+膨胀
Mat element = getStructuringElement(MORPH_RECT, Size(11, 11)); medianBlur(diff, diff, 5);//中值滤波 imshow("medianBlur", diff); dilate(diff, diff, element); imshow("dilate", diff);
均值滤波
#include "opencv2/opencv.hpp" #include<iostream> using namespace std; using namespace cv; //int to string helper function string intToString(int number) { stringstream ss; ss << number; return ss.str(); } Mat MoveDetect(Mat background, Mat img) { Mat result = img.clone(); Mat gray1, gray2; cvtColor(background, gray1, CV_BGR2GRAY); cvtColor(img, gray2, CV_BGR2GRAY); Mat diff; absdiff(gray1, gray2, diff); threshold(diff, diff, 20, 255, CV_THRESH_BINARY); imshow("threshold", diff); blur(diff, diff, Size(10, 10));//均值滤波 imshow("blur", diff); vector<vector<Point>> contours; vector<Vec4i> hierarcy; findContours(diff, contours, hierarcy, CV_RETR_EXTERNAL, CHAIN_APPROX_NONE); //查找轮廓 vector<Rect> boundRect(contours.size()); //定义外接矩形集合 //drawContours(img2, contours, -1, Scalar(0, 0, 255), 1, 8); //绘制轮廓 int x0=0, y0=0, w0=0, h0=0; for(int i=0; i<contours.size(); i++) { boundRect[i] = boundingRect((Mat)contours[i]); //查找每个轮廓的外接矩形 x0 = boundRect[i].x; //获得第i个外接矩形的左上角的x坐标 y0 = boundRect[i].y; //获得第i个外接矩形的左上角的y坐标 w0 = boundRect[i].width; //获得第i个外接矩形的宽度 h0 = boundRect[i].height; //获得第i个外接矩形的高度 //rectangle(result, Point(x0, y0), Point(x0+w0, y0+h0), Scalar(0, 255, 0), 2, 8); //绘制第i个外接矩形 circle(result, Point(x0+w0/2, y0+h0/2), 15, Scalar(0, 255, 0), 2, 8); line(result, Point(x0+w0/2-15, y0+h0/2), Point(x0+w0/2+15, y0+h0/2), Scalar(0, 255, 0), 2, 8); line(result, Point(x0+w0/2, y0+h0/2-10), Point(x0+w0/2, y0+h0/2+15), Scalar(0, 255, 0), 2, 8); putText(result,"(" + intToString(x0+w0/2)+","+intToString(y0+h0/2)+")",Point(x0+w0/2+15, y0+h0/2), 1, 1,Scalar(255,0,0),2); } return result; } void main() { VideoCapture cap("E://ball.avi"); if(!cap.isOpened()) //检查打开是否成功 return; Mat frame; Mat result; Mat background; int count=0; while(1) { cap>>frame; if(frame.empty()) break; else{ count++; if(count==1) background = frame.clone(); //提取第一帧为背景帧 imshow("video", frame); result = MoveDetect(background, frame); imshow("result", result); if(waitKey(50)==27) break; } } cap.release(); }
三、轮廓筛选去除噪声(效果挺好的)
//其余代码相同 int x0=0, y0=0, w0=0, h0=0; for(int i=0; i<contours.size(); i++) { boundRect[i] = boundingRect((Mat)contours[i]); //查找每个轮廓的外接矩形 x0 = boundRect[i].x; //获得第i个外接矩形的左上角的x坐标 y0 = boundRect[i].y; //获得第i个外接矩形的左上角的y坐标 w0 = boundRect[i].width; //获得第i个外接矩形的宽度 h0 = boundRect[i].height; //获得第i个外接矩形的高度 //筛选 if(w0>30 && h0>30) rectangle(result, Point(x0, y0), Point(x0+w0, y0+h0), Scalar(0, 255, 0), 2, 8); //绘制第i个外接矩形 }
四、运动轨迹绘制
#include "opencv2/opencv.hpp" #include<iostream> using namespace std; using namespace cv; Point center; Point fre_center;//存储前一帧中心坐标 int num=0; vector<Point> points; Mat MoveDetect(Mat background, Mat img) { Mat result = img.clone(); Mat gray1, gray2; cvtColor(background, gray1, CV_BGR2GRAY); cvtColor(img, gray2, CV_BGR2GRAY); Mat diff; absdiff(gray1, gray2, diff); imshow("absdiss", diff); threshold(diff, diff, 45, 255, CV_THRESH_BINARY); imshow("threshold", diff); Mat element = getStructuringElement(MORPH_RECT, Size(1, 1)); Mat element2 = getStructuringElement(MORPH_RECT, Size(9, 9)); erode(diff, diff, element); imshow("erode", diff); dilate(diff, diff, element2); imshow("dilate", diff); vector<vector<Point>> contours; vector<Vec4i> hierarcy; findContours(diff, contours, hierarcy, CV_RETR_EXTERNAL, CHAIN_APPROX_NONE); //查找轮廓 vector<Rect> boundRect(contours.size()); //定义外接矩形集合 //drawContours(img2, contours, -1, Scalar(0, 0, 255), 1, 8); //绘制轮廓 vector<RotatedRect> box(contours.size()); int x0=0, y0=0, w0=0, h0=0; for(int i=0; i<contours.size(); i++) { boundRect[i] = boundingRect((Mat)contours[i]); //查找每个轮廓的外接矩形 x0 = boundRect[i].x; //获得第i个外接矩形的左上角的x坐标 y0 = boundRect[i].y; //获得第i个外接矩形的左上角的y坐标 w0 = boundRect[i].width; //获得第i个外接矩形的宽度 h0 = boundRect[i].height; //获得第i个外接矩形的高度 if(w0>30 && h0>30)//筛选长宽大于30的轮廓 { num++; //rectangle(result, Point(x0, y0), Point(x0+w0, y0+h0), Scalar(0, 255, 0), 2, 8); //绘制第i个外接矩形 box[i] = fitEllipse(Mat(contours[i])); ellipse(result, box[i], Scalar(255, 0, 0), 2, 8); //椭圆轮廓 circle(result, box[i].center, 3, Scalar(0, 0, 255), -1, 8); //画中心 center = box[i].center;//当前帧的中心坐标 points.push_back(center);//中心塞进points向量集 if(num !=1) { //line(result, fre_center, center, Scalar(255, 0, 0), 2, 8); for(int j=0; j<points.size()-1; j++) line(result, points[j], points[j+1], Scalar(0, 255, 0), 2, 8); } //fre_center = center; } } return result; } void main() { VideoCapture cap("E://man.avi"); if(!cap.isOpened()) //检查打开是否成功 return; Mat frame; Mat background; Mat result; int count=0; while(1) { cap>>frame; if(!frame.empty()) { count++; if(count==1) background = frame.clone(); //提取第一帧为背景帧 imshow("video", frame); result = MoveDetect(background, frame); imshow("result", result); if(waitKey(30)==27) break; } else break; } cap.release(); }
五、车辆数量检测
1.帧差法检测运动目标
2.预处理:a.转灰度图,绝对值做差 b.二值化,腐蚀,中值滤波,膨胀 c.查找轮廓,筛选轮廓,绘制外接矩形,计数,输出
1 #include "opencv2/opencv.hpp" 2 #include<iostream> 3 using namespace std; 4 using namespace cv; 5 6 int CarNum = 0; 7 //int to string helper function 8 string intToString(int number) 9 { 10 //this function has a number input and string output 11 stringstream ss; 12 ss << number; 13 return ss.str(); 14 } 15 16 Mat MoveDetect(Mat frame1, Mat frame2) { 17 Mat result = frame2.clone(); 18 Mat gray1, gray2; 19 cvtColor(frame1, gray1, CV_BGR2GRAY); 20 cvtColor(frame2, gray2, CV_BGR2GRAY); 21 22 Mat diff; 23 absdiff(gray1, gray2, diff); 24 //imshow("absdiss", diff); 25 threshold(diff, diff, 25, 255, CV_THRESH_BINARY); 26 imshow("threshold", diff); 27 28 Mat element = getStructuringElement(MORPH_RECT, Size(3, 3)); 29 Mat element2 = getStructuringElement(MORPH_RECT, Size(19, 19)); 30 erode(diff, diff, element); 31 //imshow("erode", dst); 32 medianBlur(diff, diff, 3); 33 imshow("medianBlur", diff); 34 dilate(diff, diff, element2); 35 imshow("dilate", diff); 36 37 vector<vector<Point>> contours; 38 vector<Vec4i> hierarcy; 39 findContours(diff, contours, hierarcy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));//查找轮廓 40 vector<vector<Point>>contours_poly(contours.size()); 41 vector<Rect> boundRect(contours.size()); //定义外接矩形集合 42 //drawContours(img2, contours, -1, Scalar(0, 0, 255), 1, 8); //绘制轮廓 43 int x0 = 0, y0 = 0, w0 = 0, h0 = 0; 44 for (int i = 0; i<contours.size(); i++) 45 { 46 approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true);//对图像轮廓点进行多边形拟合:轮廓点组成的点集,输出的多边形点集,精度(即两个轮廓点之间的距离),输出多边形是否封闭 47 boundRect[i] = boundingRect(Mat(contours_poly[i])); 48 if (boundRect[i].width>55 && boundRect[i].width<180 && boundRect[i].height>55 && boundRect[i].height<180) {//轮廓筛选 49 x0 = boundRect[i].x; 50 y0 = boundRect[i].y; 51 w0 = boundRect[i].width; 52 h0 = boundRect[i].height; 53 54 rectangle(result, Point(x0, y0), Point(x0 + w0, y0 + h0), Scalar(0, 255, 0), 2, 8, 0); 55 if ((y0 + h0 / 2 + 1) >= 138 && (y0 + h0 / 2 - 1) <= 142) {//经过这条线(区间),车辆数量+1 56 CarNum++; 57 } 58 } 59 line(result, Point(0, 140), Point(568, 140), Scalar(0, 0, 255), 1, 8);//画红线 60 Point org(0, 35); 61 putText(result, "CarNum=" + intToString(CarNum), org, CV_FONT_HERSHEY_SIMPLEX, 0.8f, Scalar(0, 255, 0), 2); 62 } 63 return result; 64 } 65 66 void main() 67 { 68 VideoCapture cap("E://2.avi"); 69 if (!cap.isOpened()) //检查打开是否成功 70 return; 71 Mat frame; 72 Mat tmp; 73 Mat result; 74 int count = 0; 75 while (1) 76 { 77 cap >> frame; 78 if(frame.empty())//检查视频是否结束 79 break; 80 else{ 81 count++; 82 if (count == 1) 83 result = MoveDetect(frame, frame); 84 else result = MoveDetect(tmp, frame); 85 imshow("video", frame); 86 imshow("result", result); 87 tmp = frame.clone(); 88 if (waitKey(20) == 27) 89 break; 90 } 91 } 92 cap.release(); 93 }