如何安排kivy相机连续读取纹理?

2024-05-20 02:32:00 发布

您现在位置:Python中文网/ 问答频道 /正文

我一直在尝试创建一个kivy相机扫描仪从许多来源(我会使用zbarcam如果我可以,但花园.xcamera模块不会导入,因此我正在尝试创建类似的内容)。在

问题

问题是相机不能连续读取或更新纹理,也没有一种方法可以让我从相机捕捉一帧又一帧的图像。这意味着我只在初始化时得到纹理。在

尝试

首先,我试着安排一个每0.5秒更新一次纹理实例的事件。我无法获取相机的纹理实例,因为在加载相机时有一些延迟,这导致了一个错误。

其次,我在kv字符串中创建了一个on_texture事件,但它只在初始化时读取纹理。

第三,我稍后在python脚本中尝试绑定on_texture事件,方法是创建一个绑定函数并将其作为调度事件调用。它甚至没有得到实例。

第四,我created_triggersask_update(callbacks)事件,但是在相机实例化崩溃脚本之前,脚本再次加载到fast。

第五,我注意到有一个kivy.core.video模块包含一个on_frame属性。我重新编写了脚本以将其与kivy.uix.video模块结合使用,但注意到如果不先加载视频文件,视频就无法运行。在

代码

import kivy
import gi
kivy.require('1.11.1')
gi.require_version('Gst', '1.0')

from collections import namedtuple
from PIL import Image
from kivy.app import App
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import ListProperty, ObjectProperty
from kivy.uix.camera import Camera
import time
from gi.repository import Gst
import pyzbar.pyzbar
from kivy.uix.modalview import ModalView

Builder.load_string('''
#: import Window kivy.core.window.Window
<ScanPreview>:
    auto_dismiss: False
    size_hint_x: 0.6
    size_hint_y: None
    height: Window.height / 9
    pos_hint: {'top':0.7, 'x': 0.1}
    background_normal: ''
    background_color: (1, 1, 1, 0)
    background: 'white.png'
    Label:
        id: sc_data
        text: 'See me...'
<ScanCamera>:
    orientation: 'vertical'
    The_Camera:
        id: camera
        resolution: root.resolution
        on_texture: root._on_texture(camera)
    ToggleButton:
        text: 'Stop'
        on_press: camera.play = not camera.play
        size_hint_y: None
        height: '48dp'
''')
class ScanPreview(ModalView):
    pass
class The_Camera(Camera):
    pass
class ScanCamera(BoxLayout):
    resolution = ListProperty([640, 480])
    symbols = ListProperty([])
    code_types = ListProperty(set(pyzbar.pyzbar.ZBarSymbol))
    cam_cam = ObjectProperty(The_Camera())
    the_preview = ObjectProperty(ScanPreview())
    Symb = namedtuple('Symb', ['type','data'])
    def __init__(self, **kwargs):
        super(ScanCamera, self).__init__(**kwargs)
        self.cam_cam.play = True
    def _on_texture(self, instance):
        #source: https://github.com/kivy-garden/garden.zbarcam/blob/develop
        #/zbarcam/zbarcam.py
        print(instance)
        if  not instance.texture == None:
            print(instance.texture)
            self.symbols = self._detect_qrcode_frame(
                texture=instance.texture, code_types=self.code_types)
    def _detect_qrcode_frame(cls, texture, code_types):
        image_data = texture.pixels
        size = texture.size
        #source: https://github.com/kivy-garden/garden.zbarcam/blob/develop
        #/zbarcam/zbarcam.py
        # Fix for mode mismatch between texture.colorfmt and data returned
        #by
        # texture.pixels. texture.pixels always returns RGBA, so that 
        #should
        # be passed to PIL no matter what texture.colorfmt returns. refs:
        # https://github.com/AndreMiras/garden.zbarcam/issues/41
        pil_image = Image.frombytes(mode='RGBA', size=size,
                                        data=image_data)
        symbols = []
        print(pil_image)
        print(size)
        print(texture.tex_coords)
        print(texture.target)
        codes = pyzbar.pyzbar.decode(pil_image, symbols=code_types)
        for code in codes:
            symbol = CameraClick.Symb(type=code.type, data=code.data)
            symbols.append(symbol)
        print(symbols)
        return symbols

class TestCamera(App):
    title = 'Scan Camera'
    def build(self):
        return ScanCamera()
    def on_stop(self):
        cc = The_Camera()
        print('Stop')
        cc.play = False
    def on_pause(self):
        return True
    def on_resume(self):
        pass

TestCamera().run()

期望结果

相机的纹理必须不断更新,这将允许pyzbar和PIL模块解码纹理?在


Tags: fromimportselfdatasizeoncodecamera
1条回答
网友
1楼 · 发布于 2024-05-20 02:32:00

我不知道这是怎么做到的,但当我回答了我自己的问题时,我会把答案贴出来并做上标记。在

回答

因此,我通过使用下面的示例代码:https://kivy-fork.readthedocs.io/en/latest/_modules/kivy/uix/camera.html并混入zbarcam的一些函数来解决我的问题。

通过在on_texture调用中使用self.canvas.ask_update(),它将更新texture。我已经添加到代码,它现在不断更新纹理,并将条形码打印到一个切换按钮。目前我已经在Ubuntu仿生海狸上测试过了。本周末将在android上进行测试。在

答案代码

import kivy
import gi
kivy.require('1.11.1')
gi.require_version('Gst', '1.0')

from collections import namedtuple
from PIL import Image as Img
from kivy.app import App
from gi.repository import Gst
import pyzbar.pyzbar
from kivy.uix.image import Image
from kivy.core.camera import Camera as CoreCamera
from kivy.properties import NumericProperty, ListProperty, \
    BooleanProperty, ObjectProperty
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
from kivy.core.window import Window

Builder.load_string('''
#: import Window kivy.core.window.Window
<ScanCamera>:

<BarcWin>:
    ActionBar:
        pos_hint: {'top': 1, 'right': 1}
        color: (1,1,1,1)
        canvas.before:
            Color:
                rgba: (0,0,0,1)
            Rectangle:
                pos: self.pos
                size: self.size
        ActionView:
            use_separator: True
            ActionPrevious:
                title: ''
                with_previous: False
                app_icon: ''
            ActionButton:                
                color: (0,0,0,1)
                background_normal: ''
                Image:
                    source: 'gear_2.png'
                    center_y: self.parent.center_y
                    center_x: self.parent.center_x
                    size: self.parent.width /1.7, self.parent.height/ 1.7
                    allow_stretch: True
            ActionButton:
                color: (0,0,0,1)
                size_hint_x: 0.09
                background_normal: ''
                Image:
                    source: 'dustbin_backgrnd_792521.png'
                    center_y: self.parent.center_y
                    center_x: self.parent.center_x
                    size: self.parent.width /1.7, self.parent.height/ 1.7
                    allow_stretch: True
    ScanCamera:
        pos_hint: {'top': 0.9, 'right': 1}
        size_hint: [1, 0.8]
        canvas.before:
            PushMatrix
            Rotate:
                angle: 0
                origin: self.center
        canvas.after:
            PopMatrix
            Line:
                width: 2.
                rectangle: (self.x + 40, self.y + 40, self.width/1.1, self.height/1.12)
    ToggleButton:
        id: show_bcode
        pos_hint: {'bottom': 1, 'right': 1}
        size_hint: [1, 0.1]
        color: (1,1,1,1)
        background_color: (0,0,0,0)
        background_normal: ''
        canvas.before:
            Color:
                rgba: (.18,.36,.61,1) if self.state=='down' else (0,0,0,1) 
            Rectangle:
                pos: self.pos
                size: self.size
        text: 'Hier kom die barcode...'


''')
class BarcWin(FloatLayout):
    cam_cam = ObjectProperty(None)
    def __init__(self, **kwargs):
        super(BarcWin, self).__init__(**kwargs)
        self.cam_cam = ScanCamera()
    def accept_in(self):
        print('In')
    def accept_out(self):
        print('Out')
class ScanCamera(Image):
    play = BooleanProperty(True)
    index = NumericProperty(-1)
    resolution = ListProperty([Window.width, Window.height])
    symbols = ListProperty([])
    code_types = ListProperty(set(pyzbar.pyzbar.ZBarSymbol))
    Symb = namedtuple('Symb', ['type','data'])
    app_ini_ = ObjectProperty(None)
    got_bcode = BooleanProperty(False)
    def __init__(self, **kwargs):
        self._camera = None
        super(ScanCamera, self).__init__(**kwargs)
        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()
        self.app_ini_ = App.get_running_app()

    def on_tex(self, *l):
        self.canvas.ask_update()
        if not self.texture == None:
            self.symbols = self._detect_qrcode_frame(texture=self.texture, code_types=self.code_types)
            if not self.symbols == []:
                for s in self.symbols:
                    if s.data:
                        if s.data.decode('utf-8') != "":
                            self.app_ini_.root.ids.show_bcode.text = s.data.decode('utf-8')

    def _on_index(self, *largs):
        self._camera = None
        if self.index < 0:
            return
        if self.resolution[0] < 0 or self.resolution[1] < 0:
            return
        #first init of corecamera object
        self._camera = CoreCamera(index=self.index,
                                  resolution=self.resolution, stopped=True)

        #when camera loads call _camera_loaded method to bind corecamera method with uix.image texture
        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):
        #bind camera texture with uix.image texture that is still equal to None
        self.texture = self._camera.texture
    def on_play(self, instance, value):
        if not self._camera:
            return
        if value:
            self._camera.start()
        else:
            self._camera.stop()
    def _detect_qrcode_frame(self, texture, code_types):
        if not self.got_bcode:
            image_data = texture.pixels
            size = texture.size
            # Fix for mode mismatch between texture.colorfmt and data returned by
            # texture.pixels. texture.pixels always returns RGBA, so that should
            # be passed to PIL no matter what texture.colorfmt returns. refs:
            # https://github.com/AndreMiras/garden.zbarcam/issues/41
            pil_image = Img.frombytes(mode='RGBA', size=size,
                                            data=image_data)
            bcode = []
            codes = pyzbar.pyzbar.decode(pil_image, symbols=code_types)
            #print(pil_image, type(pil_image), dir(pil_image))
            if codes != []:
                for code in codes:
                    symbol = self.Symb(type=code.type, data=code.data)
                    bcode.append(symbol)
                return bcode
            else:
                self.got_bcode = False
                return []
class TestCamera(App):
    title = 'Scan Camera'
    def build(self):
        return BarcWin()
    def on_stop(self):
        cc = ScanCamera()
        print('Stop')
        cc._camera.stop()
    def on_pause(self):
        return True
    def on_resume(self):
        pass

TestCamera().run()


相关问题 更多 >