如何使用 Kivy 放大或缩小实时摄像头?

How can I use Kivy to zoom in or zoom out live camera?

我搜索了一下,找到了“scatter”,但是scatter用于图像。 我想用实时相机放大它。

有人知道我该怎么做吗?

这是我写的代码示例,但它不起作用。

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.lang import Builder
from kivy.properties import ObjectProperty
from kivy.uix.screenmanager import ScreenManager , Screen
from kivy.uix.image import Image
from kivy.core.window import Window
from kivy.base import runTouchApp
from kivymd.app import MDApp
from kivy.uix.boxlayout import BoxLayout
import time
from kivy.core.window import Window
from kivy.uix.camera import Camera
from kivy.uix.scatter import Scatter
from kivy.uix.relativelayout import RelativeLayout
from kivy.properties import NumericProperty


Window.size = (1600, 850)

class MyCamera(Camera):
    
    region_x = NumericProperty(0)
    region_y = NumericProperty(0)
    region_w = NumericProperty(1600)
    region_h = NumericProperty(850)
    
    def on_text(self,camera):
        self.texture = texture = camera.texture
        
        self.texture = self.texture.get_region(self.region_x, self.region_y, self.region_w, self.region_h)
        self.texture_size = list(texture.size)
        self.canvas.ask_update()



class MainPage(Screen):
    pass

class WindowManager(ScreenManager):
    pass


class CameraClick(Screen):
    scale = NumericProperty(1)


    def on_touch_down(self, touch):
        if touch.is_mouse_scrolling:
            if touch.button == 'scrolldown':
                print("down")
                if self.scale <10:
                    self.scale *= 1.1
                    self.ids['camera'].region_w /= 1.1
                    self.ids['camera'].region_h /= 1.1
                    self.ids['camera'].region_x = (1600-self.ids['camera'].region_w) // 2
                    self.ids['camera'].region_y = (850-self.ids['camera'].region_h) // 2 
    
    
            elif touch.button == 'scrollup':
                print("up")
                if self.scale >1:
                    self.scale *= 0.8
                    
                    self.ids['camera'].region_w /= 0.8
                    self.ids['camera'].region_h /= 0.8
                    if(self.ids['camera'].region_w > 1600) or (self.ids['camera'].region_h >850):
                        self.ids['camera'].region_w = 1600
                        self.ids['camera'].region_h = 850
                        
                        
                    self.ids['camera'].region_x = (1600-self.ids['camera'].region_w) //2
                    self.ids['camera'].region_y = (850-self.ids['camera'].region_h) //2
                
    
    def capture(self):
        camera = self.ids['camera']
        timestr = time.strftime("%Y%m%d_%H%M%S")
        camera.export_to_png("IMG_{}.png".format(timestr))
        print("Captured")

    
            

Builder.load_string("""

#:import utils kivy.utils
<WindowManager>:
    MainPage:
    CameraClick:
    
          

<MainPage>:
    name: "main page"
                 
    BoxLayout:
        cols:1
        orientation: "horizontal"
        size: root.width , root.height
        spacing: 25
        padding: 530, 900 , 900 , 260

        Button:
            text: "take a picture"
            color: (200,250,210)
            font_size: 40
            size_hint_x: 1
            height:60
            size_hint_y: None
            width:500
            on_release: app.root.current = "camera"

                              
<CameraClick>:

    name: "camera"
    orientation: 'vertical'    
    
    MyCamera:
        id: camera
        play: True
        allow_stretch: True
        resolusion: (640,480)

    BoxLayout:
        orientation: 'vertical'
        padding: 100 , 10 , 800 , 590

        Button:
            text: 'play'
            size_hint_y: None
            size_hint_x: None
            height: '48dp'
            pos:200,200
            font_size:40
            width: 100
            height: 50
            on_press: camera.play = not camera.play
                
                
    BoxLayout:
        orientation: 'vertical'
        padding: 100 , 10 , 800 , 380

        Button:
            text: 'capture'
            size_hint_y: None
            size_hint_x: None
            height: '48dp'
            pos:200,200
            font_size:40
            width: 100
            height: 50
            on_press: root.capture()
 

    BoxLayout:
        orientation: 'vertical'
        padding: 100 , 10 , 800 , 200
        
        Button:
            text: 'ZOOM'
            size_hint_y: None
            size_hint_x: None
            height: '48dp'
            pos:100,100
            font_size:30
            width: 100
            height: 50
            on_press: root.on_touch_down()
            
            
    BoxLayout:
        orientation: 'vertical'
        padding: 50 , 10 , 800 , 730
    

        Button:
            text: 'HOME'
            size_hint_y: None
            size_hint_x: None
            height: '48dp'
            pos:200,200
            font_size:40
            width: 100
            height: 50
            on_release: app.root.current = "main page"
        
""")

class Shenacell(MDApp):
    def build(self):
        self.theme_cls.primary_palette = "BlueGray"
        return WindowManager()

if __name__ == '__main__' :
    Shenacell().run()
    
    

    

这是小部件的源代码 Camera

它有方法 on_tex() 从真实相机 texture

def on_tex(self, camera):
    self.texture = texture = camera.texture
    self.texture_size = list(texture.size)
    self.canvas.ask_update()

texture 有方法 get_region() 可以用来只获取图像的一部分 -

self.texture.get_region(x, y, width, height)

这样你就可以创建 zoom 效果 - 当你有 allow_stretch: True.

这里是 class,它只获取了一些区域。
我假设相机提供大小为 640x480 的图像,但它需要从 Camera.

中的变量 resolution 获取值
class MyCamera(Camera):
    
    region_x = NumericProperty(0)
    region_y = NumericProperty(0)
    region_w = NumericProperty(640)
    region_h = NumericProperty(480)
    
    def on_tex(self, camera):
        
        self.texture = texture = camera.texture

        # get some region
        self.texture = self.texture.get_region(self.region_x, self.region_y, self.region_w, self.region_h)

        self.texture_size = list(texture.size)
        self.canvas.ask_update()

现在 CameraClick 我可以更改值 region_x, region_y, region_w, region_h 以创建 zoom 效果。

class CameraClick(Screen):

    scale = NumericProperty(1)
    
    def on_touch_down(self, touch):
        
        if touch.is_mouse_scrolling:

            if touch.button == 'scrolldown':
                print("down")
                if self.scale < 10:
                    self.scale *= 1.1

                    # scale region size
                    self.ids['camera'].region_w /= 1.1
                    self.ids['camera'].region_h /= 1.1

                    # center region
                    self.ids['camera'].region_x = (640-self.ids['camera'].region_w) // 2
                    self.ids['camera'].region_y = (480-self.ids['camera'].region_h) // 2
                    
            elif touch.button == 'scrollup':
                print("up")
                if self.scale > 1:
                    self.scale *= 0.8

                    # scale region size
                    self.ids['camera'].region_w /= 0.8
                    self.ids['camera'].region_h /= 0.8
                    if (self.ids['camera'].region_w > 640) or (self.ids['camera'].region_h > 480):
                        self.ids['camera'].region_w = 640
                        self.ids['camera'].region_h = 480

                    # center region
                    self.ids['camera'].region_x = (640-self.ids['camera'].region_w) // 2
                    self.ids['camera'].region_y = (480-self.ids['camera'].region_h) // 2

很多地方需要改变

  • 使用 resolution 获取真实相机尺寸而不是硬编码 640,480.
  • 使用按钮、键盘或鼠标移动图像以查看其他区域。

如果您 运行 使用 allow_stretch: False 编码,那么它会提供更小的图像而不是调整它的大小 - 所以它需要不同的方法。它需要获取纹理、重新缩放并裁剪到预期区域。


完整的工作代码。

编辑:

我添加了Captureon_touch_down中需要super().on_touch_down(touch)才能执行on_presson_release

from kivymd.app import MDApp
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.camera import Camera
from kivy.lang import Builder
from kivy.properties import NumericProperty
import time


class MyCamera(Camera):

    region_x = NumericProperty(0)
    region_y = NumericProperty(0)
    region_w = NumericProperty(640)
    region_h = NumericProperty(480)

    def on_tex(self, camera):

        self.texture = texture = camera.texture

        # get some region
        self.texture = self.texture.get_region(self.region_x, self.region_y, self.region_w, self.region_h)

        self.texture_size = list(texture.size)
        self.canvas.ask_update()


class WindowManager(ScreenManager):
    pass


class CameraClick(Screen):

    scale = NumericProperty(1)

    def on_touch_down(self, touch):
        #print('[DEBUG] on_touch_down')

        super().on_touch_down(touch)  # run original `Screen.on_touch_down` which runs `on_press`, `on_release`

        if touch.is_mouse_scrolling:
            if touch.button == 'scrolldown':
                print("down: (zoom in) ", self.scale)
                if self.scale < 10:
                    self.scale *= 1.1
                    # scale region size
                    self.ids['camera'].region_w /= 1.1
                    self.ids['camera'].region_h /= 1.1
                    # center region
                    self.ids['camera'].region_x = (640-self.ids['camera'].region_w) // 2
                    self.ids['camera'].region_y = (480-self.ids['camera'].region_h) // 2
                
            elif touch.button == 'scrollup':
                print("  up: (zoom out)", self.scale)
                if self.scale > 1:
                    self.scale *= 0.8
                    # scale region size
                    self.ids['camera'].region_w /= 0.8
                    self.ids['camera'].region_h /= 0.8
                    if (self.ids['camera'].region_w > 640) or (self.ids['camera'].region_h > 480):
                        self.ids['camera'].region_w = 640
                        self.ids['camera'].region_h = 480
                    # center region
                    self.ids['camera'].region_x = (640-self.ids['camera'].region_w) // 2
                    self.ids['camera'].region_y = (480-self.ids['camera'].region_h) // 2

    def capture(self):
        camera = self.ids['camera']
        filename = time.strftime("IMG_%Y%m%d_%H%M%S.png")
        camera.export_to_png(filename)
        print("Captured:", filename)


Builder.load_string("""
<WindowManager>:
    CameraClick:

<CameraClick>:
    name: "camera"
    orientation: 'vertical'
    MyCamera:
        id: camera
        play: True
        allow_stretch: True
    Button:
        text: 'Capture'
        size_hint_y: None
        size_hint_x: None
        on_press: root.capture()
""")


class Shenacell(MDApp):
    def build(self):
        return WindowManager()

if __name__ == '__main__':
    Shenacell().run()

也许它应该作为包装器 class 完成,它将 Camera 作为参数。


顺便说一句:

如果您需要同时显示原始图像和缩放图像,那么您可能需要我对问题

的回答中的代码