diff --git a/tsmark/__init__.py b/tsmark/__init__.py index 7ea9a7a..e7813af 100644 --- a/tsmark/__init__.py +++ b/tsmark/__init__.py @@ -2,7 +2,7 @@ import argparse from tsmark.video_annotator import Marker -VERSION = "0.7.5" +VERSION = "0.7.6" def get_options(): diff --git a/tsmark/video_annotator.py b/tsmark/video_annotator.py index 9b69bf7..eab1ddb 100755 --- a/tsmark/video_annotator.py +++ b/tsmark/video_annotator.py @@ -19,6 +19,15 @@ VIDEO_COPY = "-c:v copy".split(" ") EXT_COMPRESS = ".mpeg" PLUGIN_FOLDER = os.path.expanduser("~/.config/tsmark/plugins") +COLOR_PREPOST = (0, 128, 128) +COLOR_KEY = (60, 205, 60) +COLOR_KEY_OCCLUDED = (60, 96, 60) +COLOR_INTERP = (192, 0, 192) +COLOR_INTERP_OCCLUDED = (128, 0, 128) +COLOR_HIDDEN = (60, 60, 60) +COLOR_NONE = (255, 255, 255) +POINT_VISIBILITY = ("yes", "occluded", "hidden") + class Marker: def __init__(self, opts): @@ -178,7 +187,7 @@ class Marker: cv2.circle(frame, (p_pos, bar_middle), 3, (32, 32, 32), -1) for ts in self.points[self.point_index]: p_pos = int(self.bar_start + ts / self.frames * (self.bar_end - self.bar_start)) - color = (60, 205, 60) if self.points[self.point_index][ts]["visible"] == 1 else (96, 96, 96) + color = self.get_point_color(self.points[self.point_index][ts]) cv2.circle(frame, (p_pos, bar_middle), 1, color, -1) cv2.line( @@ -255,14 +264,9 @@ class Marker: current = self.get_interpolated_point(index=index) if current["type"] in ("pre", "post"): continue - color = (0, 192, 192) - if current["type"] == "key": - color = (60, 205, 60) - if current["type"] == "interp": - color = (192, 0, 192) - if current["visible"] == 0: - color = (96, 96, 96) + if current["visible"] == "hidden": continue + color = self.get_point_color(current) cv2.circle(frame, (current["cx"], current["cy"]), 10, (0, 0, 0), 2) cv2.circle(frame, (current["cx"], current["cy"]), 10, color, 1) self.shadow_text( @@ -295,13 +299,7 @@ class Marker: try: current = self.get_interpolated_point() if current["type"] is not None: - color = (0, 192, 192) - if current["type"] == "key": - color = (60, 205, 60) - if current["type"] == "interp": - color = (192, 0, 192) - if current["visible"] == 0: - color = (96, 96, 96) + color = self.get_point_color(current) cv2.rectangle( frame, @@ -318,7 +316,7 @@ class Marker: po = self.get_interpolated_point(p) history.append([po["cx"], po["cy"]]) history = np.array(history, np.int32).reshape((-1, 1, 2)) - cv2.polylines(frame, [history], False, (192, 0, 192), 1) + cv2.polylines(frame, [history], False, COLOR_INTERP, 1) except KeyError: print(current, self.nr) @@ -329,7 +327,7 @@ class Marker: try: current = self.get_point() if current["x0"] is not None: - cv2.circle(frame, (current["cx"], current["cy"]), 13, (60, 205, 60), 2) + cv2.circle(frame, (current["cx"], current["cy"]), 13, COLOR_KEY, 2) except KeyError: pass except IndexError: @@ -372,13 +370,12 @@ class Marker: ip = self.get_interpolated_point() if ip["type"] is None: return - {"x0": None, "y0": None, "x1": None, "y1": None, "cx": None, "cy": None, "visible": 0, "type": None} self.points[self.point_index][self.nr] = { "x0": ip["x0"], "y0": ip["y0"], "x1": ip["x1"], "y1": ip["y1"], - "visible": 1, + "visible": POINT_VISIBILITY[0], } self.interpolate_points() except Exception: @@ -413,7 +410,7 @@ class Marker: "cy": None, "w": None, "h": None, - "visible": 0, + "visible": None, } def get_interpolated_point(self, nr=None, index=None): @@ -441,7 +438,7 @@ class Marker: "y1": None, "cx": None, "cy": None, - "visible": 0, + "visible": None, "type": None, "age": None, } @@ -451,13 +448,13 @@ class Marker: if self.point_click == 1 and self.point_index in self.points: for nr in range(self.frames): ip = self.get_interpolated_point(nr=nr) - if ip["type"] == "interp" and ip["visible"] == 1: + if ip["type"] == "interp" and ip["visible"] == POINT_VISIBILITY[0]: self.points[self.point_index][nr] = { "x0": ip["x0"], "y0": ip["y0"], "x1": ip["x1"], "y1": ip["y1"], - "visible": 1, + "visible": POINT_VISIBILITY[0], } self.interpolate_points() @@ -492,7 +489,7 @@ class Marker: "y0": y, "x1": min(self.video_res[0] - 1, x + w), "y1": min(self.video_res[1] - 1, y + h), - "visible": 1, + "visible": POINT_VISIBILITY[0], } if position == "br": self.points[self.point_index][self.nr] = { @@ -500,7 +497,7 @@ class Marker: "y0": max(0, y - h), "x1": x, "y1": y, - "visible": 1, + "visible": POINT_VISIBILITY[0], } if position == "c": self.points[self.point_index][self.nr] = { @@ -508,12 +505,12 @@ class Marker: "y0": max(0, int(y - h / 2)), "x1": min(self.video_res[0] - 1, int(x + w / 2)), "y1": min(self.video_res[1] - 1, int(y + h / 2)), - "visible": 1, + "visible": POINT_VISIBILITY[0], } else: # not a new point - self.points[self.point_index][self.nr]["visible"] = 1 + self.points[self.point_index][self.nr]["visible"] = POINT_VISIBILITY[0] if position == "c": current = self.points[self.point_index][self.nr] w = abs(current["x1"] - current["x0"]) @@ -523,7 +520,7 @@ class Marker: "y0": max(0, int(y - h / 2)), "x1": min(self.video_res[0] - 1, int(x + w / 2)), "y1": min(self.video_res[1] - 1, int(y + h / 2)), - "visible": 1, + "visible": POINT_VISIBILITY[0], } elif position == "tl": self.points[self.point_index][self.nr]["x0"] = x @@ -564,7 +561,7 @@ class Marker: self.points[self.point_index][self.nr]["y0"] = int(curr_point["cy"] - new_hh) self.points[self.point_index][self.nr]["x1"] = int(curr_point["cx"] + new_wh) self.points[self.point_index][self.nr]["y1"] = int(curr_point["cy"] + new_hh) - self.points[self.point_index][self.nr]["visible"] = 1 + self.points[self.point_index][self.nr]["visible"] = POINT_VISIBILITY[0] self.interpolate_points() @@ -579,7 +576,15 @@ class Marker: if curr_point["x0"] is None: self.add_message("Not in point frame (green)") return - self.points[self.point_index][self.nr]["visible"] = 1 - self.points[self.point_index][self.nr]["visible"] + + try: + new_index = (1 + POINT_VISIBILITY.index(self.points[self.point_index][self.nr]["visible"])) % len( + POINT_VISIBILITY + ) + except (ValueError, KeyError): + new_index = 0 + + self.points[self.point_index][self.nr]["visible"] = POINT_VISIBILITY[new_index] self.interpolate_points() def track_point(self): @@ -658,7 +663,6 @@ class World: return {"x0": x0, "y0": y0, "x1": x1, "y1": y1, "type": t, "visible": visible, "age": age} def point2array(p): - return [p["x0"], p["y0"], p["x1"], p["y1"]] if not point_index in self.points: @@ -777,6 +781,28 @@ class World: parts = int(100 * (frame / self.fps)) return time.strftime("%H:%M:%S", time.gmtime(seconds)) + ".%02d" % (parts) + def get_point_color(self, point): + + t = point.get("type", "key") + v = point.get("visible", "yes") + + if v == "hidden": + return COLOR_HIDDEN + if t == "key": + if v == "yes": + return COLOR_KEY + if v == "occluded": + return COLOR_KEY_OCCLUDED + if t == "interp": + if v == "yes": + return COLOR_INTERP + if v == "occluded": + return COLOR_INTERP_OCCLUDED + if t in ("post", "pre"): + return COLOR_PREPOST + + return COLOR_NONE + def get_help(self): return """Keyboard help: @@ -799,7 +825,7 @@ class World: Bounding box editor: p toggle bounding box drawing. enter any key as index. - o toggle object is occluded + o toggle object is visible/occluded/hidden x toggle (delete) key frame r convert interpolated points to points (no undo!) mouse left: set top-left corner of box @@ -810,9 +836,11 @@ class World: t start optical flow tracker m start plugin (if defined) Color codes: - green keyframe - purple interpolated frame - gray object is occluded + green |keypoint + purple |interpolated + darker tone |occluded key/interpolated + yellow |post / pre points + gray |point is hidden """ @@ -923,7 +951,8 @@ class World: self.points[index][key]["x1"], self.points[index][key]["y1"] = self.original_to_visual( (self.points[index][key]["x1"], self.points[index][key]["y1"]) ) - + if not self.points[index][key].get("visible", "NA") in POINT_VISIBILITY: + self.points[index][key]["visible"] = POINT_VISIBILITY[0] self.interpolate_points() print(f"Loaded points with index: {index}") self.point_index = None @@ -1201,14 +1230,12 @@ class World: elif k & 0xFF == ord("s"): # toggle crop size self.crop_click = 0 if self.crop_click == 2 else 2 self.crop[2] = True - elif k & 0xFF == ord("o"): # toggle point visibility (occlusion) - if self.opts.output_points is None: - continue - self.toggle_point_visibility() + elif k & 0xFF == ord("o"): # toggle point visibility (yes/occlusion/hidden) + if self.opts.output_points is not None: + self.toggle_point_visibility() elif k & 0xFF == ord("p"): # toggle points - if self.opts.output_points is None: - continue - self.point_click = 1 - self.point_click + if self.opts.output_points is not None: + self.point_click = 1 - self.point_click if self.point_click == 1: self.shadow_text( frame_visu, @@ -1494,5 +1521,5 @@ class TrackerGUI: "y0": tracked[i][1], "x1": tracked[i][0] + tracked[i][2], "y1": tracked[i][1] + tracked[i][3], - "visible": 1, + "visible": POINT_VISIBILITY[0], }