Compare commits

..

No commits in common. "dc7d95bbcee1afbe7f70f28827bd16769de30c22" and "7710794bad26066688facccae64104b93794433e" have entirely different histories.

View file

@ -4,58 +4,18 @@ from argparse import Namespace
import datetime import datetime
import logging import logging
from multiprocessing import Event from multiprocessing import Event
from multiprocessing.synchronize import Event as BaseEvent
import cv2 import cv2
import numpy as np import numpy as np
import zmq import zmq
import tempfile
from pathlib import Path
import shutil
from trap.frame_emitter import DetectionState, Frame from trap.frame_emitter import DetectionState, Frame
logger = logging.getLogger("trap.renderer") logger = logging.getLogger("trap.renderer")
class FrameWriter:
"""
Drop-in compatible interface with cv2.VideoWriter, but support variable
framerate.
See https://video.stackexchange.com/questions/25811/ffmpeg-make-video-with-non-constant-framerate-from-image-filenames
"""
def __init__(self, filename: str, fps: float, frame_size: tuple) -> None:
self.filename = filename
self.fps = fps
self.frame_size = frame_size
self.tmp_dir = tempfile.TemporaryDirectory(prefix="trap-output-")
self.i = 0
def write(self, img: cv2.typing.MatLike):
self.i += 1
cv2.imwrite(self.tmp_dir.name + f"/{self.i:07d}.png", img)
def release(self):
"""Actually write the video"""
# ffmpeg -f image2 -ts_from_file 2 -i %d.png out.mp4
logger.info(f"Write frames from {self.tmp_dir.name} to {self.filename}")
(
ffmpeg
# the magic here is in --ts_from_file which uses the mtime of the file for the interval
# this makes it possible to have non-constant intervals between frames, which is usefull
# since we render frames when we get them
.input(self.tmp_dir.name + "/%07d.png", format="image2", ts_from_file=2)
.output(self.filename, framerate=self.fps)
.run()
)
logger.info(f"Rm frame directory: {self.tmp_dir.name}")
self.tmp_dir.cleanup()
class Renderer: class Renderer:
def __init__(self, config: Namespace, is_running: BaseEvent): def __init__(self, config: Namespace, is_running: Event):
self.config = config self.config = config
self.is_running = is_running self.is_running = is_running
@ -89,8 +49,6 @@ class Renderer:
filename = self.config.output_dir / f"render_predictions-{date_str}-{self.config.detector}.mp4" filename = self.config.output_dir / f"render_predictions-{date_str}-{self.config.detector}.mp4"
logger.info(f"Write to {filename}") logger.info(f"Write to {filename}")
return FrameWriter(str(filename), self.fps, self.frame_size)
fourcc = cv2.VideoWriter_fourcc(*'vp09') fourcc = cv2.VideoWriter_fourcc(*'vp09')
return cv2.VideoWriter(str(filename), fourcc, self.fps, self.frame_size) return cv2.VideoWriter(str(filename), fourcc, self.fps, self.frame_size)
@ -179,9 +137,9 @@ def decorate_frame(frame: Frame, prediction_frame: Frame, first_time: float, con
overlay = np.zeros(frame.img.shape, np.uint8) overlay = np.zeros(frame.img.shape, np.uint8)
# Fill image with red color(set each pixel to red) # Fill image with red color(set each pixel to red)
overlay[:] = (130, 0, 75) overlay[:] = (128, 0, 128)
frame.img = cv2.addWeighted(frame.img, .4, overlay, .6, 0) frame.img = cv2.addWeighted(frame.img, .5, overlay, .5, 0)
img = frame.img img = frame.img
# all not working: # all not working:
@ -279,6 +237,6 @@ def decorate_frame(frame: Frame, prediction_frame: Frame, first_time: float, con
return img return img
def run_renderer(config: Namespace, is_running: BaseEvent): def run_renderer(config: Namespace, is_running: Event):
renderer = Renderer(config, is_running) renderer = Renderer(config, is_running)
renderer.run() renderer.run()