Spaces:
Runtime error
Runtime error
import project_path | |
import cv2 | |
import numpy as np | |
from tqdm import tqdm | |
from lib.fish_eye.tracker import Tracker | |
VERSION = "09/21" | |
PRED_COLOR = (255, 0, 0) # blue | |
WHITE = (255, 255, 255) | |
BLACK = (0, 0, 0) | |
BORDER_PAD = 3 | |
LINE_HEIGHT= 22 | |
VIDEO_HEIGHT = 700 | |
INFO_PANE_WIDTH = 100 | |
BOX_THICKNESS = 2 | |
FONT_SCALE = 0.65 | |
FONT_THICKNESS = 1 | |
def is_fourcc_available(codec): | |
try: | |
fourcc = cv2.VideoWriter_fourcc(*codec) | |
temp_video = cv2.VideoWriter('temp.mp4', fourcc, 30, (640, 480), isColor=True) | |
return temp_video.isOpened() | |
except: | |
return False | |
def generate_video_batches(didson, preds, frame_rate, video_out_path, gp=None, image_meter_width=None, image_meter_height=None, batch_size=1000): | |
"""Write a visualized video to video_out_path, given a didson object. | |
""" | |
if (gp): gp(0, "Generating results video...") | |
end_frame = didson.info['endframe'] or didson.info['numframes'] | |
out = None # need to wait til we have height and width to instantiate video file | |
with tqdm(total=end_frame, desc="Generating results video", ncols=0) as pbar: | |
for i in range(0, end_frame, batch_size): | |
batch_end = min(end_frame, i+batch_size) | |
frames = didson.load_frames(start_frame=i, end_frame=batch_end) | |
vid_frames, h, w = get_video_frames(frames, preds, frame_rate, image_meter_width, image_meter_height, start_frame=i) | |
if out is None: | |
codec = cv2.VideoWriter_fourcc(*'avc1') if is_fourcc_available("avc1") else cv2.VideoWriter_fourcc(*'mp4v') | |
out = cv2.VideoWriter(video_out_path, codec, frame_rate, [ int(1.5*w), h ] ) | |
for j, frame in enumerate(vid_frames): | |
if gp: gp(( (i+j) / end_frame), 'Generating results video...') | |
out.write(frame) | |
pbar.update(1) | |
del frames | |
del vid_frames | |
out.release() | |
def get_video_frames(frames, preds, frame_rate, image_meter_width=None, image_meter_height=None, start_frame=0): | |
"""Get visualized video frames ready for output, given raw ARIS/DIDSON frames. | |
Warning: all frames in frames will be stored in memory - careful of OOM errors. Consider processing large files | |
in batches, such as in generate_video_batches() | |
Returns: | |
list(np.ndarray), height (int), width (int) | |
""" | |
pred_lengths = { fish['id'] : "%.2fm" % fish['length'] for fish in preds['fish'] } | |
clip_pr_counts = Tracker.count_dirs(preds) | |
color_map = { fish['id'] : fish['color'] for fish in preds['fish'] } | |
# filter JSON, if necessary (for shorter clips) | |
preds_frames = preds['frames'][start_frame:] | |
vid_frames = [] | |
if len(frames): | |
# assumes all frames the same size | |
h, w = frames[0].shape | |
# enforce a standard size so that text/box thickness is consistent | |
scale_factor = VIDEO_HEIGHT / h | |
h = VIDEO_HEIGHT | |
w = int(scale_factor*w) | |
num_frames = min(len(frames), len(preds_frames)) | |
for i, frame_raw in enumerate(frames[:num_frames]): | |
frame_raw = cv2.resize(cv2.cvtColor(frame_raw, cv2.COLOR_GRAY2BGR), (w,h)) | |
pred = preds_frames[i] | |
for fish in pred['fish']: | |
xmin, ymin, xmax, ymax = fish['bbox'] | |
left = int(round(xmin * w)) | |
right = int(round(xmax * w)) | |
top = int(round(ymin * h)) | |
bottom = int(round(ymax * h)) | |
fish_id = str(fish['fish_id']) | |
fish_len = pred_lengths[fish['fish_id']] | |
hexx = color_map[fish['fish_id']].lstrip('#') | |
color = tuple(int(hexx[i:i+2], 16) for i in (0, 2, 4)) | |
draw_fish(frame_raw, left, right, top, bottom, color, fish_id, fish_len, anno_align="right") | |
# add axis to frame | |
frame_raw = add_axis(frame_raw, image_meter_width, image_meter_height) | |
# add info | |
frame_info_panel = np.zeros((h, int(0.5*w), 3)).astype(np.uint8) | |
frame = np.concatenate((frame_info_panel, frame_raw), axis=1) | |
cv2.putText(frame, f'VERSION: {VERSION}', (BORDER_PAD, h-BORDER_PAD-LINE_HEIGHT*4), cv2.FONT_HERSHEY_SIMPLEX, FONT_SCALE, WHITE, FONT_THICKNESS, cv2.LINE_AA, False) | |
cv2.putText(frame, f'Right count: {clip_pr_counts[0]}', (BORDER_PAD, h-BORDER_PAD-LINE_HEIGHT*3), cv2.FONT_HERSHEY_SIMPLEX, FONT_SCALE, WHITE, FONT_THICKNESS, cv2.LINE_AA, False) | |
cv2.putText(frame, f'Left count: {clip_pr_counts[FONT_THICKNESS]}', (BORDER_PAD, h-BORDER_PAD-LINE_HEIGHT*2), cv2.FONT_HERSHEY_SIMPLEX, FONT_SCALE, WHITE, FONT_THICKNESS, cv2.LINE_AA, False) | |
cv2.putText(frame, f'Other fish: {clip_pr_counts[2]}', (BORDER_PAD, h-BORDER_PAD-LINE_HEIGHT*1), cv2.FONT_HERSHEY_SIMPLEX, FONT_SCALE, WHITE, FONT_THICKNESS, cv2.LINE_AA, False) | |
# cv2.putText(frame, f'Upstream: {preds["upstream_direction"]}', (0, h-1-LINE_HEIGHT*1), cv2.FONT_HERSHEY_SIMPLEX, FONT_SCALE, WHITE, FONT_THICKNESS, cv2.LINE_AA, False) | |
cv2.putText(frame, f'Frame: {start_frame+i}', (BORDER_PAD, h-BORDER_PAD-LINE_HEIGHT*0), cv2.FONT_HERSHEY_SIMPLEX, FONT_SCALE, WHITE, FONT_THICKNESS, cv2.LINE_AA, False) | |
vid_frames.append(frame) | |
return vid_frames, h, w | |
def draw_fish(frame, left, right, top, bottom, color, fish_id, fish_len, LINE_HEIGHT=18, anno_align="left"): | |
cv2.rectangle(frame, (left, top), (right, bottom), color, BOX_THICKNESS) | |
if anno_align == "left": | |
anno_align = left | |
else: | |
anno_align = right | |
cv2.putText(frame, fish_id, (anno_align, top), cv2.FONT_HERSHEY_SIMPLEX, FONT_SCALE, color, FONT_THICKNESS, cv2.LINE_AA, False) | |
cv2.putText(frame, fish_len, (anno_align, bottom+int(LINE_HEIGHT/2)), cv2.FONT_HERSHEY_SIMPLEX, FONT_SCALE, color, FONT_THICKNESS, cv2.LINE_AA, False) | |
def add_axis(img, image_meter_width=None, image_meter_height=None): | |
h, w, c = img.shape | |
# add black border around image | |
bordersize_t = 25 | |
bordersize_l = 45 | |
img = cv2.copyMakeBorder( | |
img, | |
bottom=bordersize_t, | |
top=0, | |
left=bordersize_l, | |
right=25, # this helps with text getting cut off | |
borderType=cv2.BORDER_CONSTANT, | |
value=BLACK | |
) | |
# add axis | |
axis_thickness = 1 | |
img = cv2.line(img, (bordersize_l, h+axis_thickness//2), (w+bordersize_l, h+axis_thickness//2), WHITE, axis_thickness) # x | |
img = cv2.line(img, (bordersize_l-axis_thickness//2, 0), (bordersize_l-axis_thickness//2, h), WHITE, axis_thickness) # y | |
# dist between ticks in meters | |
x_inc = 100 | |
if image_meter_width and image_meter_width > 0: | |
x_inc = w / image_meter_width / 2 # 0.5m ticks | |
if image_meter_width > 4: | |
x_inc *= 2 # 1m ticks | |
if image_meter_width > 8: | |
x_inc *= 2 # 2m ticks | |
# dist between ticks in meters | |
y_inc = 100 | |
if image_meter_height and image_meter_height > 0: | |
y_inc = h / image_meter_height / 2 # 0.5m ticks | |
if image_meter_height > 4: | |
y_inc *= 2 # 1m ticks | |
if image_meter_height > 8: | |
y_inc *= 2 # 2m ticks | |
if image_meter_height > 12: | |
y_inc *= 3/2 # 3m ticks | |
# tick mark labels | |
def x_label(x): | |
if image_meter_width and image_meter_width > 0: | |
if x_inc < w / image_meter_width: # fractional ticks | |
return "%.1fm" % (x / w * image_meter_width) | |
return "%.0fm" % (x / w * image_meter_width) | |
return str(x) # pixels | |
def y_label(y): | |
if image_meter_height and image_meter_height > 0: | |
if y_inc < y / image_meter_height: # fractional ticks | |
return "%.1fm" % (y / h * image_meter_height) | |
return "%.0fm" % (y / h * image_meter_height) | |
return str(y) # pixels | |
# add ticks | |
ticksize = 5 | |
x = 0 | |
while x < w: | |
img = cv2.line(img, (int(bordersize_l+x), h+axis_thickness//2), (int(bordersize_l+x), h+axis_thickness//2+ticksize), WHITE, axis_thickness) | |
cv2.putText(img, x_label(x), (int(bordersize_l+x), h+axis_thickness//2+LINE_HEIGHT), cv2.FONT_HERSHEY_SIMPLEX, FONT_SCALE*3/4, WHITE, FONT_THICKNESS, cv2.LINE_AA, False) | |
x += x_inc | |
y = 0 | |
while y < h: | |
img = cv2.line(img, (bordersize_l-axis_thickness//2, int(h-y)), (bordersize_l-axis_thickness//2-ticksize, int(h-y)), WHITE, axis_thickness) | |
ylabel = y_label(y) | |
txt_offset = 13*len(ylabel) | |
cv2.putText(img, y_label(y), (bordersize_l-axis_thickness//2-ticksize - txt_offset, int(h-y)), cv2.FONT_HERSHEY_SIMPLEX, FONT_SCALE*3/4, WHITE, FONT_THICKNESS, cv2.LINE_AA, False) | |
y += y_inc | |
# resize to original dims | |
return cv2.resize(img, (w,h)) |