Browse Source

Add feature to detect anomaly

dev
sipp11 5 years ago
parent
commit
47536e673d
  1. 120
      src/main.py
  2. 57
      src/utils.py

120
src/main.py

@ -5,6 +5,7 @@ time python src/_detector.py --input ~/Desktop/5min.mp4 -l
""" """
# import the necessary packages # import the necessary packages
import sys
import time import time
import argparse import argparse
import pprint import pprint
@ -18,6 +19,7 @@ from utils import (
get_heading, get_heading,
get_avg_heading, get_avg_heading,
OBJ_LEAVING_COND, OBJ_LEAVING_COND,
DONTCARE,
) )
pp = pprint.PrettyPrinter(indent=2) pp = pprint.PrettyPrinter(indent=2)
@ -73,6 +75,7 @@ print("[INFO] loading YOLO from disk...")
net = cv2.dnn.readNetFromDarknet(configPath, weightsPath) net = cv2.dnn.readNetFromDarknet(configPath, weightsPath)
ln = net.getLayerNames() ln = net.getLayerNames()
ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()] ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()]
start = end = 0
def detect_stuffs(_frame): def detect_stuffs(_frame):
@ -163,6 +166,11 @@ except:
_frame_count = 0 _frame_count = 0
tracker_counter = 1 tracker_counter = 1
for xx in range(3996):
(grabbed, frame) = vs.read()
_frame_count += 1
# loop over frames from the video file stream # loop over frames from the video file stream
while True: while True:
# read the next frame from the file # read the next frame from the file
@ -186,9 +194,25 @@ while True:
# print("boxes", boxes) # print("boxes", boxes)
print(f"[{_frame_count:08d}] ::") print(f"[{_frame_count:08d}] ::")
# quit if unable to read the video file
if not success:
print("Failed to read video", grabbed, frame)
cv_trackers.clear()
cv_trackers = cv2.MultiTracker_create()
# print(" INIT AGAIN cv_tracker", type(cv_trackers), cv_trackers)
# get rid of them in trackers too
trackers = [_ for _ in trackers if _["id"] not in ut_ids]
# add tracker again
for _trckr in trackers:
__tkr = OPENCV_OBJECT_TRACKERS["csrt"]()
cv_trackers.add(__tkr, frame, tuple(_trckr["curr_position"]))
(success, boxes) = cv_trackers.update(frame)
# print('SUCCESS? ', success)
untracking = [] untracking = []
# loop over the bounding boxes and draw then on the frame # loop over the bounding boxes and draw then on the frame
if success:
obj_cnt = len(boxes) obj_cnt = len(boxes)
# print(f"obj_cnt: ", obj_cnt, ' | tracker #', len(trackers)) # print(f"obj_cnt: ", obj_cnt, ' | tracker #', len(trackers))
for idx in range(obj_cnt): for idx in range(obj_cnt):
@ -196,6 +220,15 @@ while True:
box = boxes[idx] box = boxes[idx]
(x, y, w, h) = [int(v) for v in box] (x, y, w, h) = [int(v) for v in box]
# check if size is growing? if more than twice then, untrack it
ow, oh = trackers[idx]["size"]
if ow / w > 2 or oh / h > 2:
print(f" {tk['id']} GROW_TOO_BIG")
trackers[idx]["status"] = "grow-too-big"
untracking.append(trackers[idx])
continue
_last_pos = trackers[idx]["curr_position"] _last_pos = trackers[idx]["curr_position"]
(_x, _y, _w, _h) = _last_pos (_x, _y, _w, _h) = _last_pos
curr_distance = box_distance(box, _last_pos) curr_distance = box_distance(box, _last_pos)
@ -204,8 +237,9 @@ while True:
trackers[idx]["curr_position"] = box # [int(v) for v in box] trackers[idx]["curr_position"] = box # [int(v) for v in box]
_heading = get_heading(_x, _y, x, y) _heading = get_heading(_x, _y, x, y)
tk = trackers[idx]
STILL_DIST_PX = 2 STILL_DIST_PX = 1
if last_distance < STILL_DIST_PX and curr_distance < STILL_DIST_PX: if last_distance < STILL_DIST_PX and curr_distance < STILL_DIST_PX:
trackers[idx]["still"] += 1 trackers[idx]["still"] += 1
else: else:
@ -214,14 +248,32 @@ while True:
if trackers[idx]["still"] == 0: if trackers[idx]["still"] == 0:
trackers[idx]["heading"] = [_heading] + trackers[idx]["heading"] trackers[idx]["heading"] = [_heading] + trackers[idx]["heading"]
trackers[idx]["heading"] = trackers[idx]["heading"][:20] # trackers[idx]["heading"] = trackers[idx]["heading"][:20]
if trackers[idx]["still"] > 30 or x < 5 or x > 1250: if trackers[idx]["still"] > 30 or x < 5 or x > 1250:
untracking.append(trackers[idx]) untracking.append(trackers[idx])
tk = trackers[idx] if trackers[idx]["still"] > 10 and len(trackers[idx]["heading"]) < 5:
print(f" {tk['id']} LEFT - short-life")
trackers[idx]["status"] = "short-life"
untracking.append(trackers[idx])
# check if it's hit the first # check if it's hit the first
avg_heading = get_avg_heading(trackers[idx]["heading"]) avg_heading = get_avg_heading(trackers[idx]["heading"])
h_count = len(tk["heading"])
if 5 < h_count and h_count < 10:
# enough to validate, but never an established one
DONTCARE_IDS = [_[0][1] for _ in DONTCARE]
if tk["origin"]["id"] in DONTCARE_IDS:
dc_id = DONTCARE_IDS.index(tk["origin"]["id"])
dc_dict = dict(DONTCARE[dc_id])
print(DONTCARE_IDS, tk["origin"]["id"], dc_id, dc_dict["heading"])
if avg_heading in dc_dict["heading"]:
print(f" {tk['id']} DONT-CARE condition")
trackers[idx]["status"] = "dont-care"
untracking.append(trackers[idx])
for lvng_cnd in OBJ_LEAVING_COND: for lvng_cnd in OBJ_LEAVING_COND:
_origin = tk["origin"]["id"] _origin = tk["origin"]["id"]
cond = dict(lvng_cnd) cond = dict(lvng_cnd)
@ -229,25 +281,27 @@ while True:
continue continue
REACH_condition = False REACH_condition = False
if cond["x"] != -1: if cond["x"] != -1:
if (cond["heading"] in "NE" and x + w > cond["x"]) or (
cond["heading"] in "SW" and x < cond["x"]
):
# print("REACH condition: X", x, cond["x"]) # print("REACH condition: X", x, cond["x"])
if cond["heading"] in "NE":
_cond = cond["x"](y + w) if callable(cond["x"]) else cond["x"]
if x + w > _cond:
REACH_condition = True
elif cond["heading"] in "SW":
_cond = cond["x"](y + h) if callable(cond["x"]) else cond["x"]
if x < _cond:
REACH_condition = True REACH_condition = True
elif cond["y"] != -1: elif cond["y"] != -1:
_cond = cond["y"](x + h) if callable(cond["y"]) else cond["y"]
# Don't have one yet # Don't have one yet
if (cond["heading"] in "NE" and x + w > cond["y"]) or ( if cond["heading"] in "SW" and y + h > _cond:
cond["heading"] in "SW" and x < cond["y"] print("REACH condition: Y", y + h, _cond)
):
# print("REACH condition: Y")
REACH_condition = True REACH_condition = True
# pass
if not REACH_condition: if not REACH_condition:
continue continue
# print("MATCH COND") # print("MATCH COND")
# pp.pprint(cond) # pp.pprint(cond)
# TODO: should be a loop here if next has > 1
_nid = f"id_{cond['next_area'][0]}" _nid = f"id_{cond['next_area'][0]}"
# print(f"#{tk['id']} origin:#{_origin} to#{_nid}", end="") # print(f"#{tk['id']} origin:#{_origin} to#{_nid}", end="")
print(f" {tk['id']} LEFT from {_origin} -> {_nid}") print(f" {tk['id']} LEFT from {_origin} -> {_nid}")
@ -270,16 +324,16 @@ while True:
if GONE: if GONE:
continue continue
# print( print(
# f"[{tk['id']}-{tk['type']}] (x,y)=({x},{y})" f"[{tk['id']}{tk['type'][:1]}] o#{tk['origin']['id']} (x,y)=({x},{y},{w},{h})"
# f" | still #{tk['still']} | distance: " f" | still #{tk['still']} | distance: "
# f"{last_distance:.3f} -> {curr_distance:.3f}", f"{last_distance:.3f} -> {curr_distance:.3f}",
# end="", end="",
# ) )
# _htxt = ",".join(trackers[idx]["heading"]) _htxt = ",".join(trackers[idx]["heading"][:18])
# print(f" ------ heading: {_htxt}", end='') print(f"|heading:{_htxt}", end="")
# print(f" ------ avg heading: {avg_heading}", end="") print(f"|avg:{avg_heading}", end="")
# print("") print("")
# DRAW on FRAME # DRAW on FRAME
color = [int(c) for c in COLORS[0]] color = [int(c) for c in COLORS[0]]
@ -297,8 +351,12 @@ while True:
# Cleanup Tracker # Cleanup Tracker
if untracking: if untracking:
ut_ids = [_["id"] for _ in untracking] # untracking ids ut_ids = [_["id"] for _ in untracking] # untracking ids
# cv_trackers.clear()
cv_trackers.clear() cv_trackers.clear()
cv_trackers = None
time.sleep(1)
cv_trackers = cv2.MultiTracker_create() cv_trackers = cv2.MultiTracker_create()
# print(" cv_tracker", type(cv_trackers), cv_trackers)
# get rid of them in trackers too # get rid of them in trackers too
trackers = [_ for _ in trackers if _["id"] not in ut_ids] trackers = [_ for _ in trackers if _["id"] not in ut_ids]
# add tracker again # add tracker again
@ -313,6 +371,12 @@ while True:
if _frame_count % 15 == 1: if _frame_count % 15 == 1:
idxs, boxes, confidences, classIDs, start, end = detect_stuffs(frame) idxs, boxes, confidences, classIDs, start, end = detect_stuffs(frame)
# loop over the indexes we are keeping # loop over the indexes we are keeping
if len(idxs) == 0:
print("CAN NOT DETECT STUFFS??")
continue
if isinstance(idxs, tuple):
pp.pprint(idxs)
for i in idxs.flatten(): for i in idxs.flatten():
# extract the bounding box coordinates # extract the bounding box coordinates
(x, y) = (boxes[i][0], boxes[i][1]) (x, y) = (boxes[i][0], boxes[i][1])
@ -323,6 +387,10 @@ while True:
if not found_at: if not found_at:
continue continue
# only do for person/wheelchair
if _class not in ["person", "wheelchair", "bicycle"]:
continue
# (1) check whether it's the same object as one in trackers # (1) check whether it's the same object as one in trackers
is_same = False is_same = False
for t in trackers: for t in trackers:
@ -360,7 +428,9 @@ while True:
t = { t = {
"id": tracker_counter if gid is None else gid, "id": tracker_counter if gid is None else gid,
"type": _class, "type": _class,
"status": "",
"curr_position": bbox, "curr_position": bbox,
"size": (w, h),
"heading": [], "heading": [],
"origin": found_at, "origin": found_at,
"distance": -1, "distance": -1,
@ -383,8 +453,9 @@ while True:
# CLEANUP # CLEANUP
cleanups = [] cleanups = []
for k in W4A: for k in W4A:
print(f" CLEANUP[area {k}] ", end="")
for _o in W4A[k]["objects"]: for _o in W4A[k]["objects"]:
print(f" CLEANUP: {_o[0]['id']} - {_o[1]} - {_o[2]}", end="\r\n") print(f" {_o[0]['id']} - {_o[1]} - {_o[2]}", end="")
if _o[2] < _frame_count: if _o[2] < _frame_count:
continue continue
wf = _o[2] - _o[1] wf = _o[2] - _o[1]
@ -396,6 +467,7 @@ while True:
W4A[k]["objects"] = [ W4A[k]["objects"] = [
_ for _ in W4A[_area_id]["objects"] if _[0]["id"] != _o[0]["id"] _ for _ in W4A[_area_id]["objects"] if _[0]["id"] != _o[0]["id"]
] ]
print(f" | tracked #{len(trackers)}")
for obj, _s, _e in cleanups: for obj, _s, _e in cleanups:
print( print(
f" {obj['id']} CLEANED UP should found at {_e - _frame_count} ago" f" {obj['id']} CLEANED UP should found at {_e - _frame_count} ago"

57
src/utils.py

@ -4,6 +4,7 @@ from collections import namedtuple
Rectangle = namedtuple("Rectangle", "xmin ymin xmax ymax") Rectangle = namedtuple("Rectangle", "xmin ymin xmax ymax")
def area(a, b): def area(a, b):
# returns None if rectangles don't intersect # returns None if rectangles don't intersect
dx = min(a.xmax, b.xmax) - max(a.xmin, b.xmin) dx = min(a.xmax, b.xmax) - max(a.xmin, b.xmin)
@ -28,13 +29,13 @@ AREAS = [
], ],
[ [
("id", 3), ("id", 3),
("area", ((38, 340), (99, 482))), ("area", ((38, 340), (130, 482))),
("target", ["person", "wheelchair"]), ("target", ["person", "wheelchair"]),
("next", [5]), ("next", [5]),
], ],
[ [
("id", 4), ("id", 4),
("area", ((106, 310), (164, 461))), ("area", ((96, 310), (145, 461))),
("target", ["person", "wheelchair"]), ("target", ["person", "wheelchair"]),
], ],
[ [
@ -55,9 +56,15 @@ AREAS = [
("target", ["person", "wheelchair", "bicycle"]), ("target", ["person", "wheelchair", "bicycle"]),
("next", [4]), ("next", [4]),
], ],
[
("id", 11),
("area", ((875, 179), (945, 278))),
("target", ["person", "wheelchair"]),
("next", [9]),
],
[ [
("id", 8), ("id", 8),
("area", ((901, 223), (956, 342))), ("area", ((901, 223), (976, 342))),
("target", ["person", "wheelchair"]), ("target", ["person", "wheelchair"]),
("next", [7, 5]), ("next", [7, 5]),
], ],
@ -73,12 +80,6 @@ AREAS = [
("target", ["person", "wheelchair"]), ("target", ["person", "wheelchair"]),
("next", [9]), ("next", [9]),
], ],
[
("id", 11),
("area", ((875, 179), (915, 278))),
("target", ["person", "wheelchair"]),
("next", [9]),
],
] ]
OBJ_LEAVING_COND = [ OBJ_LEAVING_COND = [
@ -93,19 +94,19 @@ OBJ_LEAVING_COND = [
[ [
("origin_id", 3), ("origin_id", 3),
("heading", "N"), ("heading", "N"),
("x", 175), ("x", lambda y: (y - 80.5) / 2.19),
("y", -1),
("next_area", [5]),
("duration_to_next", 30 * 4),
],
[
("origin_id", 4),
("heading", "N"),
("x", 175),
("y", -1), ("y", -1),
("next_area", [5]), ("next_area", [5]),
("duration_to_next", 30 * 4), ("duration_to_next", 30 * 4),
], ],
# [
# ("origin_id", 4),
# ("heading", "N"),
# ("x", 175),
# ("y", -1),
# ("next_area", [5]),
# ("duration_to_next", 30 * 4),
# ],
[ [
("origin_id", 5), ("origin_id", 5),
("heading", "N"), ("heading", "N"),
@ -114,6 +115,14 @@ OBJ_LEAVING_COND = [
("next_area", [11]), ("next_area", [11]),
("duration_to_next", 30 * 3), ("duration_to_next", 30 * 3),
], ],
[
("origin_id", 7),
("heading", "S"),
("x", -1),
("y", lambda x: 0.4 * x + 152),
("next_area", [4]),
("duration_to_next", 30 * 4),
],
# [ # [
# ('origin_id', ), # ('origin_id', ),
# ('heading', ''), # ('heading', ''),
@ -124,10 +133,18 @@ OBJ_LEAVING_COND = [
# ], # ],
] ]
DONTCARE = (
(("origin_id", 5), ("heading", ["S", "W"])),
(("origin_id", 4), ("heading", ["S", "W"])),
(("origin_id", 7), ("heading", ["N", "E"])),
(("origin_id", 10), ("heading", ["N", "E"])),
)
def get_avg_heading(headings): def get_avg_heading(headings):
latest = headings[:15] latest = headings[:15]
chars = collections.Counter("".join(latest)).most_common(10) _h = "".join(latest).replace('W', '').replace('E', '')
chars = collections.Counter(_h).most_common(10)
if chars: if chars:
return chars[0][0] return chars[0][0]
return None return None
@ -189,7 +206,7 @@ def is_it_the_same_obj(x1, y1, w1, h1, i1, j1, w2, h2, **kwargs):
# print(" :: check against id:", _id, end="") # print(" :: check against id:", _id, end="")
# if first coords are pretty much the same spot, then they are the same # if first coords are pretty much the same spot, then they are the same
if abs(x1 - i1)/x1 < 0.05 and abs(y1 - j1)/y1 < 0.05: if abs(x1 - i1) / x1 < 0.05 and abs(y1 - j1) / y1 < 0.05:
# print(" same 1st coords") # print(" same 1st coords")
return True return True

Loading…
Cancel
Save