リモート開発メインのソフトウェア開発企業のエンジニアブログです

OpenCVで画像処理を試す

なんとなく画像認識系のプログラムに興味が湧いたところ、OpenCVを使えば簡単に試せるという話を聞いたので試してみました。

OpenCVとは?

画像処理・画像解析および機械学習等の機能を持つC/C++、Java、Python、MATLAB用ライブラリ。プラットフォームとしてmacOSやFreeBSD等全てのPOSIXに準拠したUnix系OS、Linux、Windows、Android、iOS等をサポートしている。

Wikipediaより引用:https://ja.wikipedia.org/wiki/OpenCV

準備

PC環境、言語

  • Mac(Intel) Monterey 12.3.1
  • Python 3.9.0

以下を使うのでインストール(コマンドそのまま)

  • pip3 install numpy
  • xcode-select –install
  • brew install opencv
  • pip3 install opencv-python
  • pip install Pillow

参考サイト

メジャーなオープンソースライブラリなのでググるとたくさん出てきます。

画像出してみる

import cv2
img = cv2.imread('/test.jpg')
cv2.imshow('テスト画像', img)
cv2.waitKey(1)

とりあえずこれで画像を表示できて環境構築の疲れを癒せます。

imread で画像を読み込み imshow で画像を表示できます。

超基本ですがOpenCVの馴染みやすさを一気に体感できたので後々これがめっちゃ大事だと思いました。

円の検出をしてみる

import numpy as np
img = cv2.imread('/test.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp=1, minDist=20, param1=100, param2=60, minRadius=5, maxRadius=0)
circles = np.uint16(np.around(circles))
for circle in circles[0, :]:
    # 円周を描画する
    cv2.circle(img, (circle[0], circle[1]), circle[2], (0, 165, 255), 5)
    # 中心点を描画する
    cv2.circle(img, (circle[0], circle[1]), 2, (0, 0, 255), 3)

cv2.imwrite("/test_circle.jpg", img)

img2 = cv2.medianBlur(img, 5)
cimg = cv2.cvtColor(img2, cv2.COLOR_GRAY2BGR)
circles = cv2.HoughCircles (img ,cv2.HOUGH_GRADIENT ,1 ,20, param1 = 500, param2 = 30, minRadius = 0, maxRadius = 0)

OpenCV初学で必ずと言っていいほど見かける「円の検出と描画」のプログラムです。

以下のサイトにサンプル画像も含めた詳細が載っています。

https://shikaku-mafia.com/cv2-houghcircles/

円の検出の HoughCircles や 円の描画の circle の引数もこちらのサイトの記述が参考になります。

また、何かを検出する際にグレースケール化( cvtColor に cv2.COLOR_BGR2GRAY を指定)はよく使われるので覚えておいた方がいいです。

グレースケールやカラー情報というもの自体が分からない場合は先にググっておくことを推奨します。

例:http://www.igunoss.co.jp/imageproc/imageproc1-2.html

カメラ読み込んでみる

import cv2
cap = cv2.VideoCapture(0)
while True:
    # 1フレームずつ取得する。
    ret, frame = cap.read()
    #フレームが取得できなかった場合は、画面を閉じる
    if not ret:
        break

    # ウィンドウに出力
    cv2.imshow("Frame", frame)
    key = cv2.waitKey(1)
    # Escキーを入力されたら画面を閉じる
    if key == 27:
        break
cap.release()
cv2.destroyAllWindows()

VideoCapture という関数にで動画を読み込むことができて cv2.VideoCapture(‘/sample_video.mp4’) のように記述すれば動画を読み込ませることができますが、ここに 0 を指定すると備え付けのWEBカメラがキャプチャーできます。

カメラの読み込みには終わりがないので無限ループ内で1フレーム毎に取得してウィンドウに表示すればカメラ表示を再現できます。

ループさせなければ実行時の瞬間だけ取得されて実質写真なります。

顔を検出してみる

import cv2

face_cascade_path = '/usr/local/opt/opencv/share/opencv4/haarcascades/haarcascade_frontalface_default.xml'
eye_cascade_path = '/usr/local/opt/opencv/share/opencv4/haarcascades/haarcascade_eye.xml'

face_cascade = cv2.CascadeClassifier(face_cascade_path)
eye_cascade = cv2.CascadeClassifier(eye_cascade_path)

cap = cv2.VideoCapture(0)

while True:
    ret, img = cap.read()
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(gray, scaleFactor=1.3, minNeighbors=5)
    for x, y, w, h in faces:
        cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
        face = img[y: y + h, x: x + w]
        face_gray = gray[y: y + h, x: x + w]
        eyes = eye_cascade.detectMultiScale(face_gray)
        for (ex, ey, ew, eh) in eyes:
            cv2.rectangle(face, (ex, ey), (ex + ew, ey + eh), (0, 255, 0), 2)
    cv2.imshow('video image', img)
    key = cv2.waitKey(10)
    if key == 27:  # ESCキーで終了
        break

cap.release()
cv2.destroyAllWindows()

カメラ内に顔が写ったら検知します。これぞ画像認識って感じがしますね。

メガネや正面など検知方法を変える場合は face_cascade_path を別のテンプレート(カスケード型の識別器とかカスケード型の分類器とか呼ばれてます。情報の集合体のようなもの?)に変更してください。

このファイルの絶対パスが opencv4 ではなく opencv だったりと微妙に環境によって違いがあるので注意してください。

これらファイルがそれぞれ何を検知するのに使えるのかは以下の記事が綺麗にまとめてくれてました。

https://qiita.com/tnoce/items/c819c85a85c16d246be8#カスケード型の識別器のファイル

デフォルトで入っている分類機のファイルはOpenCVが用意したものですが、これを自作することも可能です。

参考 https://www.pc-koubou.jp/magazine/21280

感想

まだ超基本的な機能に触れた程度ですが、先人達の記事により想像よりも遥かに簡単に扱えました。

なんなら環境構築段階が一番大変までありました。

しかし描画の関数などの計算はかなり面倒なのでそこら辺を自力でやっていたらかなり時間がかかりそうでした。

ちなみに文字抽出やテンプレートマッチングなど面白いことが出来そうな機能がまだまだあるので気が向いたら続きを書こうと思います。

あわよくば何かアプリでも作れたら楽しそうですね。

← 前の投稿

OpenCVでなにか作る

次の投稿 →

Goroutines と Channels を使ってジョブを並行化した時のメモ

コメントを残す