Browse Source

Handling transition

dev
sipp11 5 years ago
parent
commit
e27db785b2
  1. 164
      src/main.py
  2. 81
      src/utils.py

164
src/main.py

@ -17,6 +17,7 @@ from utils import (
box_distance, box_distance,
get_heading, get_heading,
get_avg_heading, get_avg_heading,
OBJ_LEAVING_COND,
) )
pp = pprint.PrettyPrinter(indent=2) pp = pprint.PrettyPrinter(indent=2)
@ -53,7 +54,7 @@ args = vars(ap.parse_args())
# load the COCO class labels our YOLO model was trained on # load the COCO class labels our YOLO model was trained on
# labelsPath = os.path.sep.join([args["yolo"], "coco.names"]) # labelsPath = os.path.sep.join([args["yolo"], "coco.names"])
labelsPath = "/Users/sipp11/syncthing/dropbox/tracking-obj/mytrain.names" labelsPath = "../../syncthing/dropbox/tracking-obj/mytrain.names"
LABELS = open(labelsPath).read().strip().split("\n") LABELS = open(labelsPath).read().strip().split("\n")
# 0 person, 1 wheelchair, 2 bicycle, 3 motorbike, 4 car, 5 bus, 6 truck # 0 person, 1 wheelchair, 2 bicycle, 3 motorbike, 4 car, 5 bus, 6 truck
@ -62,8 +63,8 @@ np.random.seed(42)
COLORS = np.random.randint(0, 255, size=(len(LABELS), 3), dtype="uint8") COLORS = np.random.randint(0, 255, size=(len(LABELS), 3), dtype="uint8")
# derive the paths to the YOLO weights and model configuration # derive the paths to the YOLO weights and model configuration
weightsPath = "/Users/sipp11/syncthing/dropbox/tracking-obj/mytrain_final.weights" weightsPath = "../../syncthing/dropbox/tracking-obj/mytrain_final.weights"
configPath = "/Users/sipp11/syncthing/dropbox/tracking-obj/mytrain.cfg" configPath = "../../syncthing/dropbox/tracking-obj/mytrain.cfg"
# load our YOLO object detector trained on COCO dataset (80 classes) # load our YOLO object detector trained on COCO dataset (80 classes)
@ -74,11 +75,11 @@ ln = net.getLayerNames()
ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()] ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()]
def detect_stuffs(frame): def detect_stuffs(_frame):
# construct a blob from the input frame and then perform a forward # construct a blob from the input frame and then perform a forward
# pass of the YOLO object detector, giving us our bounding boxes # pass of the YOLO object detector, giving us our bounding boxes
# and associated probabilities # and associated probabilities
blob = cv2.dnn.blobFromImage(frame, 1 / 255.0, (416, 416), swapRB=True, crop=False) blob = cv2.dnn.blobFromImage(_frame, 1 / 255.0, (416, 416), swapRB=True, crop=False)
net.setInput(blob) net.setInput(blob)
start = time.time() start = time.time()
layerOutputs = net.forward(ln) layerOutputs = net.forward(ln)
@ -135,21 +136,6 @@ def detect_stuffs(frame):
if len(idxs) == 0: if len(idxs) == 0:
continue continue
# NOTE: we are not going to draw anything from DETECTION,
# only from tracking one
# loop over the indexes we are keeping
# for i in idxs.flatten():
# # extract the bounding box coordinates
# (x, y) = (boxes[i][0], boxes[i][1])
# (w, h) = (boxes[i][2], boxes[i][3])
# # draw a bounding box rectangle and label on the frame
# color = [int(c) for c in COLORS[classIDs[i]]]
# cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2)
# text = "{}: {:.4f}".format(LABELS[classIDs[i]], confidences[i])
# cv2.putText(
# frame, text, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2
# )
return idxs, boxes, confidences, classIDs, start, end return idxs, boxes, confidences, classIDs, start, end
@ -196,15 +182,17 @@ while True:
# grab the updated bounding box coordinates (if any) for each # grab the updated bounding box coordinates (if any) for each
# object that is being tracked # object that is being tracked
(success, boxes) = cv_trackers.update(frame) (success, boxes) = cv_trackers.update(frame)
print("success", success) # print("success", success)
print("boxes", boxes) # print("boxes", boxes)
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: if success:
obj_cnt = len(boxes) obj_cnt = len(boxes)
print(f"obj_cnt: ", obj_cnt, boxes) # print(f"obj_cnt: ", obj_cnt, ' | tracker #', len(trackers))
for idx in range(obj_cnt): for idx in range(obj_cnt):
GONE = False
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]
_last_pos = trackers[idx]["curr_position"] _last_pos = trackers[idx]["curr_position"]
@ -212,7 +200,7 @@ while True:
curr_distance = box_distance(box, _last_pos) curr_distance = box_distance(box, _last_pos)
last_distance = trackers[idx]["distance"] last_distance = trackers[idx]["distance"]
trackers[idx]["distance"] = curr_distance trackers[idx]["distance"] = curr_distance
trackers[idx]["curr_position"] = [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)
@ -225,7 +213,7 @@ 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"][:30] 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])
@ -233,29 +221,63 @@ while True:
tk = trackers[idx] tk = 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"])
print(f" ---> avg heading: ", avg_heading) for lvng_cnd in OBJ_LEAVING_COND:
if ( _origin = tk["origin"]["id"]
tk["first"]["id"] == 4 cond = dict(lvng_cnd)
and avg_heading == "N" if cond["origin_id"] != _origin or avg_heading != cond["heading"]:
and (x > 140 or x + w > 175) continue
): REACH_condition = False
print("gone!") if cond["x"] != -1:
_nid = f"id_5" 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"])
REACH_condition = True
elif cond["y"] != -1:
# Don't have one yet
if (cond["heading"] in "NE" and x + w > cond["y"]) or (
cond["heading"] in "SW" and x < cond["y"]
):
print("REACH condition: Y")
REACH_condition = True
if not REACH_condition:
continue
# print("MATCH COND")
# pp.pprint(cond)
# TODO: should be a loop here if next has > 1
_nid = f"id_{cond['next_area'][0]}"
print(f"#{tk['id']} origin:#{_origin} to#{_nid}", end="")
if _nid not in W4A: if _nid not in W4A:
print(f">>add AREA {_nid} to W4A", end="")
W4A[_nid] = {"objects": []} W4A[_nid] = {"objects": []}
_expected_frame = 30 * 4 # at least 4 sec later # put this object to W4A for next area if doesn't exist
has_this = [_ for _ in W4A[_nid]["objects"] if _[0]["id"] == tk["id"]] has_this = [_ for _ in W4A[_nid]["objects"] if _[0]["id"] == tk["id"]]
if not has_this: if not has_this:
GONE = True
# unit in frame
_expected_frame = _frame_count + cond["duration_to_next"]
W4A[_nid]["objects"].append((tk, _frame_count, _expected_frame)) W4A[_nid]["objects"].append((tk, _frame_count, _expected_frame))
untracking.append(trackers[idx]) untracking.append(tk)
print(f'>>GONE - W#{len(W4A[_nid]["objects"])}')
# print(f' {_nid} objs: ')
# pp.pprint(W4A[_nid]["objects"])
# print(f' {_nid} untracking: ', [_['id'] for _ in untracking])
print( if GONE:
f"[{tk['id']}-{tk['type']}] (x,y)=({x},{y})" continue
f" | still #{tk['still']} | distance: "
f"{last_distance:.3f} -> {curr_distance:.3f}" # print(
) # f"[{tk['id']}-{tk['type']}] (x,y)=({x},{y})"
_htxt = ",".join(trackers[idx]["heading"]) # f" | still #{tk['still']} | distance: "
print(f" ------ heading: {_htxt}") # f"{last_distance:.3f} -> {curr_distance:.3f}",
# end="",
# )
# _htxt = ",".join(trackers[idx]["heading"])
# print(f" ------ heading: {_htxt}", end='')
# print(f" ------ avg heading: {avg_heading}", end="")
# print("")
# DRAW on FRAME # DRAW on FRAME
color = [int(c) for c in COLORS[0]] color = [int(c) for c in COLORS[0]]
@ -270,6 +292,20 @@ while True:
2, 2,
) )
# Cleanup Tracker
if untracking:
ut_ids = [_["id"] for _ in untracking] # untracking ids
cv_trackers.clear()
cv_trackers = cv2.MultiTracker_create()
# 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"]))
print(f"=== AFTER CLEANUP ---- UNTRACKING ===")
print(f" cv #{len(cv_trackers.getObjects())} trackers #{len(trackers)}")
# only detect once a sec # only detect once a sec
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)
@ -291,31 +327,30 @@ while True:
continue continue
_x, _y, _w, _h = t["curr_position"] _x, _y, _w, _h = t["curr_position"]
print(f"[{t['id']}] - {t['type']}") # print(f"[{t['id']}] - {t['type']}")
is_same = is_it_the_same_obj(x, y, w, h, _x, _y, _w, _h, id=t["id"]) is_same = is_it_the_same_obj(x, y, w, h, _x, _y, _w, _h, id=t["id"])
if is_same: if is_same:
break break
if not is_same: if not is_same:
gid = None gid = None
if found_at["id"] == 5: _area_id = f"id_{found_at['id']}"
print(f"FOUND AT 5 ON {_frame_count}") if _area_id in W4A:
_po = W4A["id_5"]["objects"] # check candidates which has "matched" opportunity too
_po = [_ for _ in _po if _frame_count > _[2]] _po = W4A[_area_id]["objects"]
_po = sorted(_po, key=lambda kk: kk[2]) _po = [_ for _ in _po if _frame_count > _[2]] # opportunity
print(' ------ ', _po) _po = sorted(_po, key=lambda kk: kk[2]) # first one first
if _po: if _po:
gid = _po[0][0]["id"] gid = _po[0][0]["id"]
# remove this id out of next W4A # remove this id out of next W4A
W4A["id_5"]["objects"] = [ W4A[_area_id]["objects"] = [
_ for _ in W4A["id_5"]["objects"] if _[0]["id"] == gid _ for _ in W4A[_area_id]["objects"] if _[0]["id"] != gid
] ]
print(f" >> possibly this obj: ", _po) print(f" --- {len(_po)} candicate: picked id={gid}")
# add tracker to this obj else:
# create a new object tracker for the bounding box and add it print(f" --- no candidate")
# to our multi-object tracker
# tracker = OPENCV_OBJECT_TRACKERS[args["tracker"]]() # create tracker and add to multi-object tracker
# trackers.add(tracker, frame, box)
_tracker = OPENCV_OBJECT_TRACKERS["csrt"]() _tracker = OPENCV_OBJECT_TRACKERS["csrt"]()
bbox = (x, y, w, h) bbox = (x, y, w, h)
cv_trackers.add(_tracker, frame, bbox) cv_trackers.add(_tracker, frame, bbox)
@ -324,7 +359,7 @@ while True:
"type": _class, "type": _class,
"curr_position": bbox, "curr_position": bbox,
"heading": [], "heading": [],
"first": found_at, "origin": found_at,
"distance": -1, "distance": -1,
"last_position": bbox, "last_position": bbox,
"still": 0, "still": 0,
@ -332,25 +367,22 @@ while True:
if gid is None: if gid is None:
tracker_counter += 1 tracker_counter += 1
trackers.append(t) trackers.append(t)
print(f"trackers ADD - now total #{len(trackers)}") print(f"trackers ADDED - now total #{len(trackers)}")
pp.pprint(t) pp.pprint(t)
# print(f" i -> {i} ({x},{y}), {w},{h} ({x + w},{y + h})") # print(f" i -> {i} ({x},{y}), {w},{h} ({x + w},{y + h})")
_what = ",".join([LABELS[c] for c in classIDs]) _what = ",".join([LABELS[c] for c in classIDs])
print(f"[{_frame_count:08d}] :: {_what}") print(f"[{_frame_count:08d}] :: {_what}")
# untracking
print("untracking: ", [ut["id"] for ut in untracking])
# untracking_ids = [ut["id"] for ut in untracking]
# trackers = [tk for tk in trackers if tk["id"] not in untracking_ids]
# finished += untracking
if args["live"]: if args["live"]:
cv2.imshow("Frame", frame) cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF key = cv2.waitKey(1) & 0xFF
if key == ord("w"):
while cv2.waitKey(1) & 0xFF != ord("w"):
pass
# if the `q` key was pressed, break from the loop # if the `q` key was pressed, break from the loop
if key == ord("q"): elif key == ord("q"):
break break
if args["output"]: if args["output"]:

81
src/utils.py

@ -46,9 +46,9 @@ AREAS = [
], ],
[ [
("id", 8), ("id", 8),
("area", ((877, 224), (947, 334))), ("area", ((901, 223), (956, 342))),
("target", ["person", "wheelchair"]), ("target", ["person", "wheelchair"]),
("next", [7, 9, 5]), ("next", [7, 5]),
], ],
[ [
("id", 9), ("id", 9),
@ -62,12 +62,61 @@ 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 = [
# [
# ('origin_id', ),
# ('heading', ''),
# ('x', ),
# ('y', ),
# ('next_area', []),
# ('duration_to_next', )
# ],
[
("origin_id", 3),
("heading", "N"),
("x", 175),
("y", -1),
("next_area", [5]),
("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),
('heading', 'N'),
('x', 715),
('y', -1),
('next_area', [11]),
('duration_to_next', 30 * 3)
],
# [
# ('origin_id', ),
# ('heading', ''),
# ('x', ),
# ('y', ),
# ('next_area', []),
# ('duration_to_next', )
# ],
] ]
def get_avg_heading(headings): def get_avg_heading(headings):
latest = headings[:15] latest = headings[:15]
chars = collections.Counter(''.join(latest)).most_common(10) chars = collections.Counter("".join(latest)).most_common(10)
if chars: if chars:
return chars[0][0] return chars[0][0]
return None return None
@ -115,7 +164,7 @@ def check_if_inside_the_boxes(x, y, w, h, _type):
is_inside = True is_inside = True
# if diff_x < box_w # if diff_x < box_w
if is_inside: if is_inside:
print("INSIDE!! this -> ", box) # print("INSIDE!! this -> ", box)
return box return box
return False return False
@ -125,41 +174,41 @@ def is_it_the_same_obj(x1, y1, w1, h1, i1, j1, w2, h2, **kwargs):
object and of course, dimension too. object and of course, dimension too.
""" """
_id = kwargs.get("id", None) _id = kwargs.get("id", None)
if _id: # if _id:
print(" :: check against id:", _id) # print(" :: check against id:", _id)
DIMENSION_SHIFT = 0.15 DIMENSION_SHIFT = 0.15
# we have to use centroid !! from the experience # we have to use centroid !! from the experience
cx1, cy1, cx2, cy2 = x1 + w1 / 2, y1 + h1 / 2, i1 + w2 / 2, j1 + h2 / 2 cx1, cy1, cx2, cy2 = x1 + w1 / 2, y1 + h1 / 2, i1 + w2 / 2, j1 + h2 / 2
c_dff_x, c_dff_y = abs(cx2 - cx1), abs(cy2 - cy1) c_dff_x, c_dff_y = abs(cx2 - cx1), abs(cy2 - cy1)
w_shift, h_shift = w1 * DIMENSION_SHIFT, h1 * DIMENSION_SHIFT w_shift, h_shift = w1 * DIMENSION_SHIFT, h1 * DIMENSION_SHIFT
print(" ::SAME:: shift") # print(" ::SAME:: shift")
print(f" ---> SHIFT --> w:{w_shift}, h:{h_shift}") # print(f" ---> SHIFT --> w:{w_shift}, h:{h_shift}")
print(f" ---> centroid {c_dff_x}, {c_dff_y}") # print(f" ---> centroid {c_dff_x}, {c_dff_y}")
if c_dff_x > w_shift and c_dff_y > h_shift: if c_dff_x > w_shift and c_dff_y > h_shift:
print(" ::SAME:: shift too much already -- NOT THE SAME") # print(" ::SAME:: shift too much already -- NOT THE SAME")
return False return False
# if one inside the other # if one inside the other
if i1 > x1 and (w1 - w2) > i1 - x1 and j1 > y1 and h1 - h2 > j1 - y1: if i1 > x1 and (w1 - w2) > i1 - x1 and j1 > y1 and h1 - h2 > j1 - y1:
# one is inside the other # one is inside the other
print(" ::SAME:: new one inside existing tracker") # print(" ::SAME:: new one inside existing tracker")
return True return True
if x1 > i1 and (w2 - w1) > x1 - i1 and y1 > j1 and h2 - h1 > y1 - j1: if x1 > i1 and (w2 - w1) > x1 - i1 and y1 > j1 and h2 - h1 > y1 - j1:
# one is inside the other # one is inside the other
print(" ::SAME:: existing tracker inside new tracker") # print(" ::SAME:: existing tracker inside new tracker")
return True return True
# if it's not inside the other, then we can use "size" if it's different # if it's not inside the other, then we can use "size" if it's different
size1, size2 = w1 * h1, w2 * h2 size1, size2 = w1 * h1, w2 * h2
# if size is larger than 20%, then it's not the same thing # if size is larger than 20%, then it's not the same thing
print(f" ---> size {size1}, {size2}, diff % : {abs(size2 - size1)/size1}") # print(f" ---> size {size1}, {size2}, diff % : {abs(size2 - size1)/size1}")
print(" ::SAME:: size") # print(" ::SAME:: size")
if abs(size2 - size1) / size1 > 0.45: if abs(size2 - size1) / size1 > 0.45:
print(" ::SAME:: too diff in size -- NOT THE SAME") # print(" ::SAME:: too diff in size -- NOT THE SAME")
return False return False
print(" ::SAME:: last") # print(" ::SAME:: last")
return True return True

Loading…
Cancel
Save