python - Kivy相机在多个屏幕上

标签 python opencv kivy kivy-language

我正在尝试创建一个具有两个屏幕的kivy简单应用程序,我需要在每个屏幕中同时加载一个自定义相机。

我尝试在gui.kv中加载显微镜小部件和主小部件中的凸轮,但错误地提示我

self._buffer = frame.reshape(-1)
AttributeError: 'NoneType' object has no attribute 'reshape'

当我卸下其中一台摄像机时,它可以工作,但是我在两个屏幕中都需要摄像机

按照我的代码

main.py
import kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder

from camera import CameraCv

#--load cv camera
class Cam(CameraCv):
    pass


class Microscope(Screen):

    def salve(self):
        print('salved')


#-- main widget 
class Main(Screen):


     #-- take a pic       
    def capture(self):
       self.ids.cam.capture()
#--scene manager
class ScreenManager(ScreenManager):
    pass

#--load my.kv gui
GUI = Builder.load_file('gui.kv')

#--main app
class MyApp(App):

    def build(self):

        return GUI

if __name__ == "__main__":

    MyApp().run()

gui.kv
#:kivy 1.11.1
GridLayout:
    cols: 1
    ScreenManager:
        id: screen_manager

        Main:
            name: "main"
            id: main

        Microscope:
            name: "microscope"
            id: microscope

<Main>:
    BoxLayout:

        orientation:'vertical'


        Label:
            text: 'BLA BLA BLA BLA '
            bold: True
            color: [1,1,1,1]
            size_hint: (1, None)
            height: 100

        GridLayout:
            cols:2
            size: root.width, root.height
            padding: 10

            BoxLayout:
                orientation: 'vertical'
                Label:
                    text: 'CAMERA'
                    bold: True
                    color: [1,1,0,1]
                    # size_hint: (1, None)
                    # height: 160
                ToggleButton:
                    id: Cam
                    text:'Play/Pause'
                    on_press: cam.play = not cam.play
                    size_hint: (1, None)
                    height: 60


            Cam:
                id: cam
                play: True
                size_hint: (1, None)
                height: 350     



        BoxLayout:
            orientation:'horizontal'      

            Button:
                text:"MICROSCOPIO"            
                on_press:
                    cam.play = False
                    app.root.ids['screen_manager'].current = 'microscope'



                size_hint: (1, None)
                height: 70

<Microscope>:
    BoxLayout:
        orientation:'horizontal'
        # if i remove this cam it work in main screen
        Cam:
            id:cam
            resolution: (640, 480)
            play: False

        Button:
            text:'SALVAR IMAGEM'
            #on_press: root.save()
            size_hint:(1,None)
            height: 70


<Cam>:
    resolution: (640,480)
    play: False
    keep_ratio: True
    allow_stretch: True

    canvas.before:
        PushMatrix
        Rotate:
            angle: root.angle
            axis: 0, 0, 1
            origin: root.center
    canvas.after:
        PopMatrix   

camera.py
from kivy.uix.camera import Camera
from kivy.properties import BooleanProperty, NumericProperty
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
import kivy
import time
import numpy as np
import cv2
#import controle

class CameraCv(Camera):
    # - determine angle of camera
    angle = NumericProperty(0)

    def __init__(self, **kwargs):
        super(CameraCv, self).__init__(**kwargs)

        self.isAndroid = kivy.platform == "android"
        if self.isAndroid:
            self.angle = -90

    def change_index(self, *args):
        new_index = 1 if self.index == 0 else 0
        self._camera._set_index(new_index)
        self.index = new_index
        self.angle = -90 if self.index == 0 else 90

    #-- convert cv in kv texture
    def on_tex(self, *l):

        image = np.frombuffer(self.texture.pixels, dtype='uint8')
        image = image.reshape(self.texture.height, self.texture.width, -1)
        #image = controle.cropCircle(image,50,210)


        numpy_data = image.tostring()

        self.texture.blit_buffer(numpy_data, bufferfmt="ubyte", colorfmt='rgba')

        super(CameraCv, self).on_tex(self.texture)

    def get_cameras_count(self):
        cameras = 1

        if self.isAndroid:
            cameras = self._camera.get_camera_count()
        return cameras

    def capture(self,*args):

        #timestr = time.strftime("%Y%m%d_%H%M%S")
        #self.export_to_png("temp/IMG_{}.png".format(timestr))
        self.export_to_png('temp/temp.png')

[编辑]

首先,我感谢Furas先生的回答和示例,该示例非常有效,因此我只需要改编他的代码即可。

所以现在的代码是:

camera.py
import numpy as np
import cv2
#import controle #--custom opncv methods

from kivy.uix.image import Image
from kivy.core.camera import Camera as CoreCamera
from kivy.properties import NumericProperty, ListProperty, BooleanProperty

# access to camera
core_camera = CoreCamera(index=0, resolution=(640, 480), stopped=True)

# Widget to display camera
class CameraCv(Image):
    '''Camera class. See module documentation for more information.
    '''

    play = BooleanProperty(True)
    '''Boolean indicating whether the camera is playing or not.
    You can start/stop the camera by setting this property::
        # start the camera playing at creation (default)
        cam = Camera(play=True)
        # create the camera, and start later
        cam = Camera(play=False)
        # and later
        cam.play = True
    :attr:`play` is a :class:`~kivy.properties.BooleanProperty` and defaults to
    True.
    '''

    index = NumericProperty(-1)
    '''Index of the used camera, starting from 0.
    :attr:`index` is a :class:`~kivy.properties.NumericProperty` and defaults
    to -1 to allow auto selection.
    '''

    resolution = ListProperty([-1, -1])
    '''Preferred resolution to use when invoking the camera. If you are using
    [-1, -1], the resolution will be the default one::
        # create a camera object with the best image available
        cam = Camera()
        # create a camera object with an image of 320x240 if possible
        cam = Camera(resolution=(320, 240))
    .. warning::
        Depending on the implementation, the camera may not respect this
        property.
    :attr:`resolution` is a :class:`~kivy.properties.ListProperty` and defaults
    to [-1, -1].
    '''

    def __init__(self, **kwargs):
        self._camera = None
        super(CameraCv, self).__init__(**kwargs)  # `CameraCv` instead of `Camera`
        if self.index == -1:
            self.index = 0
        on_index = self._on_index
        fbind = self.fbind
        fbind('index', on_index)
        fbind('resolution', on_index)
        on_index()

    def on_tex(self, *l):

        image = np.frombuffer(self.texture.pixels, dtype='uint8')
        image = image.reshape(self.texture.height, self.texture.width, -1)
        #image = controle.cropCircle(image,50,210) #custom opencv method
        numpy_data = image.tostring()

        self.texture.blit_buffer(numpy_data, bufferfmt="ubyte", colorfmt='rgba')
        self.canvas.ask_update()

    def _on_index(self, *largs):
        self._camera = None
        if self.index < 0:
            return
        if self.resolution[0] < 0 or self.resolution[1] < 0:
            return

        self._camera = core_camera # `core_camera` instead of `CoreCamera(index=self.index, resolution=self.resolution, stopped=True)`

        self._camera.bind(on_load=self._camera_loaded)
        if self.play:
            self._camera.start()
            self._camera.bind(on_texture=self.on_tex)

    def _camera_loaded(self, *largs):
        self.texture = self._camera.texture
        self.texture_size = list(self.texture.size)

    def on_play(self, instance, value):
        if self._camera:
            return
        if not value:
            self._camera.start()
        else:
            self._camera.stop()

gui.kv
#:kivy 1.11.1
GridLayout:
    cols: 1
    ScreenManager:
        id: screen_manager

        Main:
            name: "main"
            id: main

        Microscope:
            name: "microscope"
            id: microscope

<Main>:
    BoxLayout:

        orientation:'vertical'


        Label:
            text: 'BLA BLA BLA BLA '
            bold: True
            color: [1,1,1,1]
            size_hint: (1, None)
            height: 100

        GridLayout:
            cols:2
            size: root.width, root.height
            padding: 10
            id:box1
            BoxLayout:

                orientation: 'vertical'
                Label:
                    text: 'CAMERA'
                    bold: True
                    color: [1,1,0,1]
                    # size_hint: (1, None)
                    # height: 160
                ToggleButton:

                    text:'Play/Pause'
                    on_press: camera1.play = not camera1.play
                    size_hint: (1, None)
                    height: 60


            CameraCv:
                id: camera1
                resolution: (640,480)
                size_hint: (1, None)
                height: 350

        BoxLayout:
            orientation:'horizontal'          
            Button:
                text:"MICROSCOPIO"            
                on_press:        
                    app.root.ids['screen_manager'].current = 'microscope'                         
                size_hint: (1, None)
                height: 70

<Microscope>:
    BoxLayout:
        id: box2
        orientation:'vertical'
        # if i remove this cam it work in main screen
        CameraCv:
            id: camera2
            resolution: (640,480)
            size_hint: (1, None)
            height: 350
            keep_ratio: True
            allow_stretch: True

        Button:
            text:'image salve'
            on_press: app.root.ids['screen_manager'].current = 'main'
            size_hint:(1,None)
            height: 70


<CameraCv>:

    keep_ratio: True
    allow_stretch: True

    canvas.before:
        PushMatrix
        Rotate:
            axis: 0, 0, 1
            origin: root.center
    canvas.after:
        PopMatrix 

main.py
import kivy
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder

from camera import CameraCv

class Microscope(Screen):

    pass

#-- main widget 
class Main(Screen):
    pass

#--scene manager
class ScreenManager(ScreenManager):
    pass

#--load my.kv gui
GUI = Builder.load_file('gui.kv')

#--main app
class MyApp(App):

    def build(self):

        return GUI

if __name__ == "__main__":

    MyApp().run()

现在可以完美地工作了

最佳答案

我拿了Camera类的源代码

https://github.com/kivy/kivy/blob/master/kivy/uix/camera.py

并创建了自己的类MyCamera
我在类CoreCamera之外创建MyCamera实例,并且MyCamera的所有实例都使用CoreCamera的同一实例

core_camera = CoreCamera(index=0, resolution=(640, 480), stopped=True)

class MyCamera(Image):

    def _on_index(self, *largs):
        # ... 

        self._camera = core_camera

代替
class MyCamera(Image):

    def _on_index(self, *largs):
        # ... 

        self._camera = CoreCamera(index=self.index, resolution=self.resolution, stopped=True)

仍然有一些问题
CoreCamera中的
  • 已设置分辨率,所有MyClass使用相同的分辨率-但他们仍然需要resolution: (640, 480)中的gui.kv(但他们不使用此值)
  • play启动/停止所有MyClass的动画,因为CoreCamera控制了它。


  • 我用的例子

    https://kivy.org/doc/stable/examples/gen__camera__main__py.html

    用两个显示同一摄像机的小部件创建示例
    # https://kivy.org/doc/stable/examples/gen__camera__main__py.html
    # https://github.com/kivy/kivy/blob/master/kivy/uix/camera.py
    
    from kivy.uix.image import Image
    from kivy.core.camera import Camera as CoreCamera
    from kivy.properties import NumericProperty, ListProperty, BooleanProperty
    
    # access to camera
    core_camera = CoreCamera(index=0, resolution=(640, 480), stopped=True)
    
    # Widget to display camera
    class MyCamera(Image):
        '''Camera class. See module documentation for more information.
        '''
    
        play = BooleanProperty(True)
        '''Boolean indicating whether the camera is playing or not.
        You can start/stop the camera by setting this property::
            # start the camera playing at creation (default)
            cam = Camera(play=True)
            # create the camera, and start later
            cam = Camera(play=False)
            # and later
            cam.play = True
        :attr:`play` is a :class:`~kivy.properties.BooleanProperty` and defaults to
        True.
        '''
    
        index = NumericProperty(-1)
        '''Index of the used camera, starting from 0.
        :attr:`index` is a :class:`~kivy.properties.NumericProperty` and defaults
        to -1 to allow auto selection.
        '''
    
        resolution = ListProperty([-1, -1])
        '''Preferred resolution to use when invoking the camera. If you are using
        [-1, -1], the resolution will be the default one::
            # create a camera object with the best image available
            cam = Camera()
            # create a camera object with an image of 320x240 if possible
            cam = Camera(resolution=(320, 240))
        .. warning::
            Depending on the implementation, the camera may not respect this
            property.
        :attr:`resolution` is a :class:`~kivy.properties.ListProperty` and defaults
        to [-1, -1].
        '''
    
        def __init__(self, **kwargs):
            self._camera = None
            super(MyCamera, self).__init__(**kwargs)  # `MyCamera` instead of `Camera`
            if self.index == -1:
                self.index = 0
            on_index = self._on_index
            fbind = self.fbind
            fbind('index', on_index)
            fbind('resolution', on_index)
            on_index()
    
        def on_tex(self, *l):
            self.canvas.ask_update()
    
        def _on_index(self, *largs):
            self._camera = None
            if self.index < 0:
                return
            if self.resolution[0] < 0 or self.resolution[1] < 0:
                return
    
            self._camera = core_camera # `core_camera` instead of `CoreCamera(index=self.index, resolution=self.resolution, stopped=True)`
    
            self._camera.bind(on_load=self._camera_loaded)
            if self.play:
                self._camera.start()
                self._camera.bind(on_texture=self.on_tex)
    
        def _camera_loaded(self, *largs):
            self.texture = self._camera.texture
            self.texture_size = list(self.texture.size)
    
        def on_play(self, instance, value):
            if not self._camera:
                return
            if value:
                self._camera.start()
            else:
                self._camera.stop()
    
    
    from kivy.app import App
    from kivy.lang import Builder
    from kivy.uix.boxlayout import BoxLayout
    import time
    
    Builder.load_string('''
    <CameraClick>:
        orientation: 'vertical'
        MyCamera:
            id: camera1
            resolution: (640, 480)
        MyCamera:
            id: camera2
            resolution: (640, 480)
        ToggleButton:
            text: 'Play'
            on_press: camera1.play = not camera1.play
            size_hint_y: None
            height: '48dp'
        Button:
            text: 'Capture'
            size_hint_y: None
            height: '48dp'
            on_press: root.capture()
    ''')
    
    
    class CameraClick(BoxLayout):
        def capture(self):
            '''
            Function to capture the images and give them the names
            according to their captured time and date.
            '''
            camera = self.ids['camera1']
            timestr = time.strftime("%Y%m%d_%H%M%S")
            camera.export_to_png("IMG_{}.png".format(timestr))
            print("Captured")
    
    class TestCamera(App):
    
        def build(self):
            return CameraClick()
    
    TestCamera().run()
    

    enter image description here

    关于python - Kivy相机在多个屏幕上,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61980957/

    相关文章:

    python - Kivy:编译为单个可执行文件

    c# - 将 Python re.sub 转换为 C#

    java - 使用 Canvas 正确绘制点

    C++ - vector <class> 和 std::bad_alloc

    ios - OpenCV-iOS 演示在 iPad 上以 6-10 FPS 运行,这正常吗?

    python - Kivy ScreenManager 破坏了 BoxLayout

    python - 在 kivy 语言中设置纹理环绕选项

    python - 如何计算QPolygon面积

    Python装饰器跳过被装饰函数的代码

    python - 如何在 tkinter 中用箭头连接两个状态圆圈?