【发布时间】:2020-05-24 02:32:56
【问题描述】:
我正在尝试在具有两个屏幕的 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()
现在可以正常使用了
【问题讨论】:
-
当
cv2无法从相机读取数据时,它不会引发错误,但它会将None放入frame,您必须在显示之前检查if frame is not None。有时它可能会创建几个空帧 (None) 来预热。 -
我该如何解决?
-
先用
if frame is not None: display frame看看有没有什么变化。 -
您似乎为同一个相机创建了两个实例——
Cam类的实例——这可能会产生问题。您可能只需要创建一个Cam实例,而其他 wigets 则必须从同一实例中读取帧。 -
是的,这是问题所在,在我看来这应该可以。我不知道如何在另一个小部件中读取框架
标签: python opencv kivy kivy-language