我正试图在Kivy:中构建一个基于OpenCv的相机Android应用程序
main.py
import kivy
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.camera import Camera
import cv2
from kivy.uix.image import Image
from kivy.clock import Clock
from kivy.graphics.texture import Texture
import numpy as np
class KivyCamera(Image):
def __init__(self, capture, fps, **kwargs):
super(KivyCamera, self).__init__(**kwargs)
self.capture = capture
Clock.schedule_interval(self.update, 1.0 / fps)
def update(self, dt):
ret, frame = self.capture.read()
if ret:
# convert it to texture
buf1 = cv2.flip(frame, 0)
buf = buf1.tostring()
image_texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt='bgr')
image_texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
# display image from the texture
self.texture = image_texture
class MainApp(App):
def build(self):
self.capture = cv2.VideoCapture(0)
self.camera = KivyCamera(capture=self.capture, fps=30)
return self.camera
if __name__== "__main__":
MainApp().run()
buildozer.spec
[app]
title = Test_app
package.name = myapp
package.domain = org.test
source.dir = .
source.include_exts = py,png,jpg,kv,atlas,xml
version = 0.1
requirements = python3,kivy,numpy,opencv
orientation = portrait
# Android specific
fullscreen = 0
android.permissions = INTERNET, ACCESS_FINE_LOCATION, WRITE_EXTERNAL_STORAGE, CAMERA
android.arch = armeabi-v7a
[buildozer]
log_level = 2
warn_on_root = 1
代码在windows中运行成功。然后我用buildozer为android构建了代码,当我打开android应用程序时,它会显示一个黑色屏幕,屏幕左角有一个小正方形。我认为cv2.VideoCapture((工作不正常。因此,我将cv2.VideoCapture(0(更改为cv2.VideoCapture(-1(,并更改为cv2.VideoCapture(1(。但两者都不起作用。
有人能帮我吗?
我有两个解决方案可以让它在Android上工作。
解决方案1:
我的灵感来自kivy的android opencv演示,在GitHub:link上找到!因为Kivy不再支持Python2,下面是我的Python3解决方案。
主.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.graphics.texture import Texture
from kivy.uix.camera import Camera
from kivy.lang import Builder
import numpy as np
import cv2
Builder.load_file("myapplayout.kv")
class AndroidCamera(Camera):
camera_resolution = (640, 480)
counter = 0
def _camera_loaded(self, *largs):
self.texture = Texture.create(size=np.flip(self.camera_resolution), colorfmt='rgb')
self.texture_size = list(self.texture.size)
def on_tex(self, *l):
if self._camera._buffer is None:
return None
frame = self.frame_from_buf()
self.frame_to_screen(frame)
super(AndroidCamera, self).on_tex(*l)
def frame_from_buf(self):
w, h = self.resolution
frame = np.frombuffer(self._camera._buffer.tostring(), 'uint8').reshape((h + h // 2, w))
frame_bgr = cv2.cvtColor(frame, 93)
return np.rot90(frame_bgr, 3)
def frame_to_screen(self, frame):
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
cv2.putText(frame_rgb, str(self.counter), (20, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2, cv2.LINE_AA)
self.counter += 1
flipped = np.flip(frame_rgb, 0)
buf = flipped.tostring()
self.texture.blit_buffer(buf, colorfmt='rgb', bufferfmt='ubyte')
class MyLayout(BoxLayout):
pass
class MyApp(App):
def build(self):
return MyLayout()
if __name__ == '__main__':
MyApp().run()
myapplayout.kv
<MyLayout>
orientation: 'vertical'
size: root.width, root.height
AndroidCamera:
index: 0
resolution: self.camera_resolution
allow_stretch: True
play: True
在buildozer.spec:
requirements = python3,kivy==2.0.0,opencv==4.5.2,numpy
android.permissions = CAMERA
解决方案2:
我从显示的相机图像的小部件中获得帧,每秒4次。如果你不需要每一帧,也不需要在帧的顶部绘制文本或框之类的东西,那么这是一个简单的解决方案。
主.py
from kivy.app import App
from kivy.uix.camera import Camera
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.clock import Clock
import numpy as np
import cv2
Builder.load_file('myapplayout.kv')
class AndroidCamera(Camera):
camera_resolution = (640, 480)
cam_ratio = camera_resolution[0] / camera_resolution[1]
class MyLayout(BoxLayout):
pass
class MyApp(App):
counter = 0
def build(self):
return MyLayout()
def on_start(self):
Clock.schedule_once(self.get_frame, 5)
def get_frame(self, dt):
cam = self.root.ids.a_cam
image_object = cam.export_as_image(scale=round((400 / int(cam.height)), 2))
w, h = image_object._texture.size
frame = np.frombuffer(image_object._texture.pixels, 'uint8').reshape(h, w, 4)
gray = cv2.cvtColor(frame, cv2.COLOR_RGBA2GRAY)
self.root.ids.frame_counter.text = f'frame: {self.counter}'
self.counter += 1
Clock.schedule_once(self.get_frame, 0.25)
if __name__ == "__main__":
MyApp().run()
myapplayout.kv
<MyLayout>:
orientation: 'vertical'
size: root.width, root.height
GridLayout:
rows: 2
RelativeLayout:
size_hint: 1, 0.8
AndroidCamera:
index: 0
id: a_cam
resolution: self.camera_resolution
allow_stretch: True
play: True
canvas.before:
PushMatrix
Rotate:
angle: -90
origin: self.center
Scale:
x: self.cam_ratio
y: self.cam_ratio
origin: self.center
canvas.after:
PopMatrix
Label:
size_hint: 1, 0.2
id: frame_counter
font_size: self.height * 0.4
text: ''
buildozer.spec与解决方案1中的相同。
最后,不要忘记在安装后为相机添加权限。(我的代码中没有权限请求。(