Python OpenCVを学習してみた
2023年3月27日
2023年3月28日
OpenCV(Open Source Computer Vision Library)は、画像や動画の処理に必要な機能を様々に提供するライブラリです。画像の変換やフィルタ処理や変形、物体判定や物体認識や顔認識、カメラの入出力など豊富な機能があり、とても便利です。
OpenCVで画像の読み込み・変換をしてみる
以前にStable Diffusionの記事で使用した画像を使用します。
「sample.png」として保存しておきましょう。

読み出した画像のデータの高さ・幅・色の情報を表示してみます。
下記の結果の色「3」は [B,G,R]=[0,0,0]~[255,255,255] を意味します。
import cv2
img = cv2.imread("sample.png")
print(img.shape)

赤色だけにしたい場合は、以下のようにすると可能です。
import cv2
img = cv2.imread("sample.png")
# [B,G,R]のB,Gの値をゼロに
img[:, :, (0, 1)] = 0
# ファイルに保存
cv2.imwrite("sample_red.png", img)
from IPython.display import Image,display_png
display_png(Image("sample_red.png"))

グレースケール変換をしてみます。
import cv2
img = cv2.imread("sample.png")
# グレイスケールに変換
gry = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# ファイルに保存
cv2.imwrite("sample_gry.png", gry)
from IPython.display import Image,display_png
display_png(Image("sample_gry.png"))

ソーベルフィルター・Cannyフィルタ(共にエッジ検出)、ネガポジ反転、2値画像・輪郭の描画をしてみます。
import cv2
img = cv2.imread("sample.png")
# ソーベルフィルタを適用
sobel = cv2.Sobel(img, cv2.CV_32F, 1, 0, ksize=5)
# ファイルに保存
cv2.imwrite("sample_sobel.png", sobel)
# Cannyフィルタを適用
canny = cv2.Canny(img, 30, 200)
# 白黒反転
canny = 255 - canny
# ファイルに保存
cv2.imwrite("sample_canny.png", canny)
# ネガポジ反転
nega = cv2.bitwise_not(img)
# 白黒反転でも同じ結果が得られます。
#nega = 255 - img
# ファイルに保存
cv2.imwrite("sample_nega.png", nega)
# 2値画像の作成
gry = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gry, 100, 255, cv2.THRESH_BINARY)
# ファイルに保存
cv2.imwrite("sample_thresh.png", thresh)
# 2値画像を元に輪郭の描画
# 輪郭の抽出
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cts = cv2.drawContours(img, contours, -1, color=(0,255,0), thickness=2)
# ファイルに保存
cv2.imwrite("sample_contours.png", cts)
from IPython.display import Image,display_png
display_png(Image("sample_sobel.png"))
display_png(Image("sample_canny.png"))
display_png(Image("sample_nega.png"))
display_png(Image("sample_thresh.png"))
display_png(Image("sample_contours.png"))





OpenCVでWebカメラの画像を取り込んでみる
OpenCVを使用すると、簡単にWebカメラの画像を取り込むことが可能です。
import cv2
# カメラのキャプチャを開始
cap = cv2.VideoCapture(0)
while True:
# 1フレームずつ取得する。
ret, frame = cap.read()
#フレームが取得できなかった場合は、画面を閉じる
if not ret:
break
# ウィンドウに出力
cv2.imshow("Frame - Press ESC or ENTER to exit.", frame)
# 10ms間隔で更新する
key = cv2.waitKey(1)
# Escキーを入力されたら画面を閉じる
if key == 27:
break
# Enterキーを入力されたら画面を閉じる
if key == 13:
break
# 後処理
cap.release()
cv2.destroyAllWindows()
画像が表示されたウインドウでEscキーまたはEnterキーを押すと終了させています。
取得した画像(変数frame)に先ほど紹介した変換処理を加えて、リアルタイムに見ることも可能です。

OpenCVでWebカメラの動体検知を実施する
Webカメラの画像に、日付とモーションファクター(全体の変化した割合)を表示させています。
モーションファクターが閾値以上になると、その時の画像を保存する仕組みになっています。
ウインドウ画面には、検知した時に「DETECTED」と赤文字が出るようにしました。
import datetime
import cv2
# 画像を保存するディレクトリ
save_dir = './image/'
# ファイル名は「日付時刻 + fn_suffix」にする。
fn_suffix = 'motion_detect.jpg'
# カメラのキャプチャを開始
cap = cv2.VideoCapture(0)
# 縦と横の解像度指定
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
# 各ドットの変化を検知するしきい値
DOT_TH = 20
# モーションファクター(どれくらいの点に変化があったか)が
# どの程度以上なら記録するか。(0.0~1.0)
MOTHON_FACTOR_TH = 0.20
# 比較用のデータを格納
avg = None
while True:
# 1フレームずつ取得する。
ret, frame = cap.read()
# フレームが取得できなかった場合は、画面を閉じる
if not ret:
break
# データを取得した時刻
dt_now = datetime.datetime.now()
# ファイル名と画像中に埋め込む日付時刻
datetime_str = dt_now.strftime('%Y-%m-%d %H:%M:%S')
f_name = dt_now.strftime('%Y%m%d%H%M%S%f') + "_" + fn_suffix
# モノクロにする
gray = cv2.cvtColor(frame, cv.COLOR_BGR2GRAY)
# 比較基準用のフレームを取得する
if avg is None:
avg = gray.copy().astype("float")
continue
#「avg = (1 - 0.2) * avg + 0.2 * gray」で移動平均を更新
cv2.accumulateWeighted(gray, avg, 0.2)
# 現在のフレームと移動平均との差を計算
frameDelta = cv2.absdiff(gray, cv.convertScaleAbs(avg))
# デルタ画像を閾値処理を行う(差の有無で2値画像を作成)
thresh = cv2.threshold(frameDelta, DOT_TH, 255, cv.THRESH_BINARY)[1]
# モーションファクターを計算(全体としてどれくらいの割合が変化したか)
motion_factor = thresh.sum() * 1.0 / thresh.size / DELTA_MAX
motion_factor_str = '{:.08f}'.format(motion_factor)
# 画像に日付時刻を書き込み
cv2.putText(frame,datetime_str,(25,50),cv.FONT_HERSHEY_SIMPLEX, 1.0,(255,255,255), 2)
# 画像にmotion_factor値を書き込む
cv2.putText(frame,motion_factor_str,(25,470),cv.FONT_HERSHEY_SIMPLEX, 1.0,(255,255,255), 2)
# モーションファクターがしきい値を超えていれば動きを検知したことにする
if motion_factor > MOTHON_FACTOR_TH:
# ファイルに保存
cv2.imwrite(save_dir + f_name, frame)
print("DETECTED:" + f_name)
# 画像に検知した旨のメッセージを書き込む
cv2.putText(frame,"DETECTED",(400,470),cv.FONT_HERSHEY_SIMPLEX, 1.0,(0,0,255), 2)
# ウィンドウに出力
cv2.imshow('Frame - Press ESC or ENTER to exit.', frame)
# 10ms間隔で更新する
key = cv2.waitKey(10)
# Escキーを入力されたら画面を閉じる
if key == 27:
break
# Enterキーを入力されたら画面を閉じる
if key == 13:
break
# 後処理
cap.release()
cv2.destroyAllWindows()


画像を保存するだけではなく、メールを飛ばしたり、アラームを鳴らしたりもできそうです。
こういう便利なライブラリが無料で使えるなんて、すごいですね。
とりあえず、ここまで。