如何使用 kivy 在 python 中动态访问单独的相机类(无需预初始化相机)

Posted

技术标签:

【中文标题】如何使用 kivy 在 python 中动态访问单独的相机类(无需预初始化相机)【英文标题】:How can I access a separate camera class dynamically in python with kivy (without pre-initialising camera) 【发布时间】:2021-10-22 19:25:53 【问题描述】:

我使用 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 或 Screen。我尝试了以下方法,但没有成功。不要预先初始化相机。仅使用按钮操作激活相机。

在单独的 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:

【问题讨论】:

【参考方案1】:

您的KivyCamera 扩展了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__() 调用),并通过使KivyCamera 扩展MDBoxLayout 来消除layout 变量。

【讨论】:

非常感谢John Anderson。它按预期工作。非常感谢您的帮助

以上是关于如何使用 kivy 在 python 中动态访问单独的相机类(无需预初始化相机)的主要内容,如果未能解决你的问题,请参考以下文章

Kivy,python:如何从根类(FaceRecApp)访问外部/子类?

如何使用On_Press更改动态创建的小部件的BG颜色并使用Pickle保存? (与Kivy的Python)

Python/Kivy:使用回车键将一个 TextInput 聚焦到另一个 TextInput

您如何使用 Kivy GUI 访问其他类方法和函数?

如何使用动态名称实现属性()(在python中)

用python添加的kivy中的引用对象