はじめに
Pythonで画像を扱いたい!となった時のライブラリと言えばOpenCVですよね。
ここでは公式チュートリアルの中から、自分がよく使う処理をコードと共にまとめたいと思います。
基本的にはモデルの学習用に画像を前処理するのが目的なのでガッツリ画像を重ねたり、変形してみたいなのはやりません。
これからOpenCV使って画像データ扱いたい!という人の参考になれば幸いです。
環境・バージョン
AnacondaのJupyterLabを使います。
OpenCVのバージョンは4.5.1です。
1. OpenCVのインストール
macなら
brew install opencv
の後に
$ pip install opencv-python
でうまくいくはず。Windowsの方やエラーが出たら...各自ググって頂きたく存じます(投げやり)
2. 画像を読み込む
まずは画像を読み込みましょう。使う画像は僕が櫻坂46の田村保乃ちゃんが好きなので、推しの尊い画像を使わさせて頂きます。
とその前にopen-cvをimportしておきましょう。
import cv2
インポートはこれでok
じゃ画像を読み込みましょう
# 画像パスの定義
image_path = 'sample.jpg'
# cv2.imread(画像のパス)でcv2に画像を読み込ませられる
img = cv2.imread(image_path)
簡単ですね。特に難しいところはないかと思います。
一応引数でグレースケール、カラーを変更できます。
# imreadの第二引数を0にすることでグレースケール
img = cv2.imread(image_path, 0)
# 1にするとカラー画像:デフォルト
img = cv2.imread(image_path, 1)
2. 画像を表示する
推しの画像がちゃんと読み込めたか表示してみましょう。
ちなみに表示するならmatplotlib
を使った方が楽なのでmatplotlib
で表示します。
import matplotlib.pyplot as plt
# plt.imshowで表示できる
plt.imshow(img)
ありゃ、表示はされたけど、なんか色がおかしいですね。
matplotlib
での表示は楽なんですけど1点が注意が必要です。
というのもcv2.imreadで画像を読み込むとBGRモードで読み込まれます。ただmatplotlib
はRGBモードで表示するので、こうした色成分がおかしくなっちゃうんですね。
そのため画像をRGBモードに変える必要があります。
image_path = 'sample.jpg'
img = cv2.imread(image_path)
# cv2.cvtColorでカラー変更できる
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
第一引数に変えたい画像、第二引数にどう変えたいか書きます。COLOR_BGR2RGB
はCOLOR(色)をBGR TO RGB(BGRからRGBに)ということですね。
それじゃ変えてない画像と表示し比べてみましょう。
image_path = 'sample.jpg'
img = cv2.imread(image_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 描画領域の設定
fig = plt.figure(figsize=(8,8))
# BGR画像の表示
ax = plt.subplot(121)
ax.axis('off')
ax.imshow(img)
ax.set_title('BGR_mode')
# RGB画像の表示
ax2 = plt.subplot(122)
ax2.axis('off')
ax2.imshow(img_rgb)
ax2.set_title('RGB_mode')
正しく表示されていますね。この1点だけ注意しておけば怖くないです。
※おまけ:BGRからRGBへの変換の別のやり方
image_path = 'sample.jpg'
img = cv2.imread(image_path)
# cv2.splitでチャネルごとに分けてアンパック代入できる
b,g,r = cv2.split(img)
# cv2.mergeでrgbの順番で結合
img_rgb = cv2.merge([r,g,b])
お好きな方でいいと思います。
※さらにおまけ:グレースケール表示に関して
先ほどcv2.imread(image_path,0)
でグレースケールで読み込めると言ったので、グレー画像も表示してみようと思います。
image_path = 'sample.jpg'
img_gray = cv2.imread(image_path,0)
plt.imshow(img_gray)
なんか変ですね。これもさっきと一緒でグレースケールの画像をmatplotlib
が勝手にRGBモードで表示してるので緑っぽい感じになっちゃうと。
こういう時はmatplotlib側をいじりましょう。
# cmapを'gray'にすることでグレー画像として表示できる
plt.imshow(img_gray, cmap='gray')
ちゃんとグレーで表示されてますね。白黒でもかわいい保乃ちゃん
3. 画像を拡大・縮小する
モデル学習用に画像データを用意するときには画像サイズを揃えることは大事です。
今回は256 × 256にリサイズします。
image_path = 'sample.jpg'
img = cv2.imread(image_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# リサイズ前のサイズ(高さ、幅、チャネル数)
img_rgb.shape
# -> (1102, 1478, 3)
# cv2.resizeでリサイズ
resize_img = cv2.resize(img_rgb, (256, 256))
# リサイズ後のサイズ
resize.shape
# -> (256, 256, 3)
cv2.resize(リサイズしたい画像, (高さ、幅))
で指定サイズにリサイズできます。
4. (応用)顔部分を検出する
まぁ人の画像を扱うなら顔の部分だけを切り抜くことは多いと思います。ここでは他の記事でもたくさん使われているカスケード分類器を使って顔部分を検出、切り抜きまでやってみようと思います。
(宣伝)自分がカスケード分類器を使って櫻坂メンバーの顔分類をやった記事もあるのでよければ
→櫻坂46メンバーの顔を分類するアプリケーションをStreamlitで作りました。
4.1 カスケード分類の用意
コチラから分類器をDLしましょう。
今回はhaarcascade_frontalface_alt2.xml
を使います。
4.2 カスケード分類器を適用
# カスケード分類器がグレー画像を要求するのでグレースケールで読み込む
image_path = 'sample.jpg'
img = cv2.imread(image_path,0)
# カスケード分類器を表すパスを定義
cascade_path = 'haarcascade_frontalface_alt2.xml'
# カスケード分類器を読み込む
cascade = cv2.CascadeClassifier(cascade_path)
# 画像にカスケード分類器を適用させる
# scaleFactor: 顔検出する検索窓の拡大率
# minNeighbors: 近くにこの値以上の検出された矩形がないとNG。大きくすると精度は上がるが、検出されない可能性も高まる
# minSize: 検出する矩形の最小サイズ
face_list = cascade.detectMultiScale(img, scaleFactor=1.1,
minNeighbors=2, minSize=(64, 64))
print(face_list)
# -> array([[581, 369, 308, 308]], dtype=int32)
どのカスケード分類器を使うかや、cascade.detectMultiScaleの各種パラメータに関してはコチラが参考になります。
無事検出できているとface_listの中に顔の部分の座標情報が格納されています。
それぞれ[四角形の左上の角の座標のx座標, 四角形の左上の角の座標のx座標, 四角形の横の幅, 四角形の縦の幅 ]
を表します。
雑なイメージ図です⇩
それでは顔部分を表す四角形の部分だけ切り抜いてみましょう。
image_path = 'sample.jpg'
img = cv2.imread(image_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 顔部分の左上の座標、幅、高さをアンパック代入
x,y,w,h = face_list[0]
# 縦は左上の座標から高さを足した値まで
# 横は左上の座標から幅を足した値までをスライスで指定すればok
face_img = img_rgb[y:y+h, x:x+h]
plt.imshow(face_img)
ちゃんと顔部分だけ表示できていますね。かわいすぎるな...
おまけカラーでもやってみる
基本はグレースケール画像を要求するのですが、別にカラーでもエラーは出ないので検出される座標が違うのかやってみます。
image_path = 'sample.jpg'
img = cv2.imread(image_path)
img_gray = cv2.imread(image_path, 0)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
cascade_path = 'haarcascade_frontalface_alt2.xml'
cascade = cv2.CascadeClassifier(cascade_path)
plt.figure(figsize=(8,8))
index = 1
for image in [img_gray, img_rgb]:
face_list = cascade.detectMultiScale(image, scaleFactor=1.1,
minNeighbors=2, minSize=(64, 64))
x,y,w,h = face_list[0]
face_img = image[y:y+h, x:x+h]
plt.subplot(1,2,index)
if index == 1:
plt.imshow(face_img, cmap='gray')
else:
plt.imshow(face_img)
plt.title(face_list[0])
index += 1
検出はできているけど、検出座標は違うようですね。
5. 画像の保存
せっかく顔部分だけ切り抜いたので画像の保存もしておきましょう。
# cv2.imwriteで保存
cv2.imwrite('hono_face.jpg', face_img)
第一引数がファイル名、第二引数が保存したい画像。
さてここでも注意が必要です。
cv2.imwriteも画像をBGRモードで保存しちゃいます。
face_imgはRGBモードの画像です。これがBGRモードになるので色が変なまま保存されます。
なのでimg = cv2.imread(image_path)
のBGRモードの画像に対して顔検出して、その画像を保存すればokです。
何度も言いますが、BGRモードなのかRGBモードなのかは要注意ポイントです。
まとめ
一通り、画像データを扱うときの主な処理をまとめてみました。参考になれば幸いです。