diff --git a/examples/people_counting/people_counting.py b/examples/people_counting/people_counting.py index ab6a120..c05c651 100644 --- a/examples/people_counting/people_counting.py +++ b/examples/people_counting/people_counting.py @@ -20,65 +20,7 @@ def make_parser(): class Person(vqpy.VObjBase): - - @vqpy.property() - @vqpy.stateful(4) - def direction_vector(self): - tlbr_c, tlbr_p = self.getv('tlbr'), self.getv('tlbr', -5) - if tlbr_c is None or tlbr_p is None: - return None - center_c = (tlbr_c[:2] + tlbr_c[2:]) / 2 - center_p = (tlbr_p[:2] + tlbr_p[2:]) / 2 - diff = center_c - center_p - return int(diff[0]), int(diff[1]) - - @vqpy.property() - @vqpy.stateful(4) - def direction(self): - def denoise(target, reference): - THRESHOLD = 10 - if target != 0 and reference / target >= THRESHOLD: - target = 0 - return target - - def get_name(value, pos_name, neg_name): - if value > 0: - result = pos_name - elif value < 0: - result = neg_name - else: - result = "" - return result - - def get_center(tlbr): - return (tlbr[:2] + tlbr[2:]) / 2 - - def most_frequent(List): - from collections import Counter - occurence_count = Counter(List) - return occurence_count.most_common(1)[0][0] - - hist_len = 5 - tlbr_past = [self.getv("tlbr", (-1)*i) for i in range(1, 1 + hist_len)] - for value in tlbr_past: - if value is None: - return None - - centers = list(map(get_center, tlbr_past)) - diffs = [centers[i+1] - centers[i] for i in range(hist_len - 1)] - - diff_xs = [denoise(diff[0], diff[1]) for diff in diffs] - diff_ys = [denoise(diff[1], diff[0]) for diff in diffs] - - horizontal = most_frequent([get_name(diff_x, "right", "left") - for diff_x in diff_xs]) - vertical = most_frequent([get_name(diff_y, "bottom", "top") - for diff_y in diff_ys]) - direction = vertical + horizontal - if direction == "": - direction = None - - return direction + pass class CountPersonOnCrosswalk(vqpy.QueryBase): @@ -93,23 +35,26 @@ def set_output_configs() -> vqpy.OutputConfig: @staticmethod def setting() -> vqpy.VObjConstraint: - CROSSWALK_REGION_1 = [(731, 554), (963, 564), (436, 1076), (14, 1076)] - CROSSWALK_REGION_2 = [(1250, 528), (1292, 473), - (1839, 492), (1893, 547)] - CROSSWALK_REGIONS = [CROSSWALK_REGION_1, CROSSWALK_REGION_2] + CROSSWALK_REGION = [ + (0, 295), + (488, 1), + (684, 1), + (315, 479), + (0, 479)] filter_cons = {"__class__": lambda x: x == Person, "bottom_center": vqpy.utils.within_regions( - CROSSWALK_REGIONS + CROSSWALK_REGION )} select_cons = {"track_id": None, + "direction": None, } return vqpy.VObjConstraint(filter_cons=filter_cons, select_cons=select_cons, filename='on_crosswalk') -class CountPersonHeadLeft(CountPersonOnCrosswalk): +class CountPersonHeadTopright(CountPersonOnCrosswalk): @staticmethod def set_output_configs() -> vqpy.OutputConfig: @@ -120,17 +65,16 @@ def set_output_configs() -> vqpy.OutputConfig: @staticmethod def setting() -> vqpy.VObjConstraint: - filter_cons = {"direction": lambda x: "left" in x} + filter_cons = {"direction": lambda x: x == "topright"} select_cons = {"track_id": None, "direction": None, - "direction_vector": None } return vqpy.VObjConstraint(filter_cons=filter_cons, select_cons=select_cons, - filename='head_left') + filename='head_topright') -class CountPersonHeadRight(CountPersonOnCrosswalk): +class CountPersonHeadBottomleft(CountPersonOnCrosswalk): @staticmethod def set_output_configs() -> vqpy.OutputConfig: @@ -141,14 +85,13 @@ def set_output_configs() -> vqpy.OutputConfig: @staticmethod def setting() -> vqpy.VObjConstraint: - filter_cons = {"direction": lambda x: "right" in x} + filter_cons = {"direction": lambda x: "bottomleft" in x} select_cons = {"track_id": None, "direction": None, - "direction_vector": None } return vqpy.VObjConstraint(filter_cons=filter_cons, select_cons=select_cons, - filename='head_right') + filename='head_bottomleft') if __name__ == '__main__': @@ -156,7 +99,8 @@ def setting() -> vqpy.VObjConstraint: register("yolox", YOLOXDetector, "yolox_x.pth") vqpy.launch(cls_name=vqpy.COCO_CLASSES, cls_type={"person": Person}, - tasks=[CountPersonHeadLeft(), CountPersonHeadRight()], + # tasks=[CountPersonOnCrosswalk()], + tasks=[CountPersonHeadTopright(), CountPersonHeadBottomleft()], video_path=args.path, save_folder=args.save_folder, detector_name="yolox", diff --git a/examples/people_counting/yolox_detector.py b/examples/people_counting/yolox_detector.py index d9a9793..6735edb 100644 --- a/examples/people_counting/yolox_detector.py +++ b/examples/people_counting/yolox_detector.py @@ -28,7 +28,7 @@ def __init__(self, model_path, device="gpu", fp16=True): exp = get_exp(None, "yolox_x") exp.test_conf = 0.3 exp.nmsthre = 0.3 - exp.test_size = (640, 640) + exp.test_size = (480, 854) model = exp.get_model() model_info = get_model_info(model, exp.test_size) diff --git a/vqpy/base/query.py b/vqpy/base/query.py index 908e079..243bd1b 100644 --- a/vqpy/base/query.py +++ b/vqpy/base/query.py @@ -41,7 +41,7 @@ def vqpy_update(self, frame: FrameInterface): total_vobj_num_data = { OUTPUT_TOTAL_VOBJ_NUM_NAME: len(self._total_ids) } - if frame_id == 1: + if frame_id == 0: self._query_data = [total_vobj_num_data] else: assert OUTPUT_TOTAL_VOBJ_NUM_NAME in self._query_data[0] diff --git a/vqpy/function/functions.py b/vqpy/function/functions.py index bc2f8de..d352b6f 100644 --- a/vqpy/function/functions.py +++ b/vqpy/function/functions.py @@ -52,3 +52,58 @@ def bottom_center_coordinate(obj, tlbr): x = (tlbr[0] + tlbr[2]) / 2 y = tlbr[3] return [(x, y)] + + +@vqpy_func_logger(['tlbr'], ['direction'], ['tlbr'], required_length=5) +def direction(obj, tlbr): + """ + The general direction computed with the past 5 historical frames. + There are 9 posible results: + "top", "topright", "right", "bottomright", + "bottom", "bottomleft", "left", "topleft", + and None, which means the vobj stays still in the past 5 frames. + """ + def denoise(target, reference): + THRESHOLD = 10 + if target != 0 and reference / target >= THRESHOLD: + target = 0 + return target + + def get_name(value, pos_name, neg_name): + if value > 0: + result = pos_name + elif value < 0: + result = neg_name + else: + result = "" + return result + + def get_center(tlbr): + return (tlbr[:2] + tlbr[2:]) / 2 + + def most_frequent(List): + from collections import Counter + occurence_count = Counter(List) + return occurence_count.most_common(1)[0][0] + + hist_len = 5 + tlbr_past = [obj.getv("tlbr", (-1)*i) for i in range(1, 1 + hist_len)] + for value in tlbr_past: + if value is None: + return [None] + + centers = list(map(get_center, tlbr_past)) + diffs = [centers[i+1] - centers[i] for i in range(hist_len - 1)] + + diff_xs = [denoise(diff[0], diff[1]) for diff in diffs] + diff_ys = [denoise(diff[1], diff[0]) for diff in diffs] + + horizontal = most_frequent([get_name(diff_x, "right", "left") + for diff_x in diff_xs]) + vertical = most_frequent([get_name(diff_y, "bottom", "top") + for diff_y in diff_ys]) + direction = vertical + horizontal + if direction == "": + direction = None + + return [direction] diff --git a/vqpy/utils/predicate_funcs.py b/vqpy/utils/predicate_funcs.py index c629b79..b431ea5 100644 --- a/vqpy/utils/predicate_funcs.py +++ b/vqpy/utils/predicate_funcs.py @@ -1,10 +1,28 @@ def within_regions(regions): + """ + A predicate function that check whether the coordinate is in the regions. + This function should be used as the value of `filter_cons` dictionary. + and its key should be a coordinate property. The function will return true + if the computed coordinate values of the property is in the regions. + + :param regions: one region or a list of regions, where region should be + a list of coordinates and coordinates is a tuple of (x, y). + The coordinates should be the exterior points of a Polygon. + + """ + if type(regions[0]) is tuple: + regions = [regions] + def point_within_regions(coordinate): from shapely.geometry import Point, Polygon point = Point(coordinate) for region in regions: poly = Polygon(region) + if poly.area <= 0: + raise ValueError(f"The area with in region {region} is less" + f"than zero. Please check your region" + f"coordinates.") if point.within(poly): return True return False