OpenCV基礎プログラミング

2020/05/19

AI CORE XスターターキットとOpenVINO™ですぐに始めるディープラーニング推論」シリーズの4回目記事です。

このシリーズは、「ディープラーニングとは何か」から始まり、「各種ツールの使い方」「プログラミング基礎」「プログラミング応用・実践」までをステップバイステップでじっくり学び、自分で理解してオリジナルのAIアプリケーションが作れるようになることを目指しています。

第4回目はOpenCVを使って画像を入力し、画像処理を行い、画像を出力します。また入力として静止画だけでなくWebカメラでリアルタイムな映像も取り扱います!

【 目次 】


OpenCVとは?

CVとはComputer Visionの略です。
OpenCVはインテルのComputer Vision向けオープンソースライブラリで、具体的には、画像処理、画像解析、機械学習などが行えます。
OpenCVをPythonで使うためには cv2 モジュールを import する必要があります。cv22 はバージョンとは関係ないのでご注意ください。OpenCV2でもOpenCV3でもOpenCV4でも、import cv2 です。

画像ファイルの読み込みと表示

まず表示したい画像を実行プログラムと同じディレクトリに用意してください。ファイル名はcat.jpgとします

参考:猫の写真

こちらがコードです

import cv2
 
img = cv2.imread('cat.jpg')
cv2.imshow('image', img)
 
cv2.waitKey(0)
cv2.destroyAllWindows()

cv2.imreadで画像ファイルを読み込みます。また、cv2.imshowで画像を表示します。第一引数は画像表示ウィンドウの名前で、任意の文字列を付けることができます。第二引数にimreadで読み込んだ画像データを指定します。

imread(画像ファイル)

imshow(ウィンドウ名, 画像)

Ubuntu上では、このような感じで表示されます。画像が表示ウィンドウにフォーカスがあるとき(タイトルバーの背景色が薄いグレーで白い文字がはっきり見えるとき)、何らかのキーを押すとプログラムが終了します。

cv2.waitKey(0)はキーボードが押されるまで処理を待つ関数です。この関数がないと、プログラムがすぐ終了してしまうため、画像表示を見ることができません。
cv2.waiKeyは引数の数値だけ処理を待つという関数です。数値の単位はms(ミリセカンド)です。例えば cv2.waitKey(1000)であれば1秒待ちます。ただし、引数が0の場合は特別で、0秒待つという意味ではなく、キーボードが押されるまでずっと処理を待つという意味になります。

最後のcv2.destroyAllWindowsは、全てのウィンドウを閉じるという関数です。

画像サイズ変更

cv2.resizeを使うと画像サイズを変更できます。

resize(画像, 画像サイズ(width, height))

第一引数に入力画像、第二引数に タプル形式で画像サイズの幅と高さを指定します。
以下のコードは、先程のコードに1行追加しただけです。実行すると画像ウィンドウが400×300のサイズに小さくなって表示されることが確認できます。

import cv2
 
img = cv2.imread('cat.jpg')
img = cv2.resize(img, (400300))
cv2.imshow('image', img)
 
cv2.waitKey(0)
cv2.destroyAllWindows()

文字や図形の描画

OpenCVでは、画像を表示しているウィンドウに文字や図形を重ね合わせて描画することができます。ただし、文字や図形の描画はimshowより前に書かないと反映されませんのでご注意ください。

文字列描画

cv2.putTextは文字列を描画する関数です。引数は以下のように指定します。
位置(X, Y)は、文字列の左下角の座標で指示します。

putText(画像, 文字列, 位置(X, Y), フォント, 倍率, 色(B, G, R), 線の太さ)

色の順番はOpenCVの場合、RGBではなくBGRの順番であることに注意してください。また、倍率とはフォントの倍率のことで小数で設定可能です。一般的なフォントサイズとは単位が異なります。
以下は、最初のサンプルコードにcv2.putTextを1行追加した例です

import cv2
 
img = cv2.imread('cat.jpg')
cv2.putText(img, 'Stay Home', (250300), cv2.FONT_HERSHEY_SIMPLEX, 2.0, (0255255), 8)
cv2.imshow('image', img)
 
cv2.waitKey(0)
cv2.destroyAllWindows()

長方形の描画

cv2.rectangleは長方形を描画する関数です。引数は以下のように指定します。

rectangle(画像, 左上座標(X, Y), 右下座標(X, Y), 色(B, G, R), 線の太さ)

線の太さを -1 に設定すると長方形の中身を塗りつぶします。
以下は、最初のサンプルコードにcv2.rectangletを1行追加した例です

import cv2
 
img = cv2.imread('cat.jpg')
cv2.rectangle(img, (120220), (270260), (202020), -1)
cv2.imshow('image', img)
 
cv2.waitKey(0)
cv2.destroyAllWindows()

円の描画

cv2.circleは円を描画する関数です。引数は以下のように指定します。

circle(画像, 中心座標(X, Y), 半径, 色(B, G, R), 線の太さ)

線の太さを -1 に設定すると長方形の中身を塗りつぶします。
以下は、最初のサンプルコードにcv2.circleを1行追加した例です

import cv2
 
img = cv2.imread('cat.jpg')
cv2.circle(img, (180420), 70, (0255255), -1)
cv2.imshow('image', img)
 
cv2.waitKey(0)
cv2.destroyAllWindows()

他にもcv2.lineで直線描画、cv2.ellipseで楕円描画などありますので、色々調べてみて下さい。

クラスを活用したPNG透過画像表示

クラスとは

クラスと聞くと初心者の方は身構えてしまうと思いますが、ご安心ください。
クラスは、主に「クラスを自分で作成する場合」と「誰かが作ったクラスを使う場合」の2通りに分けることができます。前者はちょっと勉強が必要で、「コンストラクタ」やら「継承」やら「self」やらややこしい用語やお作法も理解する必要がありますが、後者の場合は簡単です。関数に毛が生えたようなものというイメージでOKです。

関数は「実行」するだけだったのに対し、クラスには「登録」と「実行」の2ステップが必要です。
この「登録」と「実行」を小難しい言葉で言い換えると以下のようになります

「インスタンス」とは何か? 良く例えられるのが「たい焼き」の例です。「クラス」がたい焼きを作る「たい焼き器」で、「インスタンス」は「たい焼き」そのもの。「たい焼き器」は1つしかありませんが、「たい焼き」はいくらでも作ることが可能です。今、これを聞いて理解できなくても全く問題ありません。聞き流してください。プログラムとしては、インスタンス名 = クラス名(引数)という形で使うということだけ覚えておいてください。

次に「メソッド呼び出し」ですが、これは簡単です。
クラスは複数の関数を持っていて、その中の1つの関数を指定して呼び出しているということです。プログラムとしては、インスタンス名.メソッド名 という形で使います

なお、Pythonではクラス名の最初を大文字にするのが規則ですので、関数なのかクラスなのかはそれで判断できます。

具体的なクラスの活用

OpenCVには画像に画像を重ねるオーバーレイ関数がありません。そこで、クラスを活用することとします。

pngoverlay.py

上記のリンク先の右上Download ZIPボタンを押します。Downloadsフォルダに保存されているので、ダブルクリックでファイルを展開します。フォルダの中にあるpngoverlay.pyworkspaceフォルダへ移動します。

次に、画像を準備します。今回はこちらを活用しますが、PNGファイルであれば何でも良いです。

参考:みかんのイラスト

ファイル名はfruit_orange.pngです。
以下は、最初のサンプルコードにPNGoverlayを加えた例です

import cv2
from pngoverlay import PNGOverlay
 
img = cv2.imread('cat.jpg')
 
# PNGOverlayインスタンス生成 
item = PNGOverlay('fruit_orange.png')
 
# 透過PNGを描画 
item.show(img, 400, 280)
 
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

元々の猫画像の上に、みかんのPNG画像が表示されました
追加したのは以下の3点です

from pngoverlay import PNGOverlay
 
# PNGOverlayインスタンス生成 
item = PNGOverlay('fruit_orange.png')
 
# 透過PNGを描画 
item.show(img, 400, 280)

1行目で、Python基礎プログラミングで学んだimportの3つ目の形式でPNGoverlayを取り込んでいます。
2つ目がインスタンス生成です。クラス名と同じPNGOverlay()という表記を使って、fruit_orange.pngを引数とし、変数名itemでインスタンスを生成しています。

3つ目がメソッドです。showは以下のような引数を持つメソッドです

show(画像, 中心座標X, 中心座標Y)

インスタンス名.メソッド名 という形で実行しているのが分かるかと思います。

みかんが大きすぎるので、小さくして、かつ角度も付けてみたいと思います

import cv2
from pngoverlay import PNGOverlay
 
img = cv2.imread('cat.jpg')
 
# PNGOverlayインスタンス生成 
item = PNGOverlay('fruit_orange.png')
 
# 透過PNGを描画 
item.resize(0.2)  # スケール 
item.rotate(15) # 角度 
item.show(img, 185, 140) # 座標 
 
cv2.imshow('image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

今回新たに2つのメソッドを使いました

resize(倍率)

rotate(角度)

resizeは引数に倍率を指定することで、元の画像のサイズを変更できます
rotateは引数に角度を指定することで、反時計回りの回転を加えることが出来ます。

以上が、PNGOverlayの使い方です。簡単にまとめます

resizerotateは無くてもOK、showは他のメソッドより後に記述というのがポイントです

カメラ映像の読み込みと表示

これまでは静止画でしたが、カメラ映像の動画を読み込んで表示します。
ラズパイにUSBカメラを接続してから、以下のコードを実行してみて下さい。カメラ映像が動画としてリアルタイムに表示されます。

import cv2
 
# カメラ準備 
cap = cv2.VideoCapture(0)
 
# 無限ループ 
while True:
    # キー押下で終了 
    key = cv2.waitKey(1)
    if key != -1:
        break
 
    # カメラ画像読み込み 
    ret, frame = cap.read()
 
    # 画像表示 
    cv2.imshow('image', frame)
 
# 終了処理 
cap.release()
cv2.destroyAllWindows()

普通にWebカメラ映像が出力されたかと思います。

コードの解説をします

カメラ準備

cv2.VideoCaptureは引数としてデバイス番号を指定しているクラスのインスタンス生成です。"VideoCapture object"というものが取得でき、それをcapという変数に入れています。
現状はAI CORE Xスターターキットに接続しているカメラは1つだけなので、デバイス番号は 0 を指定します。ただし、複数のカメラを使用したり、USBを抜き差ししているとデバイス番号が変わる場合があります。その場合は、以下のコマンドを実行することによりデバイス番号を確かめることもできます。

ls /dev/video*
 
# 実行結果例
# /dev/video0

番号は2つ表示されると思いますが、経験上、小さい方の番号がデバイス番号です。

無限ループ

OpenCVでカメラ映像を取得する手順は、動画として一括読み込み&表示ではなく、カメラ画像1フレーム毎の読み込みと表示を行う必要があります。1コマ読み込んで1コマ表示するということを繰り返すため、無限ループになっています。

キー押下で終了

無限ループのままだとプログラムが終了できないため、キーボード入力があったときに break させループから抜け出します。
今までの静止画像表示のときのように cv.waitKey(0)にしてしまうと、そこで止まってしまい動画にならないため、引数を最小値の 1 に設定しています。数値を大きくすると待ち時間が長くなりカクカクした映像になります。
また、キーが押されていないときの cv.waitKeyの戻り値は -1 です。つまり -1 以外のときは、何らかのキーが押されたということになりますので、それを判別して break しています。

カメラ画像読み込み

取得した"VideoCapture object" の cap を使って、cap.readとすることで、カメラからの静止画像を読み込みます。静止画像は2番目の戻り値 frame へ代入されます。1番目の戻り値 ret には正しく静止画像が読み込みできたか否かのブール値(True or False)が入ります。ただし、今回のプログラムではretは特に使用していません。使用しませんが、戻り値として受け取らないとエラーになるのでご注意ください。

画像表示

画像を表示させる手順は静止画表示と同様に cv2.imshowを使います。

VideoCapture object解放

不要になった"VideoCapture object"は、最後にcap.releaseで解放します。

以上、カメラ映像表示について説明しましたが、OpenCVによるカメラ映像の読み込みと表示に関しては、ほぼこのコードのまとまりをテンプレートとして使えます。
なお、カメラ映像の場合であっても、前半で行ったようなサイズ変更、文字や図形の描画、PNG透過画像表示が可能ですので色々と試してみて下さい。


次回は、ディープラーニングのプログラミングの肝となる「NumPy」を学びます
以上、「OpenCV基礎プログラミング」でした。