Mercurial Hosting > traffic-intelligence
changeset 1242:4cd8ace3552f
major update for classification, allowing the use of neural network classification
author | Nicolas Saunier <nicolas.saunier@polymtl.ca> |
---|---|
date | Wed, 07 Feb 2024 11:43:03 -0500 |
parents | ab4c72b9475c |
children | 88eedf79f16a |
files | scripts/classify-objects.py scripts/dltrack.py scripts/manual-video-analysis.py scripts/polytracktopdtv.py trafficintelligence/cvutils.py trafficintelligence/moving.py trafficintelligence/storage.py |
diffstat | 7 files changed, 40 insertions(+), 30 deletions(-) [+] |
line wrap: on
line diff
--- a/scripts/classify-objects.py Mon Feb 05 17:06:01 2024 -0500 +++ b/scripts/classify-objects.py Wed Feb 07 11:43:03 2024 -0500 @@ -52,10 +52,13 @@ carNorm = norm(classifierParams.meanVehicleSpeed, classifierParams.stdVehicleSpeed) pedNorm = norm(classifierParams.meanPedestrianSpeed, classifierParams.stdPedestrianSpeed) # numpy lognorm shape, loc, scale: shape for numpy is scale (std of the normal) and scale for numpy is exp(location) (loc=mean of the normal) -bicLogNorm = lognorm(classifierParams.scaleCyclistSpeed, loc = 0., scale = np.exp(classifierParams.locationCyclistSpeed)) -speedProbabilities = {moving.userTypeNames[1]: lambda s: carNorm.pdf(s), - moving.userTypeNames[2]: lambda s: pedNorm.pdf(s), - moving.userTypeNames[4]: lambda s: bicLogNorm.pdf(s)} +cycLogNorm = lognorm(classifierParams.scaleCyclistSpeed, loc = 0., scale = np.exp(classifierParams.locationCyclistSpeed)) +speedProbabilities = {moving.userType2Num['car']: lambda s: carNorm.pdf(s), + moving.userType2Num['pedestrian']: lambda s: pedNorm.pdf(s), + moving.userType2Num['cyclist']: lambda s: cycLogNorm.pdf(s)} +if useYolo: # consider other user types + for i in [3, 5, 6, 7]: + speedProbabilities[i] = speedProbabilities[moving.userType2Num['car']] if args.plotSpeedDistribution: import matplotlib.pyplot as plt @@ -106,7 +109,7 @@ #if undistort: # img = cv2.remap(img, map1, map2, interpolation=cv2.INTER_LINEAR) if useYolo: - results = yolo.predict(img, classes=list(moving.cocoTypeNames.keys()), verbose=False) + results = yolo.predict(img, conf = classifierParams.confidence, classes=list(moving.cocoTypeNames.keys()), verbose=False) for obj in objects[:]: if obj.getFirstInstant() <= frameNum: # if images are skipped @@ -116,15 +119,15 @@ for obj in currentObjects[:]: if obj.getLastInstant() <= frameNum: - obj.classifyUserTypeHoGSVM(minSpeedEquiprobable = classifierParams.minSpeedEquiprobable, speedProbabilities = speedProbabilities, maxPercentUnknown = classifierParams.maxPercentUnknown) + obj.classifyUserTypeHoGSVM(classifierParams.minSpeedEquiprobable, speedProbabilities, classifierParams.maxPercentUnknown) pastObjects.append(obj) currentObjects.remove(obj) else: if useYolo: # if one feature falls in bike, it's a bike # could one count all hits in various objects, or one takes majority at the instant? - # obj.classifyUserTypeYoloAtInstant(img, frameNum, width, height, classifierParams.percentIncreaseCrop, classifierParams.percentIncreaseCrop, results[0].boxes) - pass + #print('yolo', len(results[0].boxes)) + obj.classifyUserTypeYoloAtInstant(frameNum, results[0].boxes) else: obj.classifyUserTypeHoGSVMAtInstant(img, frameNum, width, height, classifierParams.percentIncreaseCrop, classifierParams.percentIncreaseCrop, classifierParams.minNPixels, classifierParams.hogRescaleSize, classifierParams.hogNOrientations, classifierParams.hogNPixelsPerCell, classifierParams.hogNCellsPerBlock, classifierParams.hogBlockNorm) if args.verbose: @@ -132,7 +135,7 @@ frameNum += 1 for obj in currentObjects: - obj.classifyUserTypeHoGSVM(minSpeedEquiprobable = classifierParams.minSpeedEquiprobable, speedProbabilities = speedProbabilities, maxPercentUnknown = classifierParams.maxPercentUnknown) + obj.classifyUserTypeHoGSVM(classifierParams.minSpeedEquiprobable, speedProbabilities, classifierParams.maxPercentUnknown) pastObjects.append(obj) print('Saving user types') storage.setRoadUserTypes(databaseFilename, pastObjects)
--- a/scripts/dltrack.py Mon Feb 05 17:06:01 2024 -0500 +++ b/scripts/dltrack.py Wed Feb 07 11:43:03 2024 -0500 @@ -25,6 +25,7 @@ parser.add_argument('--display', dest = 'display', help = 'show the raw detection and tracking results', action = 'store_true') parser.add_argument('-f', dest = 'firstFrameNum', help = 'number of first frame number to process', type = int, default = 0) parser.add_argument('-l', dest = 'lastFrameNum', help = 'number of last frame number to process', type = int, default = float('Inf')) +parser.add_argument('--conf', dest = 'confindence', help = 'object confidence threshold for detection', type = float, default = 0.25) 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) parser.add_argument('--cyclist-iou', dest = 'cyclistIou', help = 'IoU threshold to associate a bike and ped bounding box', type = float, default = 0.15) 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)
--- a/scripts/manual-video-analysis.py Mon Feb 05 17:06:01 2024 -0500 +++ b/scripts/manual-video-analysis.py Wed Feb 07 11:43:03 2024 -0500 @@ -29,9 +29,10 @@ 'car', 'pedestrian', 'motorcycle', - 'bicycle', + 'cyclist', 'bus', - 'truck'] + 'truck', + 'automated'] class UserConfiguration(object): def __init__(self, name, keyNew, keyAddInstant, nAttributes): self.name = name
--- a/scripts/polytracktopdtv.py Mon Feb 05 17:06:01 2024 -0500 +++ b/scripts/polytracktopdtv.py Wed Feb 07 11:43:03 2024 -0500 @@ -25,7 +25,7 @@ "1" -> "car" "2" -> "pedestrians" "3" -> "motorcycle" - "4" -> "bicycle" + "4" -> "cyclist" "5" -> "bus" "6" -> "truck" ... and other type if the objects_type table is defined in SQLite''' @@ -38,7 +38,7 @@ typeDict["1"] = "car" typeDict["2"] = "pedestrians" typeDict["3"] = "motorcycle" - typeDict["4"] = "bicycle" + typeDict["4"] = "cyclist" typeDict["5"] = "bus" typeDict["6"] = "truck"
--- a/trafficintelligence/cvutils.py Mon Feb 05 17:06:01 2024 -0500 +++ b/trafficintelligence/cvutils.py Wed Feb 07 11:43:03 2024 -0500 @@ -365,7 +365,8 @@ cv2.rectangle(img, (xCropMin, yCropMin), (xCropMax, yCropMax), cvBlue[colorType], 1) objDescription = '{} '.format(obj.num) if moving.userTypeNames[obj.userType] != 'unknown': - objDescription += moving.userTypeNames[obj.userType][0].upper() + s = moving.userTypeNames[obj.userType] + objDescription += s[0].upper()+s[1] if len(annotations) > 0: # if we loaded annotations, but there is no match if frameNum not in toMatches[obj.getNum()]: objDescription += " FA"
--- a/trafficintelligence/moving.py Mon Feb 05 17:06:01 2024 -0500 +++ b/trafficintelligence/moving.py Wed Feb 07 11:43:03 2024 -0500 @@ -306,7 +306,7 @@ return Point(projected[0], projected[1]) def inRectangle(self, xmin, xmax, ymin, ymax): - return (xmin <= p.x <= xmax) and (ymin <= p.y <= ymax) + return (xmin <= self.x <= xmax) and (ymin <= self.y <= ymax) def inPolygon(self, polygon): '''Indicates if the point x, y is inside the polygon @@ -1401,7 +1401,7 @@ 'car', 'pedestrian', 'motorcycle', - 'bicycle', + 'cyclist', 'bus', 'truck', 'automated'] @@ -2081,28 +2081,30 @@ croppedImg = cvutils.imageBox(img, self, instant, width, height, px, py, minNPixels) if croppedImg is not None and len(croppedImg) > 0: hog = cvutils.HOG(croppedImg, rescaleSize, orientations, pixelsPerCell, cellsPerBlock, blockNorm) - self.userTypes[instant] = self.appearanceClassifier.predict(hog.reshape(1,hog.size)) + self.userTypes[instant] = int(self.appearanceClassifier.predict(hog.reshape(1,hog.size))) else: self.userTypes[instant] = userType2Num['unknown'] - def classifyUserTypeYoloAtInstant(self, img, instant, width, height, px, py, minNPixels, bboxes): - '''Finds the user type based on where the feature fall in detected bboxes''' + def classifyUserTypeYoloAtInstant(self, instant, bboxes): + '''Finds the user type based on where the feature fall at instant in detected bboxes''' userTypes = [] if self.hasFeatures(): for f in self.getFeatures(): - if f.existsAtInstant(frameNum): - p = f.getPositionAtInstant(frameNum) + if f.existsAtInstant(instant): + p = f.getPositionAtInstant(instant) for box in bboxes: - if box.id is not None: - xyxy = box.xyxy[0].tolist() - if p.inRectangle(xyxy[0], xyxy[1], xyxy[2], xyxy[3]): - userTypes.append(moving.coco2Types[int(box.cls.item())]) + xyxy = box.xyxy[0].tolist() + if p.inRectangle(xyxy[0], xyxy[2], xyxy[1], xyxy[3]): + userTypes.append(coco2Types[int(box.cls.item())]) if len(userTypes) > 0: - pass + if userType2Num['cyclist'] in userTypes: + self.userTypes[instant] = userType2Num['cyclist'] + else: + self.userTypes[instant] = utils.mostCommon(userTypes) else: self.userTypes[instant] = userType2Num['unknown'] - def classifyUserTypeHoGSVM(self, minSpeedEquiprobable = -1, speedProbabilities = None, aggregationFunc = median, maxPercentUnknown = 0.5): + def classifyUserTypeHoGSVM(self, minSpeedEquiprobable = -1, speedProbabilities = None, maxPercentUnknown = 0.5): '''Agregates SVM detections in each image and returns probability (proportion of instants with classification in each category) @@ -2118,11 +2120,12 @@ # for t in self.getTimeInterval(): # if t not in self.userTypes: # self.classifyUserTypeHoGSVMAtInstant(images[t], t, homography, width, height, px, py, minNPixels, rescaleSize, orientations, pixelsPerCell, cellsPerBlock) + # compute P(Speed|Class) if speedProbabilities is None or self.aggregatedSpeed < minSpeedEquiprobable: # equiprobable information from speed - userTypeProbabilities = {userType2Num['car']: 1., userType2Num['pedestrian']: 1., userType2Num['bicycle']: 1.} + userTypeProbabilities = {userTypeNum: 1. for userTypeNum in speedProbabilities} else: - userTypeProbabilities = {userType2Num[userTypename]: speedProbabilities[userTypename](self.aggregatedSpeed) for userTypename in speedProbabilities} + userTypeProbabilities = {userTypeNum: speedProbabilities[userTypeNum](self.aggregatedSpeed) for userTypeNum in speedProbabilities} # compute P(Class|Appearance) nInstantsUserType = {userTypeNum: 0 for userTypeNum in userTypeProbabilities}# number of instants the object is classified as userTypename nInstantsUserType[userType2Num['unknown']] = 0 @@ -2135,7 +2138,7 @@ # class is the user type that maximizes usertype probabilities if nInstantsUserType[userType2Num['unknown']] >= maxPercentUnknown*self.length() and (speedProbabilities is None or self.aggregatedSpeed < minSpeedEquiprobable): # if no speed information and too many unknowns self.setUserType(userType2Num['unknown']) - else: + else: # if too many unknowns here, probas are just speed probas self.setUserType(utils.argmaxDict(userTypeProbabilities)) def classifyUserTypeArea(self, areas, homography):
--- a/trafficintelligence/storage.py Mon Feb 05 17:06:01 2024 -0500 +++ b/trafficintelligence/storage.py Wed Feb 07 11:43:03 2024 -0500 @@ -1520,6 +1520,7 @@ x = config.getint(self.sectionHeader, 'hog-ncells-block') self.hogNCellsPerBlock = (x, x) self.hogBlockNorm = config.get(self.sectionHeader, 'hog-block-norm') + self.confidence = config.getfloat(self.sectionHeader, 'confidence') self.speedAggregationMethod = config.get(self.sectionHeader, 'speed-aggregation-method') self.nFramesIgnoreAtEnds = config.getint(self.sectionHeader, 'nframes-ignore-at-ends')