Issue
I've searched about it and found "scatter", but scatter use for image. I want to zoom it with live camera.
Anyone know how can I do that?
This is code example I've written, but it doesn't work.
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()
Solution
Here is source code for widget Camera.
It has method on_tex()
which gets texture
from real camera
def on_tex(self, camera):
self.texture = texture = camera.texture
self.texture_size = list(texture.size)
self.canvas.ask_update()
texture
has method get_region()
which can be used to get only some part of image -
self.texture.get_region(x, y, width, height)
and this way you can create zoom
effect - when you have allow_stretch: True
.
Here is class which gets only some region.
I assumed that camera gives image with size 640x480
but it would need to get value from variable resolution
in Camera
.
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()
And now in CameraClick
I can change values region_x, region_y, region_w, region_h
to create zoom
effect.
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
It many need changes
- use
resolution
to get real camera size instead hardcoded640
,480
. - use buttons, keyboard or mouse to move image to see other regions.
If you run code with allow_stretch: False
then it gives smaller image instead of resizing it - so it would need different method. It would need to get texture, rescale it and crop to expected region.
Full working code.
EDIT:
I added Capture
. It needs super().on_touch_down(touch)
in on_touch_down
to execute on_press
,on_release
, etc.
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()
Maybe it should be done as wrapper class which gets Camera
as argument.
BTW:
If you would need to display together original image and zoomed image then you may need code from my answer for question Kivy Camera on multiple screen
Answered By - furas