Mercurial Hosting > traffic-intelligence
comparison c/feature-based-tracking.cpp @ 614:5e09583275a4
Merged Nicolas/trafficintelligence into default
author | Mohamed Gomaa <eng.m.gom3a@gmail.com> |
---|---|
date | Fri, 05 Dec 2014 12:13:53 -0500 |
parents | a3add9f751ef |
children | 045d05cef9d0 |
comparison
equal
deleted
inserted
replaced
598:11f96bd08552 | 614:5e09583275a4 |
---|---|
1 #include "Motion.hpp" | 1 #include "Motion.hpp" |
2 #include "Parameters.hpp" | 2 #include "Parameters.hpp" |
3 #include "cvutils.hpp" | 3 #include "cvutils.hpp" |
4 #include "utils.hpp" | 4 #include "utils.hpp" |
5 #include "InputVideoFileModule.h" | |
6 #include "InputFrameListModule.h" | |
5 | 7 |
6 #include "src/Trajectory.h" | 8 #include "src/Trajectory.h" |
7 #include "src/TrajectoryDBAccessList.h" | 9 #include "src/TrajectoryDBAccessList.h" |
8 #include "src/TrajectoryDBAccessBlob.h" | 10 #include "src/TrajectoryDBAccessBlob.h" |
9 | 11 |
14 #include "opencv2/highgui/highgui.hpp" | 16 #include "opencv2/highgui/highgui.hpp" |
15 #include "opencv2/objdetect/objdetect.hpp" | 17 #include "opencv2/objdetect/objdetect.hpp" |
16 | 18 |
17 #include <boost/shared_ptr.hpp> | 19 #include <boost/shared_ptr.hpp> |
18 #include <boost/foreach.hpp> | 20 #include <boost/foreach.hpp> |
21 #include <boost/filesystem.hpp> | |
19 | 22 |
20 #include <iostream> | 23 #include <iostream> |
21 #include <vector> | 24 #include <vector> |
22 #include <ctime> | 25 #include <ctime> |
26 #include <cmath> | |
23 | 27 |
24 using namespace std; | 28 using namespace std; |
25 using namespace cv; | 29 using namespace cv; |
30 namespace fs = boost::filesystem; | |
26 | 31 |
27 void drawMatchesRelative(const vector<KeyPoint>& train, const vector<KeyPoint>& query, std::vector<cv::DMatch>& matches, Mat& img) { | 32 void drawMatchesRelative(const vector<KeyPoint>& train, const vector<KeyPoint>& query, std::vector<cv::DMatch>& matches, Mat& img) { |
28 for (int i = 0; i < (int)matches.size(); i++) | 33 for (int i = 0; i < (int)matches.size(); i++) |
29 { | 34 { |
30 Point2f pt_new = query[matches[i].queryIdx].pt; | 35 Point2f pt_new = query[matches[i].queryIdx].pt; |
52 | 57 |
53 FeaturePointMatch(FeatureTrajectoryPtr _feature, const int& _pointNum): | 58 FeaturePointMatch(FeatureTrajectoryPtr _feature, const int& _pointNum): |
54 feature(_feature), pointNum(_pointNum) {} | 59 feature(_feature), pointNum(_pointNum) {} |
55 }; | 60 }; |
56 | 61 |
57 inline void saveFeatures(vector<FeatureTrajectoryPtr>& features, TrajectoryDBAccess<Point2f>& db, const string& positionsTableName, const string& velocitiesTableName, const unsigned int& minNFeatures = 0) { | 62 inline void saveFeatures(vector<FeatureTrajectoryPtr>& features, TrajectoryDBAccess<Point2f>& db, const string& positionsTableName, const string& velocitiesTableName) { |
58 /// \todo smoothing | 63 BOOST_FOREACH(FeatureTrajectoryPtr f, features) f->write(db, positionsTableName, velocitiesTableName); |
59 if (features.size() >= minNFeatures) { | 64 features.clear(); |
60 BOOST_FOREACH(FeatureTrajectoryPtr f, features) f->write(db, positionsTableName, velocitiesTableName); | |
61 features.clear(); | |
62 } | |
63 } | 65 } |
64 | 66 |
65 void trackFeatures(const KLTFeatureTrackingParameters& params) { | 67 void trackFeatures(const KLTFeatureTrackingParameters& params) { |
66 // BriefDescriptorExtractor brief(32); | 68 // BriefDescriptorExtractor brief(32); |
67 // const int DESIRED_FTRS = 500; | 69 // const int DESIRED_FTRS = 500; |
73 invHomography = homography.inv(); | 75 invHomography = homography.inv(); |
74 | 76 |
75 float minTotalFeatureDisplacement = params.nDisplacements*params.minFeatureDisplacement; | 77 float minTotalFeatureDisplacement = params.nDisplacements*params.minFeatureDisplacement; |
76 Size window = Size(params.windowSize, params.windowSize); | 78 Size window = Size(params.windowSize, params.windowSize); |
77 | 79 |
80 int interpolationMethod = -1; | |
81 if (params.interpolationMethod == 0) | |
82 interpolationMethod = INTER_NEAREST; | |
83 else if (params.interpolationMethod == 1) | |
84 interpolationMethod = INTER_LINEAR; | |
85 else if (params.interpolationMethod == 2) | |
86 interpolationMethod = INTER_CUBIC; | |
87 else if (params.interpolationMethod == 3) | |
88 interpolationMethod = INTER_LANCZOS4; | |
89 else | |
90 cout << "Unsupported option " << interpolationMethod << " for interpolation method" << endl; | |
91 | |
78 // BruteForceMatcher<Hamming> descMatcher; | 92 // BruteForceMatcher<Hamming> descMatcher; |
79 // vector<DMatch> matches; | 93 // vector<DMatch> matches; |
80 | 94 |
81 VideoCapture capture; | 95 boost::shared_ptr<InputFrameProviderIface> capture; |
82 Size videoSize; | 96 if (fs::is_directory(fs::path(params.videoFilename))) |
83 unsigned int nFrames = 0; | 97 capture = boost::shared_ptr<InputFrameListModule>(new InputFrameListModule(params.videoFilename)); |
84 capture.open(params.videoFilename); | 98 else if(!params.videoFilename.empty()) |
85 if(capture.isOpened()) { | 99 capture = boost::shared_ptr<InputVideoFileModule>(new InputVideoFileModule(params.videoFilename)); |
86 videoSize = Size(capture.get(CV_CAP_PROP_FRAME_WIDTH), capture.get(CV_CAP_PROP_FRAME_HEIGHT)); | 100 else |
87 nFrames = capture.get(CV_CAP_PROP_FRAME_COUNT); | 101 cout << "No valid input parameters" << endl; |
88 cout << "Video " << params.videoFilename << | 102 |
89 ": width=" << videoSize.width << | 103 if(!capture->isOpen()) { |
90 ", height=" << videoSize.height << | |
91 ", nframes=" << nFrames << endl; | |
92 } else { | |
93 cout << "Video filename " << params.videoFilename << " could not be opened. Exiting." << endl; | 104 cout << "Video filename " << params.videoFilename << " could not be opened. Exiting." << endl; |
94 exit(0); | 105 exit(0); |
95 } | 106 } |
96 // if (!capture.isOpened()) | 107 |
97 // { | 108 Size videoSize = capture->getSize(); |
98 // //help(argv); | 109 unsigned int nFrames = capture->getNbFrames(); |
99 // cout << "capture device " << argv[1] << " failed to open!" << endl; | 110 cout << "Video " << params.videoFilename << |
100 // return 1; | 111 ": width=" << videoSize.width << |
101 // } | 112 ", height=" << videoSize.height << |
102 | 113 ", nframes=" << nFrames << endl; |
114 | |
115 Mat map1, map2; | |
116 if (params.undistort) { | |
117 Mat intrinsicCameraMatrix = ::loadMat(params.intrinsicCameraFilename, " "); | |
118 Mat newIntrinsicCameraMatrix = intrinsicCameraMatrix.clone(); | |
119 videoSize = Size(static_cast<int>(round(videoSize.width*params.undistortedImageMultiplication)), static_cast<int>(round(videoSize.height*params.undistortedImageMultiplication))); | |
120 newIntrinsicCameraMatrix.at<float>(0,2) = videoSize.width/2.; | |
121 newIntrinsicCameraMatrix.at<float>(1,2) = videoSize.height/2.; | |
122 initUndistortRectifyMap(intrinsicCameraMatrix, params.distortionCoefficients, Mat::eye(3,3, CV_32FC1), newIntrinsicCameraMatrix, videoSize, CV_32FC1, map1, map2); | |
123 | |
124 cout << "Undistorted width=" << videoSize.width << | |
125 ", height=" << videoSize.height << endl; | |
126 } | |
127 | |
103 Mat mask = imread(params.maskFilename, 0); | 128 Mat mask = imread(params.maskFilename, 0); |
104 if (mask.empty()) | 129 if (mask.empty()) { |
130 cout << "Mask filename " << params.maskFilename << " could not be opened." << endl; | |
105 mask = Mat::ones(videoSize, CV_8UC1); | 131 mask = Mat::ones(videoSize, CV_8UC1); |
132 } | |
106 | 133 |
107 boost::shared_ptr<TrajectoryDBAccess<Point2f> > trajectoryDB = boost::shared_ptr<TrajectoryDBAccess<Point2f> >(new TrajectoryDBAccessList<Point2f>()); | 134 boost::shared_ptr<TrajectoryDBAccess<Point2f> > trajectoryDB = boost::shared_ptr<TrajectoryDBAccess<Point2f> >(new TrajectoryDBAccessList<Point2f>()); |
108 //TrajectoryDBAccess<Point2f>* trajectoryDB = new TrajectoryDBAccessBlob<Point2f>(); | 135 //TrajectoryDBAccess<Point2f>* trajectoryDB = new TrajectoryDBAccessBlob<Point2f>(); |
109 trajectoryDB->connect(params.databaseFilename.c_str()); | 136 trajectoryDB->connect(params.databaseFilename.c_str()); |
110 trajectoryDB->createTable("positions"); | 137 trajectoryDB->createTable("positions"); |
118 Mat prevDesc, currDesc; | 145 Mat prevDesc, currDesc; |
119 | 146 |
120 std::vector<FeatureTrajectoryPtr> lostFeatures; | 147 std::vector<FeatureTrajectoryPtr> lostFeatures; |
121 std::vector<FeaturePointMatch> featurePointMatches; | 148 std::vector<FeaturePointMatch> featurePointMatches; |
122 | 149 |
123 HOGDescriptor hog; | |
124 hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector()); | |
125 | |
126 int key = '?'; | 150 int key = '?'; |
127 unsigned int savedFeatureId=0; | 151 unsigned int savedFeatureId=0; |
128 Mat frame = Mat::zeros(1, 1, CV_8UC1), currentFrameBW, previousFrameBW; | 152 Mat frame = Mat::zeros(1, 1, CV_8UC1), currentFrameBW, previousFrameBW, undistortedFrame; |
129 | 153 |
130 unsigned int lastFrameNum = nFrames; | 154 unsigned int lastFrameNum = nFrames; |
131 if (params.nFrames > 0) | 155 if (params.nFrames > 0) |
132 lastFrameNum = MIN(params.frame1+static_cast<unsigned int>(params.nFrames), nFrames); | 156 lastFrameNum = MIN(params.frame1+static_cast<unsigned int>(params.nFrames), nFrames); |
133 | 157 |
134 //capture.set(CV_CAP_PROP_POS_FRAMES, params.frame1); | 158 capture->setFrameNumber(params.frame1); |
135 for (unsigned int frameNum = params.frame1; (frameNum < lastFrameNum) && !::interruptionKey(key); frameNum++) { | 159 for (unsigned int frameNum = params.frame1; (frameNum < lastFrameNum) && !::interruptionKey(key); frameNum++) { |
136 capture >> frame; | 160 bool success = capture->getNextFrame(frame); |
137 | 161 if (!success || frame.empty()) { |
138 if (frame.empty() || frame.size() != videoSize) | 162 cout << "Empty frame " << frameNum << ", breaking (" << success << " " << frame.empty() << " [" << frame.size().width << "x" << frame.size().height << "])" << endl; |
139 break; | 163 break; |
140 | 164 } else if (frameNum%50 ==0) |
141 if (frameNum%50 ==0) | |
142 cout << "frame " << frameNum << endl; | 165 cout << "frame " << frameNum << endl; |
143 | 166 |
144 //capture.get(CV_CAP_PROP_POS_FRAMES) << " " << prevPts.size() << endl; | 167 if (params.undistort) { |
145 | 168 remap(frame, undistortedFrame, map1, map2, interpolationMethod, BORDER_CONSTANT, 0.); |
146 // int emptyFrameNum = 0; | 169 frame = undistortedFrame; |
147 // while (frame.empty()) { | 170 |
148 // cerr << "empty frame " << emptyFrameNum << " " << capture.get(CV_CAP_PROP_POS_FRAMES)<< endl; | 171 if (frame.size() != videoSize) { |
149 // capture >> frame;//break; | 172 cout << "Different frame size " << frameNum << ", breaking ([" << frame.size().width << "x" << frame.size().height << "])" << endl; |
150 // emptyFrameNum++; | 173 break; |
151 // if (emptyFrameNum>=3000) | 174 } |
152 // exit(0); | 175 } |
153 // } | 176 |
154 | 177 |
155 cvtColor(frame, currentFrameBW, CV_RGB2GRAY); | 178 cvtColor(frame, currentFrameBW, CV_RGB2GRAY); |
156 // "normal" feature detectors: detect features here | |
157 // detector.detect(currentFrameBW, currKpts); // see video_homography c++ sample | |
158 | 179 |
159 if (!prevPts.empty()) { | 180 if (!prevPts.empty()) { |
160 //::keyPoints2Points(prevKpts, prevPts); | |
161 currPts.clear(); | 181 currPts.clear(); |
162 calcOpticalFlowPyrLK(previousFrameBW, currentFrameBW, prevPts, currPts, status, errors, window, params.pyramidLevel, TermCriteria(3 /*static_cast<int>(TermCriteria::COUNT)+static_cast<int>(TermCriteria::EPS)*/, params.maxNumberTrackingIterations, params.minTrackingError), 0.5 /* unused */, 0); // OPTFLOW_USE_INITIAL_FLOW | 182 calcOpticalFlowPyrLK(previousFrameBW, currentFrameBW, prevPts, currPts, status, errors, window, params.pyramidLevel, TermCriteria(static_cast<int>(TermCriteria::COUNT)+static_cast<int>(TermCriteria::EPS) /* = 3 */, params.maxNumberTrackingIterations, params.minTrackingError), /* int flags = */ 0, params.minFeatureEigThreshold); |
163 /// \todo try calcOpticalFlowFarneback | 183 /// \todo try calcOpticalFlowFarneback |
164 | 184 |
165 std::vector<Point2f> trackedPts; | 185 std::vector<Point2f> trackedPts; |
166 std::vector<FeaturePointMatch>::iterator iter = featurePointMatches.begin(); | 186 std::vector<FeaturePointMatch>::iterator iter = featurePointMatches.begin(); |
167 while (iter != featurePointMatches.end()) { | 187 while (iter != featurePointMatches.end()) { |
168 bool deleteFeature = false; | 188 bool deleteFeature = false; |
169 | 189 |
170 if (status[iter->pointNum]) { | 190 if (status[iter->pointNum]) { |
171 iter->feature->addPoint(frameNum, currPts[iter->pointNum], homography); | 191 iter->feature->addPoint(frameNum, currPts[iter->pointNum], homography); |
172 | 192 |
173 deleteFeature |= iter->feature->isDisplacementSmall(params.nDisplacements, minTotalFeatureDisplacement) | 193 deleteFeature = iter->feature->isDisplacementSmall(params.nDisplacements, minTotalFeatureDisplacement) |
174 || !iter->feature->isMotionSmooth(params.accelerationBound, params.deviationBound); | 194 || !iter->feature->isMotionSmooth(params.accelerationBound, params.deviationBound); |
175 if (deleteFeature) | 195 if (deleteFeature) |
176 iter->feature->shorten(); | 196 iter->feature->shorten(); |
177 } else | 197 } else |
178 deleteFeature = true; | 198 deleteFeature = true; |
179 | 199 |
180 if (deleteFeature) { | 200 if (deleteFeature) { |
181 if (iter->feature->length() >= params.minFeatureTime) { | 201 if (iter->feature->length() >= params.minFeatureTime) { |
182 iter->feature->setId(savedFeatureId); | 202 iter->feature->setId(savedFeatureId); |
183 savedFeatureId++; | 203 savedFeatureId++; |
204 iter->feature->movingAverage(params.nFramesSmoothing); | |
184 lostFeatures.push_back(iter->feature); | 205 lostFeatures.push_back(iter->feature); |
185 } | 206 } |
186 iter = featurePointMatches.erase(iter); | 207 iter = featurePointMatches.erase(iter); |
187 } else { | 208 } else { |
188 trackedPts.push_back(currPts[iter->pointNum]); | 209 trackedPts.push_back(currPts[iter->pointNum]); |
201 // vector<Rect> locations; | 222 // vector<Rect> locations; |
202 // hog.detectMultiScale(frame, locations, 0, Size(8,8), Size(32,32), 1.05, 2); | 223 // hog.detectMultiScale(frame, locations, 0, Size(8,8), Size(32,32), 1.05, 2); |
203 // BOOST_FOREACH(Rect r, locations) | 224 // BOOST_FOREACH(Rect r, locations) |
204 // rectangle(frame, r.tl(), r.br(), cv::Scalar(0,255,0), 3); | 225 // rectangle(frame, r.tl(), r.br(), cv::Scalar(0,255,0), 3); |
205 } | 226 } |
206 //drawOpticalFlow(prevPts, currPts, status, frame); | |
207 | |
208 // cout << matches.size() << " matches" << endl; | |
209 // descMatcher.match(currDesc, prevDesc, matches); | |
210 // cout << matches.size() << " matches" << endl; | |
211 //drawMatchesRelative(prevKpts, currKpts, matches, frame); | |
212 } | 227 } |
213 | 228 |
214 // adding new features, using mask around existing feature positions | 229 // adding new features, using mask around existing feature positions |
215 Mat featureMask = mask.clone(); | 230 Mat featureMask = mask.clone(); |
216 for (unsigned int n=0;n<currPts.size(); n++) | 231 for (unsigned int n=0;n<currPts.size(); n++) |
217 for (int j=MAX(0, currPts[n].x-params.minFeatureDistanceKLT); j<MIN(videoSize.width, currPts[n].x+params.minFeatureDistanceKLT+1); j++) | 232 for (int j=MAX(0, currPts[n].x-params.minFeatureDistanceKLT); j<MIN(videoSize.width, currPts[n].x+params.minFeatureDistanceKLT+1); j++) |
218 for (int i=MAX(0, currPts[n].y-params.minFeatureDistanceKLT); i<MIN(videoSize.height, currPts[n].y+params.minFeatureDistanceKLT+1); i++) | 233 for (int i=MAX(0, currPts[n].y-params.minFeatureDistanceKLT); i<MIN(videoSize.height, currPts[n].y+params.minFeatureDistanceKLT+1); i++) |
219 featureMask.at<uchar>(i,j)=0; | 234 featureMask.at<uchar>(i,j)=0; |
220 goodFeaturesToTrack(currentFrameBW, newPts, params.maxNFeatures, params.featureQuality, params.minFeatureDistanceKLT, featureMask, params.windowSize, params.useHarrisDetector, params.k); | 235 goodFeaturesToTrack(currentFrameBW, newPts, params.maxNFeatures, params.featureQuality, params.minFeatureDistanceKLT, featureMask, params.blockSize, params.useHarrisDetector, params.k); |
221 BOOST_FOREACH(Point2f p, newPts) { //for (unsigned int i=0; i<newPts.size(); i++) { | 236 BOOST_FOREACH(Point2f p, newPts) { //for (unsigned int i=0; i<newPts.size(); i++) { |
222 FeatureTrajectoryPtr f = FeatureTrajectoryPtr(new FeatureTrajectory(frameNum, p, homography)); | 237 FeatureTrajectoryPtr f = FeatureTrajectoryPtr(new FeatureTrajectory(frameNum, p, homography)); |
223 featurePointMatches.push_back(FeaturePointMatch(f, currPts.size())); | 238 featurePointMatches.push_back(FeaturePointMatch(f, currPts.size())); |
224 currPts.push_back(p); | 239 currPts.push_back(p); |
225 } | 240 } |
226 // currPts.insert(currPts.end(), newPts.begin(), newPts.end()); | |
227 //::keyPoints2Points(currKpts, currPts, false); | |
228 | |
229 //brief.compute(currentFrameBW, currKpts, currDesc); //Compute brief descriptors at each keypoint location | |
230 | 241 |
231 if (params.display) { | 242 if (params.display) { |
243 imshow("mask", featureMask*256); | |
232 imshow("frame", frame); | 244 imshow("frame", frame); |
233 imshow("mask", featureMask*256); | |
234 key = waitKey(2); | 245 key = waitKey(2); |
235 } | 246 } |
236 previousFrameBW = currentFrameBW.clone(); | 247 previousFrameBW = currentFrameBW.clone(); |
237 prevPts = currPts; | 248 prevPts = currPts; |
238 //prevKpts = currKpts; | 249 } |
239 //currDesc.copyTo(prevDesc); | 250 |
240 } | 251 // save the remaining currently tracked features |
252 std::vector<FeaturePointMatch>::iterator iter = featurePointMatches.begin(); | |
253 while (iter != featurePointMatches.end()) { | |
254 if (iter->feature->length() >= params.minFeatureTime) { | |
255 iter->feature->setId(savedFeatureId); | |
256 savedFeatureId++; | |
257 iter->feature->movingAverage(params.nFramesSmoothing); | |
258 iter->feature->write(*trajectoryDB, "positions", "velocities"); | |
259 } | |
260 iter++; | |
261 } | |
241 | 262 |
242 trajectoryDB->endTransaction(); | 263 trajectoryDB->endTransaction(); |
243 trajectoryDB->disconnect(); | 264 trajectoryDB->disconnect(); |
244 } | 265 } |
245 | 266 |
310 | 331 |
311 // check for connected components | 332 // check for connected components |
312 int lastInstant = frameNum+params.minFeatureTime-maxTrajectoryLength; | 333 int lastInstant = frameNum+params.minFeatureTime-maxTrajectoryLength; |
313 if (lastInstant > 0 && frameNum%10==0) { | 334 if (lastInstant > 0 && frameNum%10==0) { |
314 featureGraph.connectedComponents(lastInstant); | 335 featureGraph.connectedComponents(lastInstant); |
315 vector<vector<unsigned int> > featureGroups = featureGraph.getFeatureGroups(); | 336 vector<vector<FeatureTrajectoryPtr> > featureGroups; |
337 featureGraph.getFeatureGroups(featureGroups); | |
316 for (unsigned int i=0; i<featureGroups.size(); ++i) { | 338 for (unsigned int i=0; i<featureGroups.size(); ++i) { |
317 trajectoryDB->writeObject(savedObjectId, featureGroups[i], -1, 1, string("objects"), string("objects_features")); | 339 vector<unsigned int> featureNumbers; |
340 for (unsigned int j=0; j<featureGroups[i].size(); ++j) | |
341 featureNumbers.push_back(featureGroups[i][j]->getId()); | |
342 trajectoryDB->writeObject(savedObjectId, featureNumbers, 0 /* unknown */, 1, string("objects"), string("objects_features")); | |
318 savedObjectId++; | 343 savedObjectId++; |
319 } | 344 } |
320 } | 345 } |
321 | 346 |
322 if (frameNum%100 ==0) | 347 if (frameNum%100 ==0) |
323 cout << featureGraph.informationString() << endl; | 348 cout << featureGraph.informationString() << endl; |
324 } | 349 } |
325 | 350 |
326 // save remaining objects | 351 // save remaining objects |
327 featureGraph.connectedComponents(frameNum+maxTrajectoryLength+1); | 352 featureGraph.connectedComponents(frameNum+maxTrajectoryLength+1); |
328 vector<vector<unsigned int> > featureGroups = featureGraph.getFeatureGroups(); | 353 vector<vector<FeatureTrajectoryPtr> > featureGroups; |
354 featureGraph.getFeatureGroups(featureGroups); | |
329 for (unsigned int i=0; i<featureGroups.size(); ++i) { | 355 for (unsigned int i=0; i<featureGroups.size(); ++i) { |
330 trajectoryDB->writeObject(savedObjectId, featureGroups[i], -1, 1, string("objects"), string("objects_features")); | 356 vector<unsigned int> featureNumbers; |
357 for (unsigned int j=0; j<featureGroups[i].size(); ++j) | |
358 featureNumbers.push_back(featureGroups[i][j]->getId()); | |
359 trajectoryDB->writeObject(savedObjectId, featureNumbers, 0 /* unknown */, 1, string("objects"), string("objects_features")); | |
331 savedObjectId++; | 360 savedObjectId++; |
332 } | 361 } |
333 | 362 |
334 trajectoryDB->endTransaction(); | 363 trajectoryDB->endTransaction(); |
335 trajectoryDB->disconnect(); | 364 trajectoryDB->disconnect(); |
343 cout << "The program tracks features" << endl; | 372 cout << "The program tracks features" << endl; |
344 trackFeatures(params); | 373 trackFeatures(params); |
345 } else if (params.groupFeatures) { | 374 } else if (params.groupFeatures) { |
346 cout << "The program groups features" << endl; | 375 cout << "The program groups features" << endl; |
347 groupFeatures(params); | 376 groupFeatures(params); |
377 } else { | |
378 cout << "Main option missing or misspelt" << endl; | |
348 } | 379 } |
349 | 380 |
350 return 0; | 381 return 0; |
351 } | 382 } |
352 | 383 |