Kivy使用OpenCV在Android上的相机应用程序显示黑屏

8

我试图基于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

代码在Window系统中运行成功。然后我使用buildozer为Android构建代码。当我打开Android应用时,屏幕显示一个黑屏, 屏幕左上角有一个小方块。我认为cv2.VideoCapture()函数没有正常工作。因此,我把cv2.VideoCapture(0)改成了cv2.VideoCapture(-1)和cv2.VideoCapture(1),但是都无法正常工作。

是否有人可以帮我解决这个问题?


你解决了这个问题吗?在Android上使用OpenCV和Kivy是可能的吗? - roccolocko
还没有。您可以在这里找到一个线程 https://gist.github.com/ExpandOcean/de261e66949009f44ad2 - Aswin
我不确定这是否是答案,所以请勿在答案部分写入。我在Android Native中使用了OpenCV,并遇到了与您相同的问题-黑屏,但可以通过调用相机视图函数cameraView.setCameraPermissionGranted()来解决问题。参考:https://dev59.com/FLroa4cB1Zd3GeqPl4XC 还要检查运行时权限,在应用程序设置中强制授予权限并重试。 - dphans
@dphans 我正在尝试通过Kivi在Android上构建OpenCV相机。 - Aswin
1个回答

5

我有两种解决方案可以让它在安卓上运行。


方案一:

我受到了 GitHub 上的 kivy-for-android-opencv-demo 的启发:链接!因为 Kivy 不再支持 Python2,这里是我针对 Python3 的解决方案。

main.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次。如果你不需要每一帧,也不需要在帧上绘制文本或框之类的内容,那么这是一个很简单的解决方案。

main.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中相同。


最后,在安装后不要忘记添加相机权限。(我的代码中没有包含权限请求。)


1
如果您遇到编译错误,例如“opencv需要Android SDK工具修订版14或更高版本”,我有一个简单的视频教程,介绍如何设置您的环境:https://www.youtube.com/watch?v=c4tuSxSoERY。我是在最新的Ubuntu LTS版本(20.04)上完成的。 - Norbert Tiborcz
太棒了!!这个视频真的很有用。 - Ahmad Alhilal
部署并在华为手机上运行APK后,它没有显示任何UI界面。只是显示“正在加载...”,然后无故关闭。 - Ahmad Alhilal
我需要指定android.api = 27以与我的智能手机的Android版本相同吗? - Ahmad Alhilal
1
为了找出应用程序崩溃的原因,始终通过adb调试应用程序。(adb logcat -s "python") - Norbert Tiborcz
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接