AndroidだとVideoCaptureでカメラ画像が取れない?
Pythonでカメラで撮影したをしようとしたとき、やり方を調べて割とすぐに見つかるのがOpenCVのVideoCaptureを使用して読み取る方法。
cap = cv2.VideoCapture(0)
Kivyで使う場合のサンプルもすぐに見つかると思うので特にコードは書きませんが、私もVideoCaptureを使った方法で実装を進めていました。
PC(Webカメラ)である程度目的の処理を実装したのち、Androidでの動作を確認したところ実機でカメラが映らない事態に直面しました。
バージョンの問題(?)、VideoCaptureはAndroidのカメラはサポートしていない(?)、などなど、はっきりと使えないという情報は見つからなかったのですが、解決につながる情報も得られず、別の方法を検討することに。
KivyにはCameraを扱うためのクラスが標準で用意されており、それならAndroidで実行してもカメラの映像が表示できることが確認できました。
さて、カメラの映像が取れることは確認できたところで次の問題。
実際はカメラから取得した画像を使ってなんやかんやしたいわけで、実装も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でも動くようにしてみました。
(参考)
コメント