首页 > 解决方案 > 如何使用 kivy 在 python 中动态访问单独的相机类(无需预初始化相机)

问题描述

我已经使用 python+kivy (kivycamera.py) 编写了一个相机访问类,它正在工作。 kivycamera.py

# from kivymd.app import MDApp
from kivy.uix.image import Image
from kivy.graphics.texture import Texture
from kivy.clock import Clock
import cv2
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFillRoundFlatButton
import time


# class KivyCamera(MDApp):
class KivyCamera(Image):
    def build(self):
        layout = MDBoxLayout(orientation='vertical', spacing=10)

        self.image = Image()
        layout.add_widget(self.image)

        self.start_camera_button = MDFillRoundFlatButton(
            text="START CAMERA",
            pos_hint={'center_x': 0.5, 'center_y': 0.5},
            size_hint=(0.4, None),
            # size=("100dp", "100dp")
        )
        self.start_camera_button.bind(on_press=self.start_camera)
        layout.add_widget(self.start_camera_button)

        self.save_img_button = MDFillRoundFlatButton(
            text="TAKE PICTURE",
            pos_hint={'center_x': 0.5, 'center_y': 0.5},
            size_hint=(0.4, None),
            # size=("100dp", "100dp")
        )
        self.save_img_button.bind(on_press=self.take_picture)
        layout.add_widget(self.save_img_button)

        # self.video = cv2.VideoCapture(0)
        # Clock.schedule_interval(self.load_video, 1.0 / 30.0)
        # return layout
        # return self.image

    def start_camera(self, *args):
        self.video = cv2.VideoCapture(0)
        Clock.schedule_interval(self.load_video, 1.0 / 30.0)
        pass

    def load_video(self, *args):
        check, frame = self.video.read()
        if check:
            x, y, w, h = 200, 200, 250, 250
            p, q, r, s = 220, 220, 210, 210
            frame = cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 3)
            frame = cv2.rectangle(frame, (p, q), (p + r, q + s), (255, 0, 0), 3)
            self.image_frame = frame
            buffer = cv2.flip(frame, 0).tobytes()
            image_texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt="bgr")
            image_texture.blit_buffer(buffer, colorfmt="bgr", bufferfmt="ubyte")
            self.image.texture = image_texture

    def take_picture(self, *args):
        timestr = time.strftime("%Y%m%d_%H%M%S")
        cv2.imwrite("IMG_{}.png".format(timestr), self.image_frame)
        cv2.imshow("Hi", self.image_frame)

# KivyCamera().run()

如何仅在需要时将此类添加到另一个 MDApp 或屏幕。我尝试了以下方法,但没有成功。不要预先初始化相机。仅使用按钮操作激活相机。

在单独的 MDApp 中

from kivymd.app import MDApp
from kivycamera import KivyCamera


class DemoApp(MDApp):
    pass
    # def build(self):
    #     self.camera = KivyCamera()
    #     self.camera.build()
    #     print(self.camera)
    #     return self.camera


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

基维文件

BoxLayout:
    orientation: 'vertical'
    MDLabel:
        text: 'Hello'
    KivyCamera:

或使用屏幕管理器在单独的屏幕内

class UploadScreen(Screen):
    pass
    # def build(self):
    #     self.my_camera = KivyCamera()
    #     self.my_camera.build()
    #     self.ids.my_camera = self.my_camera
    #     return self.my_camera

基维文件

<UploadScreen>:
    name: 'upload'
    KivyCamera:

标签: pythonpython-3.xkivyopencv-pythonkivymd

解决方案


您的KivyCameraextends Image,但您将KivyCamera用作小部件容器。此外,在您的build()方法中,您正在创建一个layout,但没有对它做任何事情。我建议修改KivyCamera定义以将其基类更改为某些Layout(我选择RelativeLayout)并将其添加layout为子类。这是执行此操作的代码的修改版本:

from kivymd.app import MDApp
from kivycamera import KivyCamera


class DemoApp(MDApp):
    def build(self):
        self.camera = KivyCamera()
        self.camera.build()
        print(self.camera)
        return self.camera


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

和 kivycamera.py:

# from kivymd.app import MDApp
from kivy.uix.image import Image
from kivy.graphics.texture import Texture
from kivy.clock import Clock
import cv2
from kivy.uix.relativelayout import RelativeLayout
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFillRoundFlatButton
import time


# class KivyCamera(MDApp):
class KivyCamera(RelativeLayout):  # make this a container
    def build(self):
        layout = MDBoxLayout(orientation='vertical', spacing=10)

        self.image = Image()
        layout.add_widget(self.image)

        self.start_camera_button = MDFillRoundFlatButton(
            text="START CAMERA",
            pos_hint={'center_x': 0.5, 'center_y': 0.5},
            size_hint=(0.4, None),
            # size=("100dp", "100dp")
        )
        self.start_camera_button.bind(on_press=self.start_camera)
        layout.add_widget(self.start_camera_button)

        self.save_img_button = MDFillRoundFlatButton(
            text="TAKE PICTURE",
            pos_hint={'center_x': 0.5, 'center_y': 0.5},
            size_hint=(0.4, None),
            # size=("100dp", "100dp")
        )
        self.save_img_button.bind(on_press=self.take_picture)
        layout.add_widget(self.save_img_button)
        self.add_widget(layout)  # add the layout to the GUI

        # self.video = cv2.VideoCapture(0)
        # Clock.schedule_interval(self.load_video, 1.0 / 30.0)
        # return layout
        # return self.image

    def start_camera(self, *args):
        self.video = cv2.VideoCapture(0)
        Clock.schedule_interval(self.load_video, 1.0 / 30.0)
        pass

    def load_video(self, *args):
        check, frame = self.video.read()
        if check:
            x, y, w, h = 200, 200, 250, 250
            p, q, r, s = 220, 220, 210, 210
            frame = cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 3)
            frame = cv2.rectangle(frame, (p, q), (p + r, q + s), (255, 0, 0), 3)
            self.image_frame = frame
            buffer = cv2.flip(frame, 0).tobytes()
            image_texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt="bgr")
            image_texture.blit_buffer(buffer, colorfmt="bgr", bufferfmt="ubyte")
            self.image.texture = image_texture

    def take_picture(self, *args):
        timestr = time.strftime("%Y%m%d_%H%M%S")
        cv2.imwrite("IMG_{}.png".format(timestr), self.image_frame)
        cv2.imshow("Hi", self.image_frame)

# KivyCamera().run()

您还可以进行其他几种简化。例如,您可以通过将build()方法更改为方法来消除该__init__()方法(必须添加super(KivyCamera, self).__init__()调用),并layout通过KivyCamera扩展来消除变量MDBoxLayout


推荐阅读