├── ObjectDetectionWithTrack.cpp └── README.md /ObjectDetectionWithTrack.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | using namespace cv; 13 | using namespace dnn; 14 | using namespace std; 15 | 16 | // Initialize the parameters 17 | vector trackerTypes = {"BOOSTING", "MIL", "KCF", "TLD", "MEDIANFLOW", "GOTURN", "MOSSE", "CSRT"}; 18 | float confThreshold = 0.5; // Confidence threshold 19 | float nmsThreshold = 0.4; // Non-maximum suppression threshold 20 | int inpWidth = 416; // Width of network's input image 21 | int inpHeight = 416; // Height of network's input image 22 | vector classes; 23 | 24 | // Draw the predicted bounding box 25 | void drawPred(int classId, float conf, int left, int top, int right, int bottom, Mat& frame) 26 | { 27 | //Draw a rectangle displaying the bounding box 28 | rectangle(frame, Point(left, top), Point(right, bottom), Scalar(255, 178, 50), 3); 29 | 30 | //Get the label for the class name and its confidence 31 | string label = format("%.2f", conf); 32 | if (!classes.empty()) 33 | { 34 | CV_Assert(classId < (int)classes.size()); 35 | label = classes[classId] + ":" + label; 36 | } 37 | 38 | //Display the label at the top of the bounding box 39 | int baseLine; 40 | Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine); 41 | top = max(top, labelSize.height); 42 | rectangle(frame, Point(left, top - round(1.5*labelSize.height)), Point(left + round(1.5*labelSize.width), top + baseLine), Scalar(255, 255, 255), FILLED); 43 | putText(frame, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0,0,0),1); 44 | } 45 | 46 | vector postprocess(Mat& frame, const vector& outs) 47 | { 48 | vector classIds; 49 | vector confidences; 50 | vector boxes; 51 | 52 | for (size_t i = 0; i < outs.size(); ++i) 53 | { 54 | // Scan through all the bounding boxes output from the network and keep only the 55 | // ones with high confidence scores. Assign the box's class label as the class 56 | // with the highest score for the box. 57 | float* data = (float*)outs[i].data; 58 | for (int j = 0; j < outs[i].rows; ++j, data += outs[i].cols) 59 | { 60 | Mat scores = outs[i].row(j).colRange(5, outs[i].cols); 61 | Point classIdPoint; 62 | double confidence; 63 | // Get the value and location of the maximum score 64 | minMaxLoc(scores, 0, &confidence, 0, &classIdPoint); 65 | if (confidence > confThreshold) 66 | { 67 | int centerX = (int)(data[0] * frame.cols); 68 | int centerY = (int)(data[1] * frame.rows); 69 | int width = (int)(data[2] * frame.cols); 70 | int height = (int)(data[3] * frame.rows); 71 | int left = centerX - width / 2; 72 | int top = centerY - height / 2; 73 | 74 | classIds.push_back(classIdPoint.x); 75 | confidences.push_back((float)confidence); 76 | boxes.push_back(Rect(left, top, width, height)); 77 | } 78 | } 79 | } 80 | 81 | // Perform non maximum suppression to eliminate redundant overlapping boxes with 82 | // lower confidences 83 | vector indices; 84 | NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices); 85 | for (size_t i = 0; i < indices.size(); ++i) 86 | { 87 | int idx = indices[i]; 88 | Rect box = boxes[idx]; 89 | drawPred(classIds[idx], confidences[idx], box.x, box.y, 90 | box.x + box.width, box.y + box.height, frame); 91 | } 92 | return boxes; 93 | } 94 | // Get the names of the output layers 95 | vector getOutputsNames(const Net& net) 96 | { 97 | static vector names; 98 | if (names.empty()) 99 | { 100 | //Get the indices of the output layers, i.e. the layers with unconnected outputs 101 | vector outLayers = net.getUnconnectedOutLayers(); 102 | 103 | //get the names of all the layers in the network 104 | vector layersNames = net.getLayerNames(); 105 | 106 | // Get the names of the output layers in names 107 | names.resize(outLayers.size()); 108 | for (size_t i = 0; i < outLayers.size(); ++i) 109 | names[i] = layersNames[outLayers[i] - 1]; 110 | } 111 | return names; 112 | } 113 | 114 | //用随机的颜色来画这些跟踪的框 115 | void getRandomColors(vector &color, int numColors) 116 | { 117 | RNG rng(0); 118 | for(int i = 0; i createTrackerByName(string trackerType) 125 | { 126 | Ptr tracker; 127 | if (trackerType == trackerTypes[0]) 128 | tracker = TrackerBoosting::create(); 129 | else if (trackerType == trackerTypes[1]) 130 | tracker = TrackerMIL::create(); 131 | else if (trackerType == trackerTypes[2]) 132 | tracker = TrackerKCF::create(); 133 | else if (trackerType == trackerTypes[3]) 134 | tracker = TrackerTLD::create(); 135 | else if (trackerType == trackerTypes[4]) 136 | tracker = TrackerMedianFlow::create(); 137 | else if (trackerType == trackerTypes[5]) 138 | tracker = TrackerGOTURN::create(); 139 | else if (trackerType == trackerTypes[6]) 140 | tracker = TrackerMOSSE::create(); 141 | else if (trackerType == trackerTypes[7]) 142 | tracker = TrackerCSRT::create(); 143 | else { 144 | cout << "Incorrect tracker name" << endl; 145 | cout << "Available trackers are: " << endl; 146 | for (vector::iterator it = trackerTypes.begin() ; it != trackerTypes.end(); ++it) 147 | std::cout << " " << *it << endl; 148 | } 149 | return tracker; 150 | } 151 | int main() 152 | { 153 | // Load names of classes 154 | string classesFile = "/Users/tangxi/cpp/learnOpenCV/utils/coco.names"; 155 | ifstream ifs(classesFile.c_str()); 156 | string line; 157 | while (getline(ifs, line)) classes.push_back(line); 158 | 159 | // Give the configuration and weight files for the model 160 | String modelConfiguration = "/Users/tangxi/cpp/learnOpenCV/utils/yolov3.cfg"; 161 | String modelWeights = "/Users/tangxi/cpp/learnOpenCV/utils/yolov3.weights"; 162 | 163 | // Load the network 164 | Net net = readNetFromDarknet(modelConfiguration, modelWeights); 165 | net.setPreferableBackend(DNN_BACKEND_OPENCV); 166 | net.setPreferableTarget(DNN_TARGET_CPU); 167 | string filename = "/Users/tangxi/cpp/learnOpenCV/utils/run.mp4"; 168 | VideoCapture cap(filename); 169 | if(!cap.isOpened()) 170 | { 171 | cerr<<"cannot open ..."< multiTracker = MultiTracker::create(); 177 | vectorcolors; 178 | vectorbboxes; 179 | while(true) 180 | { 181 | cap>>frame; 182 | if (frame.empty()) { 183 | cout << "Done processing !!!" << endl; 184 | waitKey(3000); 185 | break; 186 | } 187 | if(i % 5 == 0) 188 | { 189 | // Create a 4D blob from a frame. 190 | blobFromImage(frame, blob, 1/255.0, Size(inpWidth, inpHeight), Scalar(0,0,0), true, false); 191 | 192 | //Sets the input to the network 193 | net.setInput(blob); 194 | 195 | // Runs the forward pass to get output of the output layers 196 | vector outs; 197 | net.forward(outs, getOutputsNames(net)); 198 | bboxes = postprocess(frame,outs); 199 | if(bboxes.size()<1) 200 | continue ; 201 | 202 | getRandomColors(colors, bboxes.size()); 203 | //为每个目标添加跟踪器 204 | for(int i = 0; iadd(createTrackerByName(trackerType),frame,Rect2d(bboxes[i])); 207 | } 208 | } 209 | //跟踪 210 | multiTracker->update(frame); 211 | 212 | for(size_t i = 0; igetObjects().size();i++) 213 | { 214 | rectangle(frame, multiTracker->getObjects()[i], colors[i],2,1); 215 | } 216 | 217 | imshow("MultiTracker",frame); 218 | if(waitKey(1)==27) 219 | break; 220 | i++; 221 | 222 | } 223 | return 0; 224 | } 225 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Object-detection-with-tracking 2 | ## Introduction 3 | 视频目标检测会比较不稳定,而跟踪会比较稳定。因此在一段视频中每隔一段时间进行一次目标检测,对检测到的目标进行跟踪,增加实时目标检测的稳定性 4 | ## Dependency 5 | - OpenCV 4.1.0 6 | - C++11 7 | - Mac OS High Sierra 10.13.6, Xcode 10.1 8 | 9 | ## Continue... 10 | 后续会添加代码注释和文档,以及代码结构优化. 11 | --------------------------------------------------------------------------------