简体   繁体   English

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

[英]How to schedule the kivy camera to read the texture continuously?

I have been trying to create a kivy camera scanner from a number of sources (I would use the zbarcam if I could, but the garden.xcamera module will not import, therefore I am trying to create something similar). 我一直试图从许多来源创建一个kivy相机扫描仪(如果可以的话,我会使用zbarcam,但garden.xcamera模块将不会导入,因此我试图创建类似的东西)。

Problem 问题

The problem is the camera does not read or update the texture continuously nor is there a way that I can find to capture frame-for-frame from the camera. 问题是相机不能连续读取或更新纹理,也没有办法从相机中逐帧捕捉。 This means I only get the texture on initialization. 这意味着我只在初始化时获得纹理。

Tried 试着

Firstly, I have tried scheduling an event that will update the texture instance every 0.5 seconds. 首先,我尝试安排一个事件,每0.5秒更新一次纹理实例。 I could not get the texture instance of the camera because there is some delay in the camera to load, which caused an error. 我无法获得相机的纹理实例,因为相机有一些延迟加载,这导致错误。

Secondly, I created an on_texture event in my kv string, but it only reads the texture on initialization. 其次,我在我的kv字符串中创建了一个on_texture事件,但它只在初始化时读取纹理。

Thirdly, I tried binding the on_texture event a bit later in the python script, by creating a binding function and calling it as a scheduled event. 第三,我尝试在python脚本中稍后绑定on_texture事件,方法是创建一个绑定函数并将其作为调度事件调用。 It did not even get the instance. 它甚至没有得到实例。

Fourthly, I created_triggers and ask_update(callbacks) to the _on_texture() event, but again the script loads to fast before the camera can instantiate crashing the script. 第四,我created_triggersask_update(callbacks)_on_texture()事件,但再次脚本加载快速前摄像头可以实例崩溃的脚本。

Fifthly, I noticed there is a kivy.core.video module that contains a on_frame attribute. 第五,我注意到有一个包含on_frame属性的kivy.core.video模块。 Did re-write my script to use it in conjunction with the kivy.uix.video module, but noticed that the video cannot run without first loading a video file. 重新编写我的脚本以与kivy.uix.video模块一起使用它,但注意到视频无法在没有首先加载视频文件的情况下运行。

Code

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()

Desired result 期望的结果

The camera's texture must continuously update, which will allow the pyzbar and PIL module to decode the texture? 相机的纹理必须不断更新,这将允许pyzbar和PIL模块解码纹理?

I don't know if this is how its done, but as I answered my own question I am posting the answer and marking it as such. 我不知道这是不是这样做了,但是当我回答我自己的问题时,我发布了答案并将其标记为这样。

Answer 回答

So I managed to solve my question by using the example code here: https://kivy-fork.readthedocs.io/en/latest/_modules/kivy/uix/camera.html and mixing in some functions of zbarcam. 所以我设法通过使用示例代码解决了我的问题: https ://kivy-fork.readthedocs.io/en/latest/_modules/kivy/uix/camera.html并混合了zbarcam的一些功能。

By using self.canvas.ask_update() in the on_texture call it updates the texture . 通过在on_texture调用中使用self.canvas.ask_update() ,它会更新texture I have added to the code and it now updates the texture continuously and prints the barcode to a togglebutton. 我已添加到代码中,它现在不断更新纹理并将条形码打印到togglebutton。 At the moment I have just tested it on Ubuntu Bionic Beaver. 目前我刚刚在Ubuntu Bionic Beaver上测试过它。 Will test it on android this weekend. 将在本周末在Android上测试它。

Answer code 答案代码

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()


声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM