PythonのGUIライブラリKivyでOpenCVで読み込んだ画像を表示する方法です。
またPillowでの表示する方法についても説明します
補足
OpenCVで画像を表示する方法については@IMUさんのツィートを参考にしてこちらでの検証結果を追加したものになります。
ようやくKivy上でOpenCVで読み込んだ画像開けたぁぁぁ!
— IMU (@imu_prographics) 2017年10月3日
ただ、エンディアンの違いなのか上下反転してるからKivyに渡すたびに変換が必要そう pic.twitter.com/IDXVwTfGqe
今回使用した画像ですがぱたくそから借りました。
サンプルコードについて
今回の内容をGithubにあげました。
Github:https://github.com/okajun35/kivy_show_opencv_pillow
OpenCVで読み込んだ画像を表示する
大きく分けて次の2つの方法があると思います。
- 一時的に画像ファイルに書き出したして
kivy.uix.image()
で表示する - メモリ上に読み込んだ画像
をblit_buffer()
を使用して表示する
一時的に画像ファイルに書き出したして kivy.uix.image() で表示する
この方法はさほど難しくなくOpenCVで読み込んだ画像をcv2.imwrite()
を使用して保存します。
その後その保存した画像をkivy.uix.image()
を使用して表示します。
KivyのAppクラスにはuser_data_dir
でconfig.iniで設定されたデータを保存するパスを格納しています。
user_data_dir
を使用して画像を保存する場所を指定して保存します。
あとはImageを使用して保存された画像を表示します。
保存された画像に関してはAppクラスにはon_end()
といって終了時に呼ぶメソッドがあるので、
on_end()
内で保存した画像を削除します。
参考
メモリ上に読み込んだ画像をblit_buffer()を使用して表示する
一時的に画像ファイルに書き出さずメモリ上のやり取りで済ませたい場合は、blit_buffer()
を使用することでTextureオブジェクトにバイト列の情報を吐き出すことが可能です。
blit_buffer()
を使用すればOpenCVで読み込んだ画像を表示することが可能ですが次の3点に注意することが必要です。
- OpenCVの画像の幅と高さは画像オブジェクトのshape[]に入っているが、**shapeに格納されている情報は「高さ」、「横幅」、「チャンネル」の順で、Kivyのsize()は「横幅」、「高さ」**なので順番が違う。
- OpenCVの画像の色の並びはBGRでKivyの
blit_buffer
での色の並びはデフォルトではRGBなので並びが違う。 - OpenCVの原点座標は左上だが、Kivyの原点座標は左下なのでそのままだと画像が上下反転される。
この3点の対応ですがOpenCVで対応するのとKivyで対応する2パターンがあります。
OpenCVで対応する方法
コードは以下の通りです。
import numpy as np
import cv2
from PIL import Image
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.graphics.texture import Texture
from kivy.graphics import Rectangle
class MyApp(App):
title = "opencv on kivy"
def build(self):
img = cv2.imread('kai_058Kazukiya17103_TP_V.jpg',1)
if img is None:
print('load image')
sys.exit(1)
# 表示確認用
cv2.imshow('opencv_normal', img)
widget = Widget()
''' pattern1 (openCVの機能で表示) '''
img2 = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # openCVの色の並びはBGRなのでRGBに直す
img2 = cv2.flip(img2, 0) # Kivyの座標の原点は左下なので上下反転する
# OpenCVの座標shapeは「高さ」、「幅」、「チャンネル」の順番 Kivyのsizeは(幅、高さ)なので逆にする必要がある
texture = Texture.create(size=(img2.shape[1], img2.shape[0]))
texture.blit_buffer(img2.tostring())
with widget.canvas: # 描画
Rectangle(texture=texture ,pos=(0, 0), size=(img2.shape[1], img2.shape[0]))
まず、読み込んだ画像をcvtColor()
の「COLOR_BGR2RGB」で色の並びをBGRからRGBに変更します。
次にflip(<読み込んだ画像>, 0)
で画像を上下反転します。
次にKivyでテクスチャーで表示しますが、Texture.create()
でテクスチャの設定をします。
その際、size=(img2.shape[1], img2.shape[0])
で画像の横幅と高さを指定します。
あとはblit_buffer()
で画像情報をバイト列で吐き出します。
後はcanvasで、画像と同じ大きさの矩形を描画してそのテクスチャーにblit_buffer()
を行ったテクスチャーを指定することで表示できるようになります。
Kivyで対応する方法
OpenCVで表示の下準備をすることもできますが、Kivyで表示するのであればKivyで表示の準備をした方がよいかと思います。kivyで表示する場合は以下の方法になります。
''' pattern2 (kivyの機能のみで表示) '''
texture = Texture.create(size=(img.shape[1], img.shape[0]), colorfmt='bgr', bufferfmt='ubyte') # BGRモードで用意,ubyteはデフォルト引数なので指定なくてもよい
texture.blit_buffer(img.tostring(),colorfmt='bgr', bufferfmt='ubyte') # ★ここもここもBGRで指定しないとRGBになって色の表示がおかしくなる
texture.flip_vertical() # 画像を上下反転する
with widget.canvas:
Rectangle(texture=texture ,pos=(0, 0), size=(img.shape[1], img.shape[0]))
OpenCVの場合と違うのはテクスチャーを作成するcreate()
で指定する際にcolorfmt='bgr'
とすることで画像の並びをBGRに対応するようにします。※colorfmtのデフォルト値は'rgb'です。
またblit_buffer()
実行時にもcolorfmt='bgr'
を指定します。こうすることで画像の色の並びがBGRの画像でも表示できます。
さらにflip_vertical()
で画像を上下反転させます。
以上でOpenCVで処理していたのと同じように画像を表示させることができます。
参考 グレースケール画像を表示させる場合
OpenCVで処理したグレースケール画像を表示させる場合ですが、Kivyではカラーチャンネルが1個の画像は表示できません。表示させる場合はOpenCV側で疑似的に3チャンネルに変換して表示させます。
# 画像をグレイスケールに変換
gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
gray_to_rgb_img = cv2.cvtColor(gray_img, cv2.COLOR_GRAY2RGB) # bilt_bufferでは1チェンネルのグレイ画像は表示できないので3チャンネルの画像に変換する
Pillowで読み込んだ画像を表示する
Pillowでの表示はOpenCVほど難しくはないです。
以下が該当部分のコードです。
pillow_img = Image.open('kai_058Kazukiya17103_TP_V.jpg', 'r')
texture = Texture.create(size=pillow_img.size)
texture.blit_buffer(pillow_img.tobytes())
texture.flip_vertical() #
with widget.canvas:
Rectangle(texture=texture ,pos=(0, 0), size=pillow_img.size)
OpenCVと違いPillowの場合は画像の色の並びがRGBであることと画像の「横幅」、「高さ」の並びもKivyと一緒なので変換はいりません。ただし座標の原点座標がPillowは左上に対してKivyは左下が原点なのでflip_vertical()
を使用して画像を上下反転させる必要があります。