How to draw kitten on video with Gstreamer faster?
Gstreamer with Python is highly flexible for data visualization. Any video streaming pipeline could be extended with custom Python plugins that allows you to draw any information (bounding boxes, confidences, class names, etc.). Next information is simple comparison of two most popular approaches to draw on gstreamer buffer: PyCairo, OpenCV.
- Time to read: 15 min
- Code, Attachments
- Libraries: PyCairo, OpenCV, Gstreamer
- Plugins: filesrc, fpsdisplaysink, decodebin, gtksink
- Goal: Create plugin to draw on Gst.Buffer faster
Requirements
- Install OpenCV
pip3 install opencv-pythonOtherwise, look at this simple guide to install OpenCV from sources.
- Install PyCairo (official docs)
git clone https://github.com/pygobject/pycairo.git cd pycairo python setup.py build python setup.py installWarning: If install from pip you can face next Exception:
Error: Surface.create_for_data: Not Implemented yet
How it works?
Let implement plugin to visualize “walking kitten” on video. (See attachments to get files)
+
Firstly, try to launch code from repository:
git clone https://github.com/jackersson/gst-overlay cd gst-overlay # run with opencv python3 run.py -f ~/attachments/car.mpg --images ~/attachments/cat --opencv --fps # run with cairo python3 run.py -f ~/attachments/car.mpg --images ~/attachments/cat --cairo --fps
You should get something like this:
Hope everything works as expected. Now let go through implementation. (You can easily extend next approach to solve your tasks)
Explanation
All code for this project is based on post “How to implement Blur Filter using Gstreamer in Python with OpenCV“. You’ll get a lot of explanations for those parts of code that won’t be covered here.
#1. Implement plugin templates for GstOverlayOpenCv, GstOverlayCairo
Let make plugin that draw on incoming buffer instead of drawing on output buffer to avoid unnecessary buffer creation. To make this just override next function in GstBase.BaseTransform:
def do_transform_ip(self, inbuffer):
#2. Implement drawing with Cairo
Have a look at PyCairo documentation to get an intuition how to use it. To implement common drawing tasks with Cairo use next guide.
# Initialize stride value in respect to buffer format and width to accelerate image processing # https://pycairo.readthedocs.io/en/latest/reference/surfaces.html#cairo.ImageSurface.format_stride_for_width stride = cairo.ImageSurface.format_stride_for_width(cairo.FORMAT_RGB24, width) # Create image surface from image buffer (buffer should be mapped with READ | WRITE flags) # https://pycairo.readthedocs.io/en/latest/reference/surfaces.html#cairo.ImageSurface.create_for_data surface = cairo.ImageSurface.create_for_data(buffer, cairo.FORMAT_RGB24, width, height, stride) # Create cairo context to manipulate with image data # Documentation: https://pycairo.readthedocs.io/en/latest/reference/context.html context = cairo.Context(surface) # Calculate bottom-right point to place image overlay = self.overlay() x = width - overlay.get_width() y = height - overlay.get_height() # Documentation: https://www.cairographics.org/FAQ/ # Question: How do I paint on surface? context.set_source_surface(self.overlay(), x, y) context.paint()
Put previous code to do_transform_ip() in GstOverlayCairo
Note: To learn how to make Gst.Buffer writable read this.
Note: To draw wih cairo we use cairo.FORMAT_RGB24. So update Src/Sink caps should have RGBx format.
Gst.Caps.from_string("video/x-raw,format=RGBx")
#3. Implement drawing with OpenCV
Next function draws transparent image over some other image. Approach was taken from here.
def draw_image(source, image, x, y): h, w = image.shape[:2] max_x, max_y = x + w, y + h alpha = image[:, :, 3] / 255.0 for c in range(0, 3): color = image[:, :, c] * (alpha) beta = source[y:max_y, x:max_x, c] * (1.0 - alpha) source[y:max_y, x:max_x, c] = color + beta return source
Put draw_image() to do_transform_ip() in GstOverlayOpenCv
#4. Test with “walking kitten” images and video
The main goal is to draw “walking kitten” over video and measure its performance to check what plugin GstOverlayOpenCv or GstOverlayCairo works faster. For this purpose have a look at fpsdisplaysink plugin that allows to draw Average FPS on video.
Gstreamer pipeline looks like this:
filesrc location=~/attachments/car.mpg ! decodebin ! videoconvert ! gstoverlayopencv name=overlay ! videoconvert ! fpsdisplaysink video-sink=gtksink sync=false
Now launch with OpenCV:
python3 run.py -f ~/attachments/car.mpg --images ~/attachments/cat --opencv --fps
Now launch with Cairo:
python3 run.py -f ~/attachments/car.mpg --images ~/attachments/cat --cairo --fps
Note: To load images from file with Cairo use next call:
def load_image_cairo(filename): assert os.path.isfile(filename), "Invalid filename: {}".format(filename) return cairo.ImageSurface.create_from_png(filename)
#5. Check performance
Analyzing results you can see that video with GstOverlayCairo works faster (~103 fps) in comparison with GstOverlayOpenCv (~67 FPS). It is ~35% speed up for your applications. Look at this video (recorded from my PC to compare results)
Note: one of the reason is that in GstOverlayCairo no Gst.Buffer -> np.ndarray conversion.
Note: CPU specs: Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz 4/8
Conslusion
When visualization is performance consuming part – consider Cairo instead OpenCV.
Hope everything worked well. But if you have any troubles launching this code leave a question below 🙂
Hi, when executing your code getting the following error,there were no code changes.
Note:Gstreamer 1.15.1 is installed,opencv-python,numpy is installed using python3
(python3:11020): GStreamer-WARNING **: Element factory metadata for ‘gstoverlayopencv’ has no valid long-name field
Traceback (most recent call last):
File “run.py”, line 19, in
from gst_overlay.gst_overlay_opencv import GstOverlayOpenCv
File “/work/mediaPlayer(Gstreamer)/gst-overlay/gst_overlay/gst_overlay_opencv.py”, line 93, in
register_by_name(GST_OVERLAY_OPENCV)
File “/work/mediaPlayer(Gstreamer)/gst-overlay/gst_overlay/gst_overlay_opencv.py”, line 90, in register_by_name
raise ImportError(“Plugin {} not registered”.format(plugin_name))
ImportError: Plugin gstoverlayopencv not registered
Hi,
Answered to you in issues: https://github.com/jackersson/gst-overlay/issues/1
Best regards,
Taras Lishchenko
It¦s actually a nice and useful piece of info. I¦m satisfied that you shared this useful info with us. Please stay us up to date like this. Thank you for sharing.