[Kivy]+OpenCV Androidのカメラ画像を扱う

プログラミング

AndroidだとVideoCaptureでカメラ画像が取れない?

Pythonでカメラで撮影したをしようとしたとき、やり方を調べて割とすぐに見つかるのがOpenCVのVideoCaptureを使用して読み取る方法。

cap = cv2.VideoCapture(0)

Kivyで使う場合のサンプルもすぐに見つかると思うので特にコードは書きませんが、私もVideoCaptureを使った方法で実装を進めていました。

PC(Webカメラ)である程度目的の処理を実装したのち、Androidでの動作を確認したところ実機でカメラが映らない事態に直面しました。

バージョンの問題(?)、VideoCaptureはAndroidのカメラはサポートしていない(?)、などなど、はっきりと使えないという情報は見つからなかったのですが、解決につながる情報も得られず、別の方法を検討することに。

KivyにはCameraを扱うためのクラスが標準で用意されており、それならAndroidで実行してもカメラの映像が表示できることが確認できました。

Camera Example — Kivy 2.3.0 documentation

さて、カメラの映像が取れることは確認できたところで次の問題。

実際はカメラから取得した画像を使ってなんやかんやしたいわけで、実装もOpenCVで画像処理で進めていたのでなんとかOpenCVで扱えるようにつなげる必要あります。

前置きが少し長くなりましたが、実装サンプルを書きます。

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy import platform
from kivy.clock import Clock
from kivy.graphics.texture import Texture
from kivy.uix.camera import Camera
import cv2
import numpy as np

from kivy.core.window import Window

class CameraView(BoxLayout):
    def play(self, button):

        # self.image_capture = cv2.VideoCapture(0)

        self.cam = Camera(index=0)
        self.cam.play = True

        Clock.schedule_interval(self.update, 1.0 / 30)
        button.disabled = True

    def update(self, dt):

        # ret, frame = self.image_capture.read()
        # if ret:
        #     buf = cv2.flip(frame, 0)
        #     image_texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt='bgr')
        #     image_texture.blit_buffer(buf.tostring(), colorfmt='bgr', bufferfmt='ubyte')
        #     self.ids.camera_view.texture = image_texture

        height, width = self.cam.texture.height, self.cam.texture.width
        capture = np.frombuffer(self.cam.texture.pixels, np.uint8)
        capture = capture.reshape(height, width, 4)
        frame = capture[:,:,:3]
        if type(frame).__module__ == 'numpy':
            ret = True
        if ret:
            buf = cv2.flip(frame, 0)
            image_texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt='rgb')
            image_texture.blit_buffer(buf.tostring(), colorfmt='rgb', bufferfmt='ubyte')
            self.ids.camera_view.texture = image_texture

class TestCameraViewApp(App):

    def build(self):
        if platform == "android":
            Window.bind(on_keyboard=self.key_input)
            
            from android.permissions import request_permissions, Permission
            request_permissions([Permission.CAMERA])

        return CameraView()
    
    def key_input(self, window, key, scancode, codepoint, modifier):
        if key == 27:
            return True
        else:
            return False

if __name__ == '__main__':
    TestCameraViewApp().run()
<CameraView>:
    camera_view:camera_view
    BoxLayout:
        orientation: 'vertical'
        Image:
            id: camera_view
        Button:
            text: 'play'
            size_hint_y: 0.1
            on_press: root.play(self)

OpenCVは画像をNumPyの配列で扱っているので、それに合わせて変換してあげれば、あとは同じように扱えるみたいです。見落としがちな点として、OpenCVは色をBGRで管理しているのでVideoCaptureで読み取ったときと違いが出てきます。

一応、コメントでOpenCVのVideoCaptureでの実装も入れているので変更イメージになればと思います。

ちなみに、上記の方法で VideoCapture での実装と同等のことが行えるのはPCで確認したのですが、Androidの実機で確認すると、カメラからとれる画像が反転していたり、横向きで取得されたりします。

機種やアプリで想定している向きによる違いがあるかと思いますが、確認してもうひと手間必要かもしれません。

VideoCapture からの置き換えの主旨とは少し外れますが、変換例を書いておきます。

if platform == 'android':
    frame = cv2.flip(frame, 0)
    frame = cv2.rotate(frame, cv2.ROTATE_90_CLOCKWISE)

以上です。

より細かくカメラを扱うためには Camera API(android.hardware.camera2)とかを使わないといけないと思いますが、使いこなすのに時間がかかりそうだったので、出回ってるサンプルから極力手間の少ない変更でAndroidでも動くようにしてみました。

(参考)

Convert a kivy texture to opencv image
I am trying to convert my kivy texture to an image format so that i can process it using opencv (cv2). I tried reading the texture using read() and using cv2.im...

コメント

タイトルとURLをコピーしました