【问题标题】:Camera video and taking a picture with Python Kivy相机视频和使用 Python Kivy 拍照
【发布时间】:2021-08-08 00:40:26
【问题描述】:

我正在使用屏幕管理器并希望在访问 ScreenThree 时 显示摄像机图像。我希望使用 cv2 相机代码。单击按钮后,我希望捕获一张图片,然后使用该图像执行代码。 到目前为止,我在互联网上找到了一个线程版本,可以很好地显示相机输入。通过单击按钮捕获图片是我的努力失败了。

Screen3 包含一个放置视频的位置和一个执行代码拍照的按钮。

<ScreenThree>:
    name: "screenthree"
    FloatLayout:
        Image:
            id: vid    #Vido image
            pos_hint: {'x':0.0, 'y':0.2}
            size_hint: 1.0, 0.8
        Image:
            source: "clue.png" # Decorative image
            size_hint:(0.20,0.20)
            pos_hint: {"x":0.05,"y":0.75}
        Button:
            text: "Click to scan"
            background_color : 0, 0, 1, 1
            size_hint: 0.2,0.05
            pos_hint: {"x":0.25,"y":0.85}
            on_press: root.takepic()
        Button:
            text: "Go to next screen"      # Screen4
            background_color : 1, 0, 1, 1
            size_hint: 0.2,0.05
            pos_hint: {"x":0.45,"y":0.85}
            on_press:
                root.manager.transition.direction = 'left'
                root.manager.transition.duration = 1
                root.manager.current = 'screen_four'

线程代码是(我不知道隐藏窗口是什么);

class ScreenApp(App):
    def build(self):
        threading.Thread(target=self.doit, daemon=True).start()
        self.new_screen = ScreenThree()
        return screen_manager

    def doit(self):
        self.do_vid = True
        cv2.namedWindow('Hidden', cv2.WINDOW_NORMAL | cv2.WINDOW_FREERATIO)
        cv2.resizeWindow('Hidden', 0, 0)
        cam=cv2.VideoCapture('/dev/video0', cv2.CAP_V4L)

        while (self.do_vid):
            ret, frame = cam.read()
            Clock.schedule_once(partial(self.display_frame, frame))
            cv2.imshow('Hidden', frame)
            cv2.waitKey(1)
        cam.release()
        cv2.destroyAllWindows()

    def display_frame(self, frame, dt):
        texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt='bgr')
        texture.blit_buffer(frame.tobytes(order=None), colorfmt='bgr', bufferfmt='ubyte')
        texture.flip_vertical()

        self.root.get_screen('screen_three').ids.vid.texture = texture

我将捕获图像的代码放在 ScreenThree 类中。单击按钮会转移到 takepic() 方法,但无法读取图像,尝试写入空图像时会崩溃。 /dev/video0 上的摄像头仍由线程代码持有。如何访问相机以拍摄照片,或者如何提示线程例程为我拍摄照片。

class ScreenThree(Screen):
    def takepic(self):
        cap = cv2.VideoCapture('/dev/video0', cv2.CAP_V4L)

        # set dimensions
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

        # take frame
        ret, frame = cap.read()
        # write frame to file
        cv2.imwrite('image.jpg', frame)
        # release camera
        cap.release()

【问题讨论】:

    标签: python multithreading image camera kivy


    【解决方案1】:

    由于您的线程已经在从相机捕获帧,您可以只使用已捕获的当前帧,如下所示:

    def doit(self):
        self.do_vid = True
        self.frame = None  # add reference to the current frame
        cv2.namedWindow('Hidden', cv2.WINDOW_NORMAL | cv2.WINDOW_FREERATIO)
        cv2.resizeWindow('Hidden', 0, 0)
        cam=cv2.VideoCapture('/dev/video0', cv2.CAP_V4L)
    
        while (self.do_vid):
            ret, self.frame = cam.read()  # save the current frame
            Clock.schedule_once(partial(self.display_frame, self.frame))
            cv2.imshow('Hidden', self.frame)
            cv2.waitKey(1)
        cam.release()
        cv2.destroyAllWindows()
    

    takepic()方法中使用当前帧:

    class ScreenThree(Screen):
        def takepic(self):
            # get current frame
            frame = App.get_running_app().frame
            # write frame to file
            cv2.imwrite('image.jpg', frame)
    

    【讨论】:

    • 我不知道 App.get_running_app() 存在,太棒了!这可能是答案,除了在 takepic 中获得的名声对象是 NoneType 并且它在尝试写入图像时崩溃。线程方法 doit() 显示框架对象是 并且除了相机图像出现在窗口中,因此框架在那里是完整的..
    • 你记得修改这行:ret, self.frame = cam.read() # save the current frame吗?注意self.frame
    • 是的,这是正确的,它现在可以工作了。我可以制作一个通用的工作版本并发布。合适吗?
    • 有时人们会这样做并将其作为另一个答案发布。这将使可能正在搜索相同答案的其他人更容易。
    【解决方案2】:

    感谢 John Anderson 先生的帮助,这个通用代码可以正常工作,并且可能对其他人有用。

    """
     Sets up multiple screens with transitions between screens.
     From Screen three the camera imaging appears.
     There may be a delay before appearing
     A button labeled Click to take" captures and stores the camera image
    """
    import threading
    from functools import partial
    import kivy
    import gi
    gi.require_version('Gst', '1.0')
    from kivy.app import App
    from kivy.uix.label import Label
    from kivy.clock import Clock
    from kivy.graphics.texture import Texture
    from PIL import Image as Pimage
    from kivy.config import Config
    import numpy
    import cv2
    # Builder is used when .kv file is in .py file
    from kivy.lang import Builder
    
    # The screen manager is a widget
    # dedicated to managing multiple screens for your application.
    from kivy.uix.screenmanager import ScreenManager, Screen
    
    from kivy.uix.camera import Camera
    Config.set('graphics', 'resizable', True)
    
    # You can create your kv code in the Python file
    Builder.load_string("""
    <ScreenOne>:
        BoxLayout:
            Button:
                text: "This is screen one"
                font_size: 48
                background_color : 0, 0, 1, 1
                on_press:
                    # Define the duration of the change and the direction of the slide
                    root.manager.transition.direction = 'left'
                    root.manager.transition.duration = 1
                    root.manager.current = 'screen_two'
    <ScreenTwo>:
        BoxLayout:
            orientation: 'vertical'
            background_color: 1,1,1,1
    #        Image:
    #            source: "somepic.png"
    #            size_hint:(0.7,0.35)
    #            pos_hint:{"center_x":0.5,"center_y":0.5}
    #            allow_stretch: True
    #            keep_ratio: False
    
            Button:
                text: "Screen Two press to change screen"
                font_size: 34
                font_name: 'AbyssinicaSIL-R'
                background_color : 1, 1, 0, 1
                on_press:
                    root.manager.transition.direction = 'left'
                    root.manager.transition.duration = 1
                    root.manager.current = 'screen_three'
    <ScreenThree>:
        name: "screenthree"
        FloatLayout:
            Image:
                id: vid
                pos_hint: {'x':0.0, 'y':0.2}
                size_hint: 1.0, 0.8
    #        Image:
    #            source: "Somepic2.png"
    #            size_hint:(0.20,0.20)
    #            pos_hint: {"x":0.05,"y":0.75}
            Button:
                text: "Click to scan"
                background_color : 0, 0, 1, 1
                size_hint: 0.2,0.05
                pos_hint: {"x":0.25,"y":0.85}
                on_press: root.takepic()
            Button:
                text: "Go to next screen"      #Goes to Screen4
                background_color : 1, 0, 1, 1
                size_hint: 0.2,0.05
                pos_hint: {"x":0.45,"y":0.85}
                on_press:
                    root.manager.transition.direction = 'left'
                    root.manager.transition.duration = 1
                    root.manager.current = 'screen_four'
            Label:
                id:notif
                text:"Wait for camera image."
                font_size: 20
                pos_hint: {"x":0.2,"y":0.1}
    <ScreenFour>:
        BoxLayout:
            Button:
                text: "Go to Screen 5"
                background_color : 0, 1, 1, 1
                on_press:
                    root.manager.transition.direction = 'left'
                    root.manager.transition.duration = 1
                    root.manager.current = 'screen_one'
    """)
    # Create a class for all screens in which you can include
    # helpful methods specific to that screen
    class ScreenOne(Screen):
        pass
    
    class ScreenTwo(Screen):
        pass
    
    class ScreenThree(Screen):
        def takepic(self):
            kk = App.get_running_app()
            frame = App.get_running_app().frame
            pdb.set_trace()
            cv2.imwrite('image.jpg', frame)
    
    class ScreenFour(Screen):
        pass
    
    # The ScreenManager controls moving between screens
    screen_manager = ScreenManager()
    
    # Add the screens to the manager and then supply a name
    # that is used to switch screens
    screen_manager.add_widget(ScreenOne(name ="screen_one"))
    screen_manager.add_widget(ScreenTwo(name ="screen_two"))
    screen_manager.add_widget(ScreenThree(name ="screen_three"))
    screen_manager.add_widget(ScreenFour(name ="screen_four"))
    # Create the App class
    class ScreenApp(App):
        def build(self):
            threading.Thread(target=self.doit, daemon=True).start()
            self.new_screen = ScreenThree()
            return screen_manager
    
        def doit(self):
            self.do_vid = True
            self.frame=None
            cv2.namedWindow('Hidden', cv2.WINDOW_NORMAL | cv2.WINDOW_FREERATIO)
            cv2.resizeWindow('Hidden', 0, 0)
            cam=cv2.VideoCapture('/dev/video0', cv2.CAP_V4L)
    
            while (self.do_vid):
                ret, self.frame = cam.read()
                Clock.schedule_once(partial(self.display_frame, self.frame))
                cv2.imshow('Hidden', self.frame)
                cv2.waitKey(1)
            cam.release()
            cv2.destroyAllWindows()
    
        def display_frame(self, frame, dt):
            texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt='bgr')
            texture.blit_buffer(frame.tobytes(order=None), colorfmt='bgr', bufferfmt='ubyte')
            texture.flip_vertical()
            self.root.get_screen('screen_three').ids.vid.texture = texture
    
    # run the app
    sample_app = ScreenApp()
    sample_app.run()
    

    【讨论】:

      猜你喜欢
      • 2013-02-12
      • 1970-01-01
      • 1970-01-01
      • 2021-08-09
      • 1970-01-01
      • 1970-01-01
      • 2023-03-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多