OpenCVってそもそも何?

スポンサーリンク

以前からMediaPipeを使ってきた流れで、「もっと低レベルな画像処理もやってみたい!」と思っていたところ、OpenCV(Open Source Computer Vision Library)の存在を改めて調べてみました😊

OpenCVはC++で書かれたオープンソースの画像処理ライブラリで、Pythonバインディングが用意されているためpip install一発で使い始められます。顔認識・物体検出・特徴点マッチングなど、コンピュータビジョン系の処理がほぼ何でもできてしまう頼もしいやつです。

組み込み屋としてはカメラ映像の前処理とか、センサーデータの可視化なんかに応用できないかな〜と思いつつ、まずは基本から触ってみました!

環境構築

今回はMac上のPython 3.11で試しています。インストールはシンプルです。

# OpenCVのインストール
pip install opencv-python

# 追加機能(顔検出などのモデルが含まれるフルビルド)
pip install opencv-contrib-python

バージョン確認はこちら。

import cv2
print(cv2.__version__)  # 例: 4.9.0

インストールが終わったら、試しに手元にある適当な画像ファイルを読み込んでみましょう。

基本的な画像操作を試してみた

まずはOpenCVの基本中の基本、画像の読み込み・表示・保存から。

import cv2
import numpy as np

# 画像の読み込み(BGRで読み込まれる点に注意)
img = cv2.imread('sample.jpg')

# グレースケール変換
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# ぼかし(ガウシアンブラー)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)

# 保存
cv2.imwrite('output_gray.jpg', gray)
cv2.imwrite('output_blur.jpg', blurred)

print('処理完了!')

ここで最初にハマったのが「OpenCVの色順はBGR」という点でした。PillowやMatplotlibはRGBなので、そのままimshowに渡すと色が狂います😅 cv2.cvtColor(img, cv2.COLOR_BGR2RGB)で変換してから使うのがポイントです。

よく使う変換処理をまとめると以下のとおりです。

処理 関数 主な用途
グレースケール変換 cv2.cvtColor(..., COLOR_BGR2GRAY) 前処理全般
ガウシアンブラー cv2.GaussianBlur() ノイズ除去
二値化 cv2.threshold() 背景分離
リサイズ cv2.resize() 入力サイズ統一
回転・アフィン変換 cv2.warpAffine() データ拡張

エッジ検出(Canny法)に挑戦

画像処理といえばエッジ検出!Canny法はノイズに強く、結果がシャープで見栄えがいいのでお気に入りです。

import cv2

img = cv2.imread('sample.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Cannyエッジ検出(閾値は低め・高めの2段階)
edges = cv2.Canny(gray, threshold1=50, threshold2=150)

cv2.imwrite('edges.jpg', edges)
print('エッジ検出完了')

threshold1threshold2の値を変えると、拾うエッジの細かさが変わります。数値が低いほど細かいエッジまで拾いますが、ノイズも増えます。実際に色々試してみると、物体の輪郭がくっきり白線で浮かび上がって、なんかかっこいい画像になりました😊

この結果を見ながら「あ、これ3Dモデルのワイヤーフレームっぽい…」とUnityやBlenderとの連携アイデアが湧いてきたり。組み込み系だと距離センサーとカメラを組み合わせた障害物検知なんかにも使えそうです。

顔検出(Haar Cascade)を試してみた

OpenCVには昔からあるHaar Cascadeという機械学習ベースの検出器が同梱されています。精度はYOLOなどには劣りますが、追加モデルなしで動くのが魅力。

import cv2

# Haar Cascadeの読み込み
face_cascade = cv2.CascadeClassifier(
    cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
)

img = cv2.imread('photo.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 顔検出
faces = face_cascade.detectMultiScale(
    gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)
)

# 検出した顔に矩形を描画
for (x, y, w, h) in faces:
    cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)

cv2.imwrite('faces_detected.jpg', img)
print(f'{len(faces)}個の顔を検出しました')

自分の写真で試したところ、ちゃんと顔の位置に緑の矩形が表示されました!scaleFactorminNeighborsのチューニングで誤検知率が変わるので、この辺はデータに合わせて調整が必要ですね。

MediaPipeと比べると検出精度は低いですが、依存ライブラリが少なくて軽量なので、Raspberry PiなどのエッジデバイスではHaar Cascadeの方が向いているシーンもありそうです。組み込み屋的にはそういう視点が大事だな〜と改めて思いました😌

まとめ

今回はPython + OpenCVで以下の処理を試してみました。

  • 画像の読み込み・グレースケール変換・ガウシアンブラー
  • Canny法によるエッジ検出
  • Haar Cascadeを使った顔検出

MediaPipeを先に触っていたこともあって、意外とすんなり理解できた気がします。OpenCVはできることが本当に幅広いので、次は動画キャプチャ・オプティカルフロー・テンプレートマッチングあたりを深掘りしてみたいな〜と思っています。

UnityやBlenderとの連携でも使えそうなネタが出てきたので、そのうち記事にしますね😊

それでは、今回はここまで。ありがとうございました😊