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('エッジ検出完了')
threshold1とthreshold2の値を変えると、拾うエッジの細かさが変わります。数値が低いほど細かいエッジまで拾いますが、ノイズも増えます。実際に色々試してみると、物体の輪郭がくっきり白線で浮かび上がって、なんかかっこいい画像になりました😊
この結果を見ながら「あ、これ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)}個の顔を検出しました')
自分の写真で試したところ、ちゃんと顔の位置に緑の矩形が表示されました!scaleFactorやminNeighborsのチューニングで誤検知率が変わるので、この辺はデータに合わせて調整が必要ですね。
MediaPipeと比べると検出精度は低いですが、依存ライブラリが少なくて軽量なので、Raspberry PiなどのエッジデバイスではHaar Cascadeの方が向いているシーンもありそうです。組み込み屋的にはそういう視点が大事だな〜と改めて思いました😌
まとめ
今回はPython + OpenCVで以下の処理を試してみました。
- 画像の読み込み・グレースケール変換・ガウシアンブラー
- Canny法によるエッジ検出
- Haar Cascadeを使った顔検出
MediaPipeを先に触っていたこともあって、意外とすんなり理解できた気がします。OpenCVはできることが本当に幅広いので、次は動画キャプチャ・オプティカルフロー・テンプレートマッチングあたりを深掘りしてみたいな〜と思っています。
UnityやBlenderとの連携でも使えそうなネタが出てきたので、そのうち記事にしますね😊
それでは、今回はここまで。ありがとうございました😊