Search code examples
pythonopencvkivyrtsp

Create OpenCV Video Widgets to use in Kivy


I have 2 to 4 rtsp streams that I want to display within a box layout in Kivy but I can't seem to figure out how to use the kv language to achieve this. I am able to get one camera displaying using this code:

class KivyCamera(Image):
    def __init__(self, capture, fps, **kwargs):
        super(KivyCamera, self).__init__(**kwargs)
        self.capture = capture
        Clock.schedule_interval(self.update, 1.0 / fps)

    def update(self, dt):
        ret, frame = self.capture.read()
        if ret:
            # convert it to texture
            buf1 = cv2.flip(frame, 0)
            buf = buf1.tostring()
            image_texture = Texture.create(
                size=(frame.shape[1], frame.shape[0]), colorfmt='bgr')
            image_texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
            # display image from the texture
            self.texture = image_texture


class CamApp(App):
    def build(self):
        self.capture = cv2.VideoCapture('stream address removed for stack overflow')
        self.my_camera = KivyCamera(capture=self.capture, fps=30)
        return self.my_camera

    def on_stop(self):
        #without this, app will not exit even if the window is closed
        self.capture.release()


if __name__ == '__main__':
    CamApp().run()

However this is only using .py. I would like to have the ability to use the kv language to create instances of KivyCamera and set a different capture value for each one.

For example:

BoxLayout:
        orientation: 'horizontal'

        KivyCamera:
            capture: 'myvalue'
            pos_hint: {'left': 1}

        KivyCamera:
            capture: 'anothervalue'
            pos_hint: {'right': 1}

Solution

  • You can make the properties be fps and the source (index, filename, etc) instead of the VideoCapture:

    main.py

    from kivy.app import App
    from kivy.uix.image import Image
    from kivy.clock import Clock
    from kivy.graphics.texture import Texture
    from kivy.properties import ObjectProperty, NumericProperty
    
    import cv2
    
    class KivyCamera(Image):
        source = ObjectProperty()
        fps = NumericProperty(30)
    
        def __init__(self, **kwargs):
            super(KivyCamera, self).__init__(**kwargs)
            self._capture = None
            if self.source is not None:
                self._capture = cv2.VideoCapture(self.source)
            Clock.schedule_interval(self.update, 1.0 / self.fps)
    
        def on_source(self, *args):
            if self._capture is not None:
                self._capture.release()
            self._capture = cv2.VideoCapture(self.source)
    
        @property
        def capture(self):
            return self._capture
    
        def update(self, dt):
            ret, frame = self.capture.read()
            if ret:
                buf1 = cv2.flip(frame, 0)
                buf = buf1.tostring()
                image_texture = Texture.create(
                    size=(frame.shape[1], frame.shape[0]), colorfmt="bgr"
                )
                image_texture.blit_buffer(buf, colorfmt="bgr", bufferfmt="ubyte")
                self.texture = image_texture
    
    
    class CamApp(App):
        pass
    
    
    if __name__ == "__main__":
        CamApp().run()
    

    cam.kv

    BoxLayout:
        orientation: 'horizontal'
    
        KivyCamera:
            source: "stream address removed for stack overflow"