├── Screenshot ├── .directory ├── Screenshot_1.png ├── Screenshot_2.png ├── Screenshot_3.png └── Screenshot_4.png ├── README.md ├── .gitignore ├── main.cpp ├── ObjectCounting.pro ├── objectcounter.h └── objectcounter.cpp /Screenshot/.directory: -------------------------------------------------------------------------------- 1 | [Dolphin] 2 | PreviewsShown=true 3 | Timestamp=2017,11,23,17,34,22 4 | Version=3 5 | -------------------------------------------------------------------------------- /Screenshot/Screenshot_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iegrsy/Object-Counting/HEAD/Screenshot/Screenshot_1.png -------------------------------------------------------------------------------- /Screenshot/Screenshot_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iegrsy/Object-Counting/HEAD/Screenshot/Screenshot_2.png -------------------------------------------------------------------------------- /Screenshot/Screenshot_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iegrsy/Object-Counting/HEAD/Screenshot/Screenshot_3.png -------------------------------------------------------------------------------- /Screenshot/Screenshot_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iegrsy/Object-Counting/HEAD/Screenshot/Screenshot_4.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Object-Counting 2 | Object traking and counting with opencv. 3 | ![alt text](https://github.com/iegrsy/Object-Counting/blob/master/Screenshot/Screenshot_1.png) 4 | ![alt text](https://github.com/iegrsy/Object-Counting/blob/master/Screenshot/Screenshot_2.png) 5 | ![alt text](https://github.com/iegrsy/Object-Counting/blob/master/Screenshot/Screenshot_3.png) 6 | ![alt text](https://github.com/iegrsy/Object-Counting/blob/master/Screenshot/Screenshot_4.png) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ objects and libs 2 | 3 | *.slo 4 | *.lo 5 | *.o 6 | *.a 7 | *.la 8 | *.lai 9 | *.so 10 | *.dll 11 | *.dylib 12 | 13 | # Qt-es 14 | 15 | /.qmake.cache 16 | /.qmake.stash 17 | *.pro.user 18 | *.pro.user.* 19 | *.qbs.user 20 | *.qbs.user.* 21 | *.moc 22 | moc_*.cpp 23 | moc_*.h 24 | qrc_*.cpp 25 | ui_*.h 26 | Makefile* 27 | *build-* 28 | 29 | # QtCreator 30 | 31 | *.autosave 32 | 33 | # QtCtreator Qml 34 | *.qmlproject.user 35 | *.qmlproject.user.* 36 | 37 | # QtCtreator CMake 38 | CMakeLists.txt.user* 39 | 40 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "objectcounter.h" 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | 8 | ObjectCounter oc; 9 | #if 1 10 | VideoCapture capture("../../video.avi"); 11 | #else 12 | VideoCapture capture(0); 13 | #endif 14 | Mat frame; 15 | if( capture.isOpened() ){ 16 | while( true ){ 17 | if(capture.read(frame)){ 18 | oc.movemontDetection(frame); 19 | }else{ 20 | qDebug(" --(!) No captured frame -- Break!"); 21 | break; 22 | } 23 | 24 | int c = waitKey(10); 25 | if( (char)c == 'q' ){ 26 | break; 27 | }else if( (char)c == 'p' ){ 28 | while (true) { 29 | int c = waitKey(10); 30 | if( (char)c == 'p' ){ 31 | break; 32 | } 33 | } 34 | } 35 | } 36 | destroyAllWindows(); 37 | } 38 | return 0; 39 | //return a.exec(); 40 | } 41 | 42 | -------------------------------------------------------------------------------- /ObjectCounting.pro: -------------------------------------------------------------------------------- 1 | QT += core gui network printsupport 2 | 3 | TARGET = ObjectCounting 4 | CONFIG += console 5 | CONFIG -= app_bundle 6 | 7 | TEMPLATE = app 8 | 9 | SOURCES += main.cpp \ 10 | objectcounter.cpp 11 | 12 | HEADERS += \ 13 | objectcounter.h 14 | 15 | INCLUDEPATH += /usr/local/include/opencv 16 | 17 | LIBS += -L/usr/local/lib \ 18 | -lopencv_stitching \ 19 | -lopencv_superres \ 20 | -lopencv_videostab \ 21 | -lopencv_photo \ 22 | -lopencv_aruco \ 23 | -lopencv_bgsegm \ 24 | -lopencv_bioinspired \ 25 | -lopencv_ccalib \ 26 | -lopencv_cvv \ 27 | -lopencv_dpm \ 28 | -lopencv_face \ 29 | -lopencv_freetype \ 30 | -lopencv_fuzzy \ 31 | -lopencv_hdf \ 32 | -lopencv_img_hash \ 33 | -lopencv_line_descriptor \ 34 | -lopencv_optflow \ 35 | -lopencv_reg \ 36 | -lopencv_rgbd \ 37 | -lopencv_saliency \ 38 | -lopencv_stereo \ 39 | -lopencv_structured_light \ 40 | -lopencv_phase_unwrapping \ 41 | -lopencv_surface_matching \ 42 | -lopencv_tracking \ 43 | -lopencv_datasets \ 44 | -lopencv_text \ 45 | -lopencv_dnn \ 46 | -lopencv_plot \ 47 | -lopencv_xfeatures2d \ 48 | -lopencv_shape \ 49 | -lopencv_video \ 50 | -lopencv_ml \ 51 | -lopencv_ximgproc \ 52 | -lopencv_calib3d \ 53 | -lopencv_features2d \ 54 | -lopencv_highgui \ 55 | -lopencv_videoio \ 56 | -lopencv_flann \ 57 | -lopencv_xobjdetect \ 58 | -lopencv_imgcodecs \ 59 | -lopencv_objdetect \ 60 | -lopencv_xphoto \ 61 | -lopencv_imgproc \ 62 | -lopencv_core 63 | -------------------------------------------------------------------------------- /objectcounter.h: -------------------------------------------------------------------------------- 1 | #ifndef OBJECTCOUNTER_H 2 | #define OBJECTCOUNTER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "opencv2/video/background_segm.hpp" 9 | #include "opencv2/features2d/features2d.hpp" 10 | #include "opencv2/objdetect/objdetect.hpp" 11 | #include "opencv2/xfeatures2d/nonfree.hpp" 12 | #include "opencv2/objdetect/objdetect.hpp" 13 | #include "opencv2/highgui/highgui.hpp" 14 | #include "opencv2/imgproc/imgproc.hpp" 15 | #include "opencv2/calib3d/calib3d.hpp" 16 | #include "opencv2/imgproc/imgproc_c.h" 17 | #include "opencv2/video/tracking.hpp" 18 | #include "opencv2/core/core.hpp" 19 | #include "opencv2/imgcodecs.hpp" 20 | #include "opencv2/tracking.hpp" 21 | #include "opencv2/core/ocl.hpp" 22 | #include "opencv2/imgproc.hpp" 23 | #include "opencv2/highgui.hpp" 24 | #include 25 | #include "opencv2/core.hpp" 26 | #include "highgui.h" 27 | #include "cv.h" 28 | 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | using namespace std; 37 | using namespace cv; 38 | using namespace cv::xfeatures2d; 39 | 40 | class ObjectCounter 41 | { 42 | public: 43 | ObjectCounter(); 44 | 45 | void imgShow(QImage img); 46 | void movemontDetection(const Mat &img); 47 | void init(); 48 | private: 49 | Mat frameOriginal,frame1, knn; 50 | Mat grayImage1,grayImage2; 51 | Mat differenceImage, thresholdImage; 52 | Ptr pKNN; 53 | bool isDebugmod, isCountmod, isDrawingmod, isSettingmod; 54 | }; 55 | 56 | #endif // OBJECTCOUNTER_H 57 | -------------------------------------------------------------------------------- /objectcounter.cpp: -------------------------------------------------------------------------------- 1 | #include "objectcounter.h" 2 | 3 | static int SENSITIVITY_VALUE = 150; 4 | static int BLUR_SIZE = 15; 5 | static int CLOSE_VALUE = 2500; 6 | static int MIN_AREA = 1000; 7 | 8 | static bool isFirst = false; 9 | static int s_slider = SENSITIVITY_VALUE; 10 | static int b_slider = BLUR_SIZE; 11 | static int c_slider = CLOSE_VALUE; 12 | static int m_slider = MIN_AREA; 13 | 14 | static int slider_max = 200; 15 | static void on_trackbar(int, void*){ 16 | SENSITIVITY_VALUE = s_slider; 17 | BLUR_SIZE = b_slider; 18 | CLOSE_VALUE = c_slider; 19 | MIN_AREA = m_slider; 20 | } 21 | static void mypause(){ 22 | Q_UNUSED(mypause) 23 | while (true) 24 | if( (char)waitKey(10) == 'p' ) 25 | break; 26 | } 27 | 28 | static Rect countRect; 29 | static Point rectStart; 30 | static Point rectEnd; 31 | static Point lineStart; 32 | static Point lineEnd; 33 | 34 | static int objectUpCount; 35 | static int objectDownCount; 36 | 37 | static bool intersection(Point2f o1, Point2f p1, Point2f o2, Point2f p2, Point2f &r){ 38 | Q_UNUSED(intersection) 39 | 40 | Point2f x = o2 - o1; 41 | Point2f d1 = p1 - o1; 42 | Point2f d2 = p2 - o2; 43 | 44 | float cross = d1.x*d2.y - d1.y*d2.x; 45 | if (abs(cross) < /*EPS*/1e-8) 46 | return false; 47 | 48 | double t1 = (x.x * d2.y - x.y * d2.x)/cross; 49 | r = o1 + d1 * t1; 50 | return true; 51 | } 52 | 53 | static int isPosition(Point a, Point b, Point c){ 54 | int s = ((b.x - a.x)*(c.y - a.y) - (b.y - a.y)*(c.x - a.x)); 55 | if(s > 0) 56 | return -1; 57 | else if(s == 0) 58 | return 0; 59 | else if(s < 0) 60 | return 1; 61 | else 62 | return 2; 63 | } 64 | class _objectTracking{ 65 | public: 66 | //tracking methods: {"BOOSTING", "MIL", "KCF", "TLD","MEDIANFLOW", "GOTURN"}; 67 | Ptr tracker; 68 | _objectTracking(){ 69 | tracker = TrackerKCF::create(); 70 | } 71 | 72 | void init(Mat frame,Rect2d box){ 73 | tracker->init(frame, box); 74 | } 75 | 76 | bool update(Mat frame, Rect2d &box){ 77 | return tracker->update(frame, box); 78 | } 79 | }; 80 | 81 | class _objectFollow{ 82 | public: 83 | _objectFollow(){ 84 | lastkey = 0; 85 | loopCount = 0; 86 | } 87 | 88 | void setPoint(Point mp, Mat mt){ 89 | if(objects.isEmpty()){ 90 | addObject(); 91 | addObjectPoint(lastkey, mp, mt); 92 | } 93 | else{ 94 | int ck = findCloseObject(mp, mt); 95 | if (objects.contains(ck)) 96 | addObjectPoint(ck, mp, mt); 97 | //qDebug()< >::iterator i; 112 | for(i = objects.begin(); i != objects.end(); ++i){ 113 | vector< vector > printVector; 114 | printVector.push_back(i.value().toVector().toStdVector()); 115 | polylines(img, printVector, false, Scalar((i.key()*28%75), ((i.key()*28%255)), ((i.key()*28%10+200))), 2, CV_AA); 116 | } 117 | } 118 | 119 | 120 | private: 121 | QHash > objects; 122 | QHash objectsState; 123 | QHash objectsPosState; 124 | QHash objectsLastMat; 125 | 126 | int lastkey; 127 | int loopCount; 128 | 129 | void addObject(){ 130 | QList tp; 131 | objects.insert(++lastkey, tp); 132 | objectsState.insert(lastkey, false); 133 | objectsPosState.insert(lastkey, 2); 134 | } 135 | 136 | void addObjectPoint(int key, Point mp, Mat mt){ 137 | QList tp = objects.value(key); 138 | tp.append(mp); 139 | if(tp.size() > 100) 140 | tp.removeFirst(); 141 | 142 | objects.insert(key,tp); 143 | objectsState.insert(key, true); 144 | Mat m; 145 | mt.copyTo(m); 146 | objectsLastMat.insert(key, m); 147 | 148 | lineCount(); 149 | clearHistory(); 150 | } 151 | 152 | int getObjectCount(){ 153 | return objects.size(); 154 | } 155 | 156 | int findCloseObject(Point mp, Mat mt){ 157 | #if 1 158 | double lastMax = DBL_MAX; 159 | if(getObjectCount() > 0){ 160 | int oi = 0; 161 | double minDisp; 162 | QHashIterator > i(objects); 163 | while(i.hasNext()){ 164 | i.next(); 165 | 166 | int dispt = compareImage(objectsLastMat.value(i.key()), mt); 167 | 168 | if(dispt < lastMax){ 169 | oi = i.key(); 170 | minDisp = dispt; 171 | } 172 | } 173 | 174 | if(minDisp > CLOSE_VALUE){ 175 | addObject(); 176 | addObjectPoint(lastkey, mp, mt); 177 | oi = lastkey; 178 | //qDebug()<<"add object: "<< minDisp << "oi: " << oi; 179 | } 180 | //qDebug()<< "min disp: " << minDisp; 181 | return oi; 182 | } 183 | return -1; 184 | #elif 0 185 | if(getObjectCount() > 0){ 186 | int lastMax = 0; 187 | int oi = 0; 188 | 189 | QHashIterator > i(objects); 190 | while(i.hasNext()){ 191 | i.next(); 192 | if(!objectsLastMat.contains(i.key())) 193 | break; 194 | 195 | int matchCount = compareImage(objectsLastMat.value(i.key()), mt); 196 | 197 | if(matchCount > lastMax){ 198 | oi = i.key(); 199 | lastMax = matchCount; 200 | } 201 | } 202 | if(lastMax < CLOSE_VALUE){ 203 | addObject(); 204 | addObjectPoint(lastkey, mp, mt); 205 | oi = lastkey; 206 | } 207 | return oi; 208 | } 209 | return -1; 210 | #elif 0 211 | double lastMax = DBL_MAX; 212 | if(getObjectCount() > 0){ 213 | int oi = 0; 214 | double minDisp; 215 | QHashIterator > i(objects); 216 | while(i.hasNext()){ 217 | i.next(); 218 | Point p2(i.value().last().x, i.value().last().y); 219 | double dispt = norm(Mat(mp), Mat(p2)); 220 | 221 | if(dispt < lastMax){ 222 | oi = i.key(); 223 | minDisp = dispt; 224 | } 225 | } 226 | if(minDisp > CLOSE_VALUE){ 227 | addObject(); 228 | addObjectPoint(lastkey, mp, mt); 229 | oi = lastkey; 230 | } 231 | //qDebug()<< "min disp: " << minDisp; 232 | return oi; 233 | } 234 | #endif 235 | } 236 | 237 | void clearHistory(){ 238 | loopCount++; 239 | if(loopCount > 50){ 240 | QHash::iterator i; 241 | for (i = objectsState.begin(); i != objectsState.end(); ++i) 242 | if(!i.value()) 243 | objects.remove(i.key()); 244 | 245 | QHash::iterator i1; 246 | for (i1 = objectsState.begin(); i1 != objectsState.end(); ++i1) 247 | objectsState.insert(i1.key(),false); 248 | 249 | loopCount = 0; 250 | } 251 | } 252 | 253 | void lineCount(){ 254 | QHashIterator > i(objects); 255 | while(i.hasNext()){ 256 | i.next(); 257 | if(i.value().size() > 2) 258 | if(countRect.contains(i.value().first())){ 259 | int p = isPosition(lineStart, lineEnd, i.value().last()); 260 | int os = objectsPosState.value(i.key()); 261 | //qDebug()< detector = SURF::create(); 339 | detector->setHessianThreshold(minHessian); 340 | 341 | std::vector keypoints_1, keypoints_2; 342 | 343 | Mat descriptors_1, descriptors_2; 344 | detector->detectAndCompute( img_1, Mat(), keypoints_1, descriptors_1 ); 345 | detector->detectAndCompute( img_2, Mat(), keypoints_2, descriptors_2 ); 346 | 347 | //-- Step 2: Matching descriptor vectors using FLANN matcher 348 | 349 | FlannBasedMatcher matcher; 350 | std::vector< DMatch > matches; 351 | 352 | if(descriptors_1.type()!=CV_32F) { 353 | descriptors_1.convertTo(descriptors_1, CV_32F); 354 | } 355 | if(descriptors_2.type()!=CV_32F) { 356 | descriptors_2.convertTo(descriptors_2, CV_32F); 357 | } 358 | 359 | matcher.match( descriptors_1, descriptors_2, matches ); 360 | 361 | double max_dist = 0; double min_dist = 100; 362 | 363 | //-- Quick calculation of max and min distances between keypoints 364 | for( int i = 0; i < descriptors_1.rows; i++ ){ 365 | double dist = matches[i].distance; 366 | if( dist < min_dist ) min_dist = dist; 367 | if( dist > max_dist ) max_dist = dist; 368 | } 369 | printf("-- Max dist : %f \n", max_dist ); 370 | printf("-- Min dist : %f \n", min_dist ); 371 | //-- Draw only "good" matches (i.e. whose distance is less than 2*min_dist, 372 | //-- or a small arbitary value ( 0.02 ) in the event that min_dist is very 373 | //-- small) 374 | //-- PS.- radiusMatch can also be used here. 375 | std::vector< DMatch > good_matches; 376 | for( int i = 0; i < descriptors_1.rows; i++ ) 377 | { if( matches[i].distance <= max(2*min_dist, 0.02) ) 378 | { good_matches.push_back( matches[i]); } 379 | } 380 | //-- Draw only "good" matches 381 | Mat img_matches; 382 | drawMatches( img_1, keypoints_1, img_2, keypoints_2, 383 | good_matches, img_matches, Scalar::all(-1), Scalar::all(-1), 384 | vector(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS ); 385 | //-- Show detected matches 386 | imshow( "Good Matches", img_matches ); 387 | for( int i = 0; i < (int)good_matches.size(); i++ ) 388 | { printf( "-- Good Match [%d] Keypoint 1: %d -- Keypoint 2: %d \n", i, good_matches[i].queryIdx, good_matches[i].trainIdx ); } 389 | qDebug()<apply(frame1,knn); 472 | 473 | if(isDebugmod) 474 | imshow("Movemont Detection: KNN", knn); 475 | 476 | blur(knn, knn, Size(BLUR_SIZE, BLUR_SIZE)); 477 | threshold(knn, knn, SENSITIVITY_VALUE, 255, THRESH_BINARY); 478 | knn.convertTo(knn, CV_8U); 479 | if(isDebugmod) 480 | imshow("Movemont Detection: KNN2", knn); 481 | 482 | vector< vector > contours; 483 | findContours( knn, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE ); 484 | 485 | int cSize = contours.size(); 486 | if (!contours.empty()){ 487 | vector contour_moments(cSize); 488 | vector mass_centers(cSize); 489 | 490 | bool in = false; 491 | for (int i = 0; i < cSize; ++i) { 492 | double area = contourArea(contours[i]); 493 | if (area > MIN_AREA){ 494 | contour_moments[i] = moments(contours[i], false); 495 | mass_centers[i] = Point(contour_moments[i].m10 / contour_moments[i].m00, contour_moments[i].m01 / contour_moments[i].m00); 496 | 497 | if(countRect.contains(mass_centers[i])){ 498 | // Draw target 499 | Rect roi = boundingRect(contours[i]); 500 | //drawContours(frame1, contours, i, Scalar(0, 0, 255)); 501 | rectangle(frame1, roi, Scalar(0, 0, 255)); 502 | drawTarget(mass_centers[i],frame1,i); 503 | 504 | if(isCountmod){ 505 | // Draw footprint 506 | Mat car = frameOriginal(roi); 507 | _ofollow.setPoint(mass_centers[i], car); 508 | if(isDrawingmod) 509 | _ofollow.drawFootprints(frame1); 510 | } 511 | in = true; 512 | } 513 | } 514 | } 515 | if(!in) 516 | _ofollow.clearObjects(); 517 | }else{ 518 | _ofollow.clearObjects(); 519 | } 520 | 521 | putText(frame1, QString::number(cSize).toStdString(), cvPoint(frame1.cols-30, 30), 522 | FONT_HERSHEY_COMPLEX_SMALL, 0.8, cvScalar(200,200,250), 1, CV_AA); 523 | putText(frame1, QString("Up count: %1").arg(QString::number(objectUpCount)).toStdString(), Point(20,30), 524 | FONT_HERSHEY_COMPLEX_SMALL, 0.8, cvScalar(200,200,250), 1, CV_AA); 525 | putText(frame1, QString("Down count: %1").arg(QString::number(objectDownCount)).toStdString(), Point(20,60), 526 | FONT_HERSHEY_COMPLEX_SMALL, 0.8, cvScalar(200,200,250), 1, CV_AA); 527 | if(isCountmod){ 528 | countRect = Rect(rectStart,rectEnd); 529 | rectangle(frame1, rectStart, rectEnd, Scalar(76,255,0), 3); 530 | line(frame1, lineStart, lineEnd, Scalar(76,10,255), 3); 531 | } 532 | 533 | imshow("Movemont Detection", frame1); 534 | 535 | if(!isFirst){ 536 | if(isSettingmod){ 537 | createTrackbar("SENSITIVITY_VALUE", "Movemont Detection", &s_slider, slider_max, on_trackbar); 538 | createTrackbar("BLUR_SIZE", "Movemont Detection", &b_slider, slider_max, on_trackbar); 539 | createTrackbar("CLOSE_VALUE", "Movemont Detection", &c_slider, 50000, on_trackbar); 540 | createTrackbar("MIN_AREA", "Movemont Detection", &m_slider, (int) (frame1.rows * frame1.cols), on_trackbar); 541 | } 542 | if(isCountmod) 543 | setMouseCallback("Movemont Detection", onmouse, &frame1); 544 | 545 | isFirst = true; 546 | } 547 | } 548 | --------------------------------------------------------------------------------