Mercurial Hosting > traffic-intelligence
comparison scripts/dltrack.py @ 1271:b2f90cada58f
removed complex merging of bikes and peds, may result in more fragmentation
author | Nicolas Saunier <nicolas.saunier@polymtl.ca> |
---|---|
date | Fri, 14 Jun 2024 15:56:01 -0400 |
parents | 20a5e1292321 |
children | 8e61ff3cd503 |
comparison
equal
deleted
inserted
replaced
1270:20a5e1292321 | 1271:b2f90cada58f |
---|---|
36 parser.add_argument('-l', dest = 'lastFrameNum', help = 'number of last frame number to process', type = int, default = inf) | 36 parser.add_argument('-l', dest = 'lastFrameNum', help = 'number of last frame number to process', type = int, default = inf) |
37 parser.add_argument('--conf', dest = 'confidence', help = 'object confidence threshold for detection', type = float, default = 0.25) | 37 parser.add_argument('--conf', dest = 'confidence', help = 'object confidence threshold for detection', type = float, default = 0.25) |
38 parser.add_argument('--bike-prop', dest = 'bikeProportion', help = 'minimum proportion of time a person classified as bike or motorbike to be classified as cyclist', type = float, default = 0.2) | 38 parser.add_argument('--bike-prop', dest = 'bikeProportion', help = 'minimum proportion of time a person classified as bike or motorbike to be classified as cyclist', type = float, default = 0.2) |
39 parser.add_argument('--cyclist-iou', dest = 'cyclistIou', help = 'IoU threshold to associate a bike and ped bounding box', type = float, default = 0.15) | 39 parser.add_argument('--cyclist-iou', dest = 'cyclistIou', help = 'IoU threshold to associate a bike and ped bounding box', type = float, default = 0.15) |
40 parser.add_argument('--cyclist-match-prop', dest = 'cyclistMatchingProportion', help = 'minimum proportion of time a bike exists and is associated with a pedestrian to be merged as cyclist', type = float, default = 0.3) | 40 parser.add_argument('--cyclist-match-prop', dest = 'cyclistMatchingProportion', help = 'minimum proportion of time a bike exists and is associated with a pedestrian to be merged as cyclist', type = float, default = 0.3) |
41 parser.add_argument('--max-temp-overal', dest = 'maxTemporalOverlap', help = 'maximum proportion of time to merge 2 bikes associated with same pedestrian', type = float, default = 0.05) | 41 #parser.add_argument('--max-temp-overal', dest = 'maxTemporalOverlap', help = 'maximum proportion of time to merge 2 bikes associated with same pedestrian', type = float, default = 0.05) |
42 | 42 |
43 args = parser.parse_args() | 43 args = parser.parse_args() |
44 params, videoFilename, databaseFilename, homography, invHomography, intrinsicCameraMatrix, distortionCoefficients, undistortedImageMultiplication, undistort, firstFrameNum = storage.processVideoArguments(args) | 44 params, videoFilename, databaseFilename, homography, invHomography, intrinsicCameraMatrix, distortionCoefficients, undistortedImageMultiplication, undistort, firstFrameNum = storage.processVideoArguments(args) |
45 | 45 |
46 if args.homographyFilename is not None: | 46 if args.homographyFilename is not None: |
63 mask = cv2.imread(params.maskFilename, cv2.IMREAD_GRAYSCALE) | 63 mask = cv2.imread(params.maskFilename, cv2.IMREAD_GRAYSCALE) |
64 else: | 64 else: |
65 mask = None | 65 mask = None |
66 if params is not None: | 66 if params is not None: |
67 smoothingHalfWidth = params.smoothingHalfWidth | 67 smoothingHalfWidth = params.smoothingHalfWidth |
68 minObjectDuration = params.minFeatureTime | |
68 else: | 69 else: |
69 smoothingHalfWidth = None | 70 smoothingHalfWidth = None |
71 minObjectDuration = 3 | |
70 | 72 |
71 # TODO use mask, remove short objects, smooth | 73 # TODO use mask, remove short objects, smooth |
72 | 74 |
73 # TODO add option to refine position with mask for vehicles, to save different positions | 75 # TODO add option to refine position with mask for vehicles, to save different positions |
74 # TODO work with optical flow (farneback or RAFT) https://pytorch.org/vision/main/models/raft.html | 76 # TODO work with optical flow (farneback or RAFT) https://pytorch.org/vision/main/models/raft.html |
140 cv2.destroyAllWindows() | 142 cv2.destroyAllWindows() |
141 | 143 |
142 # classification | 144 # classification |
143 shortObjectNumbers = [] | 145 shortObjectNumbers = [] |
144 for num, obj in objects.items(): | 146 for num, obj in objects.items(): |
145 if obj.length() < 3: | 147 if obj.length() < minObjectDuration: |
146 shortObjectNumbers.append(num) | 148 shortObjectNumbers.append(num) |
147 else: | 149 else: |
148 obj.setUserType(utils.mostCommon(obj.userTypes)) # improve? mix with speed? | 150 obj.setUserType(utils.mostCommon(obj.userTypes)) # improve? mix with speed? |
149 for num in shortObjectNumbers: | 151 for num in shortObjectNumbers: |
150 del objects[num] | 152 del objects[num] |
179 | 181 |
180 if costs.size > 0: | 182 if costs.size > 0: |
181 # before matching, scan for pedestrians with good non-overlapping temporal match with different bikes | 183 # before matching, scan for pedestrians with good non-overlapping temporal match with different bikes |
182 for pedInd in range(costs.shape[1]): | 184 for pedInd in range(costs.shape[1]): |
183 nMatchedBikes = (costs[:,pedInd] < -args.cyclistMatchingProportion).sum() | 185 nMatchedBikes = (costs[:,pedInd] < -args.cyclistMatchingProportion).sum() |
184 if nMatchedBikes == 0: # peds that have no bike matching: see if they have been classified as bikes sometimes | 186 if nMatchedBikes == 0: # peds that have no bike matching: see if they have been classified as bikes sometimes (more than args.bikeProportion) |
185 userTypeStats = Counter(obj.userTypes) | 187 userTypeStats = Counter(obj.userTypes) |
186 if (moving.userType2Num['cyclist'] in userTypeStats or (moving.userType2Num['motorcyclist'] in userTypeStats and moving.userType2Num['cyclist'] in userTypeStats and userTypeStats[moving.userType2Num['motorcyclist']]<=userTypeStats[moving.userType2Num['cyclist']])) and userTypeStats[moving.userType2Num['motorcyclist']]+userTypeStats[moving.userType2Num['cyclist']] > args.bikeProportion*userTypeStats.total(): # verif if not turning all motorbike into cyclists | 188 if (moving.userType2Num['cyclist'] in userTypeStats or (moving.userType2Num['motorcyclist'] in userTypeStats and moving.userType2Num['cyclist'] in userTypeStats and userTypeStats[moving.userType2Num['motorcyclist']]<=userTypeStats[moving.userType2Num['cyclist']])) and userTypeStats[moving.userType2Num['motorcyclist']]+userTypeStats[moving.userType2Num['cyclist']] > args.bikeProportion*userTypeStats.total(): # verif if not turning all motorbike into cyclists |
187 obj.setUserType(moving.userType2Num['cyclist']) | 189 obj.setUserType(moving.userType2Num['cyclist']) |
188 elif nMatchedBikes > 1: # try to merge bikes first | 190 # elif nMatchedBikes > 1: # try to merge bikes first |
189 twIndices = np.nonzero(costs[:,pedInd] < -args.cyclistMatchingProportion)[0] | 191 # twIndices = np.nonzero(costs[:,pedInd] < -args.cyclistMatchingProportion)[0] |
190 # we have to compute temporal overlaps of all 2 wheels among themselves, then remove the ones with the most overlap (sum over column) one by one until there is little left | 192 # # we have to compute temporal overlaps of all 2 wheels among themselves, then remove the ones with the most overlap (sum over column) one by one until there is little left |
191 nTwoWheels = len(twIndices) | 193 # nTwoWheels = len(twIndices) |
192 twTemporalOverlaps = np.zeros((nTwoWheels,nTwoWheels)) | 194 # twTemporalOverlaps = np.zeros((nTwoWheels,nTwoWheels)) |
193 for i in range(nTwoWheels): | 195 # for i in range(nTwoWheels): |
194 for j in range(i): | 196 # for j in range(i): |
195 twi = objects[twowheels[twIndices[i]]] | 197 # twi = objects[twowheels[twIndices[i]]] |
196 twj = objects[twowheels[twIndices[j]]] | 198 # twj = objects[twowheels[twIndices[j]]] |
197 twTemporalOverlaps[i,j] = len(set(twi.bboxes).intersection(set(twj.bboxes)))/max(len(twi.bboxes), len(twj.bboxes)) | 199 # twTemporalOverlaps[i,j] = len(set(twi.bboxes).intersection(set(twj.bboxes)))/max(len(twi.bboxes), len(twj.bboxes)) |
198 #twTemporalOverlaps[j,i] = twTemporalOverlaps[i,j] | 200 # #twTemporalOverlaps[j,i] = twTemporalOverlaps[i,j] |
199 tw2merge = list(range(nTwoWheels)) | 201 # tw2merge = list(range(nTwoWheels)) |
200 while len(tw2merge)>0 and (twTemporalOverlaps[np.ix_(tw2merge, tw2merge)] > args.maxTemporalOverlap).sum(0).max() >= 2: | 202 # while len(tw2merge)>0 and (twTemporalOverlaps[np.ix_(tw2merge, tw2merge)] > args.maxTemporalOverlap).sum(0).max() >= 2: |
201 i = (twTemporalOverlaps[np.ix_(tw2merge, tw2merge)] > args.maxTemporalOverlap).sum(0).argmax() | 203 # i = (twTemporalOverlaps[np.ix_(tw2merge, tw2merge)] > args.maxTemporalOverlap).sum(0).argmax() |
202 del tw2merge[i] | 204 # del tw2merge[i] |
203 twIndices = [twIndices[i] for i in tw2merge] | 205 # twIndices = [twIndices[i] for i in tw2merge] |
204 tw1 = objects[twowheels[twIndices[0]]] | 206 # tw1 = objects[twowheels[twIndices[0]]] |
205 twCost = costs[twIndices[0],:]*tw1.nBBoxes | 207 # twCost = costs[twIndices[0],:]*tw1.nBBoxes |
206 nBBoxes = tw1.nBBoxes | 208 # nBBoxes = tw1.nBBoxes |
207 for twInd in twIndices[1:]: | 209 # for twInd in twIndices[1:]: |
208 mergeObjects(tw1, objects[twowheels[twInd]]) | 210 # mergeObjects(tw1, objects[twowheels[twInd]]) |
209 twCost = twCost + costs[twInd,:]*objects[twowheels[twInd]].nBBoxes | 211 # twCost = twCost + costs[twInd,:]*objects[twowheels[twInd]].nBBoxes |
210 nBBoxes += objects[twowheels[twInd]].nBBoxes | 212 # nBBoxes += objects[twowheels[twInd]].nBBoxes |
211 twIndicesToKeep = list(range(costs.shape[0])) | 213 # twIndicesToKeep = list(range(costs.shape[0])) |
212 for twInd in twIndices[1:]: | 214 # for twInd in twIndices[1:]: |
213 twIndicesToKeep.remove(twInd) | 215 # twIndicesToKeep.remove(twInd) |
214 del objects[twowheels[twInd]] | 216 # del objects[twowheels[twInd]] |
215 twowheels = [twowheels[i] for i in twIndicesToKeep] | 217 # twowheels = [twowheels[i] for i in twIndicesToKeep] |
216 costs = costs[twIndicesToKeep,:] | 218 # costs = costs[twIndicesToKeep,:] |
217 | 219 |
218 twIndices, matchingPedIndices = linear_sum_assignment(costs) | 220 twIndices, matchingPedIndices = linear_sum_assignment(costs) |
219 for twInd, pedInd in zip(twIndices, matchingPedIndices): # caution indices in the cost matrix | 221 for twInd, pedInd in zip(twIndices, matchingPedIndices): # caution indices in the cost matrix |
220 if -costs[twInd, pedInd] >= args.cyclistMatchingProportion: | 222 if -costs[twInd, pedInd] >= args.cyclistMatchingProportion: |
221 tw = objects[twowheels[twInd]] | 223 tw = objects[twowheels[twInd]] |
222 ped = objects[pedestrians[pedInd]] | 224 ped = objects[pedestrians[pedInd]] |
223 mergeObjects(tw, ped) | 225 mergeObjects(tw, ped) |
224 del objects[pedestrians[pedInd]] | 226 del objects[pedestrians[pedInd]] |
227 # link ped to each assigned bike, remove bike from cost (and ped is temporal match is high) | |
228 | |
225 #TODO Verif overlap piéton vélo : si long hors overlap, changement mode (trouver exemples) | 229 #TODO Verif overlap piéton vélo : si long hors overlap, changement mode (trouver exemples) |
226 | 230 # TODO continue assigning if leftover bikes (if non temporally overlapping with existing bikes assigned to ped) |
231 | |
227 # interpolate and save image coordinates | 232 # interpolate and save image coordinates |
228 for num, obj in objects.items(): | 233 for num, obj in objects.items(): |
229 for f in obj.getFeatures(): | 234 for f in obj.getFeatures(): |
230 if f.length() != len(f.tmpPositions): # interpolate | 235 if f.length() != len(f.tmpPositions): # interpolate |
231 f.positions = moving.Trajectory.fromPointDict(f.tmpPositions) | 236 f.positions = moving.Trajectory.fromPointDict(f.tmpPositions) |
232 else: | 237 else: |
233 f.positions = moving.Trajectory.fromPointList(list(f.tmpPositions.values())) | 238 f.positions = moving.Trajectory.fromPointList(list(f.tmpPositions.values())) |
234 if not args.notSavingImageCoordinates: | 239 if not args.notSavingImageCoordinates: |
235 storage.saveTrajectoriesToSqlite(utils.removeExtension(args.databaseFilename)+'-bb.sqlite', list(objects.values()), 'object') | 240 storage.saveTrajectoriesToSqlite(utils.removeExtension(args.databaseFilename)+'-bb.sqlite', list(objects.values()), 'object') |
236 # project, smooth and save | 241 # project and smooth |
237 for num, obj in objects.items(): | 242 for num, obj in objects.items(): |
238 features = obj.getFeatures() | 243 features = obj.getFeatures() |
239 # possible to save bottom pedestrians? not consistent with other users | 244 # possible to save bottom pedestrians? not consistent with other users |
240 # if moving.userTypeNames[obj.getUserType()] == 'pedestrian': | 245 # if moving.userTypeNames[obj.getUserType()] == 'pedestrian': |
241 # assert len(features) == 2 | 246 # assert len(features) == 2 |
249 t.append(moving.Point.agg(points, np.mean).aslist()) | 254 t.append(moving.Point.agg(points, np.mean).aslist()) |
250 #t = sum([f.getPositions().asArray() for f in features])/len(features) | 255 #t = sum([f.getPositions().asArray() for f in features])/len(features) |
251 #t = (moving.Trajectory.add(t1, t2)*0.5).asArray() | 256 #t = (moving.Trajectory.add(t1, t2)*0.5).asArray() |
252 projected = cvutils.imageToWorldProject(np.array(t).T, intrinsicCameraMatrix, distortionCoefficients, homography) | 257 projected = cvutils.imageToWorldProject(np.array(t).T, intrinsicCameraMatrix, distortionCoefficients, homography) |
253 featureNum = features[0].getNum() | 258 featureNum = features[0].getNum() |
254 obj.features=[moving.MovingObject(featureNum, obj.getTimeInterval(), moving.Trajectory(projected.tolist()))] | 259 feature = moving.MovingObject(featureNum, obj.getTimeInterval(), moving.Trajectory(projected.tolist())) |
260 if smoothingHalfWidth is not None: # smoothing | |
261 feature.smoothPositions(smoothingHalfWidth, replace = True)#f.positions = f.getPositions().filterMovingWindow(smoothingHalfWidth) | |
262 feature.computeVelocities() | |
263 obj.features=[feature] | |
255 obj.featureNumbers = [featureNum] | 264 obj.featureNumbers = [featureNum] |
256 if smoothingHalfWidth is not None: # smoothing | 265 #saving |
257 for num, obj in objects.items(): | |
258 for f in obj.getFeatures(): | |
259 f.smoothPositions(smoothingHalfWidth, replace = True)#f.positions = f.getPositions().filterMovingWindow(smoothingHalfWidth) | |
260 f.computeVelocities() | |
261 storage.saveTrajectoriesToSqlite(args.databaseFilename, list(objects.values()), 'object') | 266 storage.saveTrajectoriesToSqlite(args.databaseFilename, list(objects.values()), 'object') |
262 | 267 |
263 | 268 |
264 | 269 |
265 # todo save bbox and mask to study localization / representation | 270 # todo save bbox and mask to study localization / representation |