第12回:画像処理¶
前回は matplotlib という外部ライブラリで、集計結果をグラフにした。今回も外部ライブラリを使う。テーマは画像処理、道具は OpenCV。画像をプログラムで読み込んで、サイズを変えたり、白黒にしたり、輪郭を抜き出したりする。
写真を 1 枚ずつ加工するのは無理¶
スマホで撮った写真 100 枚を、全部同じサイズに縮小したい。1 枚ずつペイントで開いて…は現実的でない。プログラムなら一瞬で大量の画像をまとめて処理できる。
- 例:SNS のアップロード制限に合わせてリサイズ、プロフィール写真をモノクロ加工、証明写真のサイズ調整
身近なものの内部でも画像処理は使われている。標準写真アプリ、Instagram や LINE のフィルター、スマホの顔認識、QR コードリーダ、レジのバーコード読み取り……。卒研の実験画像の処理や作業の自動化にも効く。画像をプログラムで扱えると強い。
OpenCV と pip¶
画像処理には OpenCV(オープンシーヴィー) という外部ライブラリを使う。標準では入っていないので pip で入れる。
コマンドプロンプトで次を実行する。
※実行後はSpyderを再起動すること。
プログラムの先頭で次のように書くと、以降 cv2.〇〇 で機能を呼び出せる。
ライブラリ名と読み込む名前が違う
インストールするパッケージ名は opencv-python だが、プログラムで読み込む名前は cv2。
画像の読み込みと表示¶
cv2.imread(ファイル名)で画像を読み込む(変数に入る)cv2.imshow(ウィンドウ名, 画像)で別ウィンドウに表示するcv2.waitKey(0)でキー入力待ちにする(これが無いと一瞬で閉じてしまう)cv2.destroyAllWindows()でウィンドウを閉じる
import cv2
img = cv2.imread('block.jpg')
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
この 4 行セットが画像表示の基本形。画像ファイル(ここでは block.jpg)はプログラムと同じフォルダに置く。
手元の画像で試すには
block.jpg は説明用の見本画像。手元で動かすときは、自分の好きな .jpg ファイルを同じフォルダに置いてファイル名を書き換えれば OK(このあとの授業内演習では barbara.jpg を使う)。
画像のサイズを変える¶
- 画像のサイズは
img.shapeで分かる shape[0]が高さ、shape[1]が幅(順番に注意)cv2.resize(画像, (幅, 高さ))でサイズを変えるcv2.imwrite(ファイル名, 画像)でファイルに保存する
img = cv2.imread('block.jpg')
height = img.shape[0] # 高さ
width = img.shape[1] # 幅
resized = cv2.resize(img, (width // 2, height)) # 幅だけ半分にする
cv2.imwrite('resized.jpg', resized)
順番が逆:取り違え注意
img.shape は (高さ, 幅) の順、cv2.resize の引数は (幅, 高さ) の順。逆なので取り違えやすい。
色を変える(グレースケール)¶
cv2.cvtColor(画像, cv2.COLOR_BGR2GRAY) で白黒(グレースケール)にできる。
OpenCV の色の順番は BGR(青・緑・赤)。RGB ではないので、定数名は BGR2GRAY。
img = cv2.imread('block.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imwrite('gray.jpg', gray)
グレー画像は色の情報が 1 つになるので、shape が 2 次元(高さ, 幅)になる(カラーは (高さ, 幅, 3) の 3 次元)。
エッジ検出(Canny)¶
cv2.Canny(画像, 閾値1, 閾値2) で、輪郭(エッジ)だけを抜き出した白黒画像が作れる。
閾値1・閾値2 を変えると検出する輪郭の量が変わる(小さくすると細かい線まで拾う)。
数字を調整して、輪郭がちょうどよく出る値を探すのがコツ(応用課題で使う)。
もっと知りたい人へ(発展):円の検出¶
エッジ(輪郭)が分かると、その中から特定の形を探せるようになる。cv2.HoughCircles(...) を使うと、画像の中の円(道路標識・コイン・お皿など)を見つけられる。見つけた円は cv2.circle(...) で画像に上描きして確認する。
import cv2
import sys
img = cv2.imread('road_sign.jpg')
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
canny_img = cv2.Canny(gray_img, 600, 650)
circles = cv2.HoughCircles(canny_img, cv2.HOUGH_GRADIENT, dp=1, minDist=10,
param1=700, param2=27, minRadius=20, maxRadius=60)
if circles is None:
sys.exit()
for (x, y, r) in circles[0]:
cv2.circle(img, (int(x), int(y)), int(r), (0, 255, 0), 6)
cv2.imshow('detected circles', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
import sysは、円が 1 つも見つからないときsys.exit()で処理を止めるためparam1・param2などの数字を調整して、円がうまく検出されるよう工夫する- 見つかった円を
forで 1 つずつ取り出し、cv2.circleで上描きする
授業内演習¶
barbara.jpg を読み込んで、画像を表示するプログラムを作成しなさい。
barbara.jpg は上のリンクからダウンロードし、プログラムと同じフォルダに置くこと。
確認の観点:
- 本文「画像の読み込みと表示」の4 行セットを、
barbara.jpgに当てはめる cv2.waitKey(0)を入れないと、ウィンドウが一瞬で閉じてしまう
基本課題¶
barbara.jpg を読み込んで、キーボードから画像の縦サイズと横サイズを入力し、cv2.resize 関数を用いてサイズを変更して画像を表示するプログラムを作成しなさい。
実行例:
ヒント:
input()で受け取った値は文字列なので、int()で数値にするcv2.resizeの引数は (横, 縦) の順(本文「順番が逆」の注意を見直す)
応用課題¶
barbara.jpg を読み込んで、cv2.Canny 関数によってエッジ検出を行い、エッジ画像を表示して保存するプログラムを作成しなさい。
約束ごと:
- エッジが鮮明に見えるように、
cv2.Canny関数の第2引数と第3引数(閾値1・閾値2)を調整して工夫すること - 保存したエッジ画像もアップロードすること