この記事は、画像処理エンジニア検定を受験するにあたり、OpenCVを実際に利用して画像処理について学ぶことを目的としています。勉強した内容をメモします。
画僧処理初心者がOpenCVを使ってみた(1) の続きです。
※画像処理でできることをメインに書いていくので、Pythonや使用する各関数の説明などは省略しています。
目次
- エッジの検出
- 直線の検出
- モルフォロジー演算
- 特徴抽出
- 顔検出
エッジの検出
エッジとは何かについては、以下のサイトで勉強させていただきました。
ソーベル法(Sobel)でエッジ検出
ここでは画像のX軸とY軸、それぞれについて画像のエッジ検出結果を見てみたいと思います。
import cv2
img = cv2.imread("data/src/favorite.jpg", 0)
#用意した画像が大きいためリサイズして縮小させてます
size = (300, 200)
img = cv2.resize(img,size)
img_sobelx = cv2.Sobel(img, cv2.CV_32F, 1, 0, ksize = 3)
img_sobely = cv2.Sobel(img, cv2.CV_32F, 0, 1, ksize = 3)
img_sobelx = cv2.convertScaleAbs(img_sobelx)
img_sobely = cv2.convertScaleAbs(img_sobely)
cv2.imshow("x",img_sobelx)
cv2.imshow("y",img_sobely)
cv2.waitKey(0)
cv2.destroyAllWindows()
上記のコード実行結果は以下の画像のようになりました。
X軸とY軸でエッジの検出結果が違うことが分かります。
ラプラシアン法でエッジ検出
import cv2
img = cv2.imread("data/src/favorite.jpg", 0)
#用意した画像が大きいためリサイズして縮小させてます
size = (300, 200)
img = cv2.resize(img,size)
img_lap = cv2.Laplacian(img, cv2.CV_32F)
img_lap = cv2.convertScaleAbs(img_lap)
cv2.imshow("laplacian",img_lap)
cv2.imshow("img",img)
cv2.waitKey(0)
cv2.destroyAllWindows()
この方法では、画像の軸に関係なくエッジを検出します。
以下の画像が出力結果です。
さらにエッジ検出をわかりやすくするために、GaussianBlur
を用いてラプラシアン法を適用してみました。
import cv2
img = cv2.imread("data/src/favorite.jpg", 0)
#用意した画像が大きいためリサイズして縮小させてます
size = (300, 200)
img = cv2.resize(img,size)
img_blur = cv2.GaussianBlur(img, (3,3), 2)
img_lap = cv2.Laplacian(img_blur, cv2.CV_32F)
img_lap = cv2.convertScaleAbs(img_lap)
cv2.imshow("laplacian",img_lap)
cv2.imshow("img",img)
cv2.waitKey(0)
cv2.destroyAllWindows()
上記のコードの実行結果は以下のようになりました。
上のラプラシアン法よりもノイズが少ないことが分かります。
キャニー法でエッジ検出
import cv2
img = cv2.imread("data/src/favorite.jpg", 0)
#用意した画像が大きいためリサイズして縮小させてます
size = (300, 200)
img = cv2.resize(img,size)
img_canny = cv2.Canny(img, 10, 200)
cv2.imshow("canny",img_canny)
cv2.imshow("img",img)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.Canny
の関数の閾値を変更してエッジの検出がどうなるか確認しました。
import cv2
img = cv2.imread("data/src/favorite.jpg", 0)
#用意した画像が大きいためリサイズして縮小させてます
size = (300, 200)
img = cv2.resize(img,size)
#引数でエッジの閾値を変更する
img_canny = cv2.Canny(img, 100, 200)
cv2.imshow("canny",img_canny)
cv2.imshow("img",img)
cv2.waitKey(0)
cv2.destroyAllWindows()
上記のコードを実行した結果が以下です。上の例よりノイズが少ないことが分かります。
直線の検出
画像から直線を検出する方法を試しました。
import cv2
import numpy as np
img = cv2.imread("data/src/favorite.jpg")
img_gray = cv2.imread("data/src/favorite.jpg",0)
#用意した画像が大きいためリサイズして縮小させてます
size = (300, 200)
img = cv2.resize(img,size)
img_gray = cv2.resize(img_gray,size)
img_canny = cv2.Canny(img_gray, 300, 400)
lines = cv2.HoughLines(img_canny, 1, np.pi/180, 100)
for i in lines[:]:
rho = i[0][0]
theta = i[0][1]
a = np.cos(theta)
b = np.sin(theta)
x0 = rho * a
y0 = rho * b
x1 = int(x0 + 1000 * (-b))
y1 = int(y0 + 1000 * a)
x2 = int(x0 - 1000 * (-b))
y2 = int(y0 - 1000 * a)
cv2.line(img, (x1,y1), (x2, y2), (255,0,0), 1)
cv2.imshow("canny",img_canny)
cv2.imshow("img",img)
cv2.waitKey(0)
cv2.destroyAllWindows()
上記のコードを実行した結果、以下の画像が出力されました。直線部分が検出されていることが分かります。
##モルフォロジー演算
画像ピクセルを収縮させたり、膨張させて画像内の図形を分離するとき、ノイズを消したりするときに使用します。
詳しくは以下のページを参考に勉強させていただきました。
以下のコードは、元画像(img)に対して、膨張と縮小を行った結果です。
import cv2
import numpy as np
img = cv2.imread("data/src/favorite.jpg")
#用意した画像が大きいためリサイズして縮小させてます
size = (300, 200)
img = cv2.resize(img,size)
#画像の二値化
ret, img_th = cv2.threshold(img, 100, 255, cv2.THRESH_BINARY)
kernel = np.ones((3, 3), dtype=np.uint8)
#膨張
imf_d = cv2.dilate(img_th, kernel)
#縮小
img_e = cv2.erode(img_th, kernel)
cv2.imshow("img",img)
cv2.imshow("imf_d",imf_d)
cv2.imshow("img_e",img_e)
cv2.waitKey(0)
cv2.destroyAllWindows()
実行した結果が以下の画像です。膨張させた際は画像の白い部分を拾ってきていますが、縮小させた際は黒い部分を拾ってきていることが分かります。
次にクロージングをしてみます。
画像を膨張させた後に縮小を行ってみました。
以下が実行コードです。
#上のコードの続きです
img_c = cv2.morphologyEx(img_th, cv2.MORPH_CLOSE, kernel)
cv2.imshow("img",img)
cv2.imshow("img_c",img_c)
cv2.imshow("img_e",img_e)
cv2.waitKey(0)
cv2.destroyAllWindows()
上記のコードを実行した結果が以下の画像です。左のimg_c画像を膨張させたのち、収縮させました。元の画像に近い状態を保っていることが分かります。中央のimg_d画像は、膨張させただけの画像です。比較するために表示させてみました。
特徴抽出
特徴とはパターン認識に役立つ情報量の多い部分のことを指します。
詳しくまとまっている記事がありましたので、こちらで勉強させていただきました。
###Harris
ハリスのコーナー検出は、画像からコーナーを検出する手法のことです。
以下のコードでハリスの手法を用いてコーナーの特徴量を抽出しました。
import cv2
import numpy as np
import copy
img = cv2.imread("data/src/favorite.jpg")
img_gray = cv2.imread("data/src/favorite.jpg",0)
#用意した画像が大きいためリサイズして縮小させてます
size = (300, 200)
img = cv2.resize(img,size)
img_gray = cv2.resize(img_gray,size)
#元の画像が上書きされないようにコピーを作成しておきます
img_harris = copy.deepcopy(img)
img_dst = cv2.cornerHarris(img_gray, 2, 3, 0.05)
#特徴量(コーナー)に赤色を付けます
img_harris[img_dst > 0.05 * img_dst.max()] = [0, 0, 255]
cv2.imshow("img",img)
cv2.imshow("imf_harris",img_harris)
cv2.waitKey(0)
cv2.destroyAllWindows()
実行結果は以下の画像です。
コーナーの部分が赤色で表示されていることが分かります。
###kaze
こちらも特徴量を抽出するアルゴリズムです。
上と同様にコーナーの特徴量の抽出を行いました。
import cv2
import numpy as np
import copy
img = cv2.imread("data/src/favorite.jpg")
img_gray = cv2.imread("data/src/favorite.jpg",0)
#用意した画像が大きいためリサイズして縮小させてます
size = (300, 200)
img = cv2.resize(img,size)
img_gray = cv2.resize(img_gray,size)
#元の画像が上書きされないようにコピーを作成しておきます
img_kaze = copy.deepcopy(img)
kaze = cv2.KAZE_create()
kp1 = kaze.detect(img, None)
img_kaze = cv2.drawKeypoints(img_kaze, kp1, None)
cv2.imshow("img",img)
cv2.imshow("img_kaze",img_kaze)
cv2.waitKey(0)
cv2.destroyAllWindows()
###ORB
こちらも特徴量を抽出するアルゴリズムです。
上と同様にコーナーの特徴量の抽出を行いました。
import cv2
import numpy as np
import copy
img = cv2.imread("data/src/favorite.jpg")
img_gray = cv2.imread("data/src/favorite.jpg",0)
#用意した画像が大きいためリサイズして縮小させてます
size = (300, 200)
img = cv2.resize(img,size)
img_gray = cv2.resize(img_gray,size)
#元の画像が上書きされないようにコピーを作成しておきます
img_orb = copy.deepcopy(img)
orb = cv2.ORB_create()
kp2 = orb.detect(img_orb)
img_orb = cv2.drawKeypoints(img_orb, kp2, None)
cv2.imshow("img",img)
cv2.imshow("img_orb",img_orb)
cv2.waitKey(0)
cv2.destroyAllWindows()
上記のコードを実行した結果が以下の画像です。kazeを使用したときより特徴量の検出が少ないのが分かります。
顔検出
OpenCVの中にすでに顔を検出するための検出器が用意されているので、それを用いて顔検出を行いました。
以下、顔検出するためコードです。
import cv2
#検出器のパスです
HAAR_FILE = "C:/Users/natsumin/anaconda3/pkgs/libopencv-3.4.2-h20b85fd_0/Library/etc/haarcascades/haarcascade_frontalface_default.xml"
cascade = cv2.CascadeClassifier(HAAR_FILE)
img = cv2.imread("data/src/BTS.jpg")
img_gray = cv2.imread("data/src/BTS.jpg",0)
face = cascade.detectMultiScale(img_gray)
#検出した顔部分に緑の枠を付けます
for x, y, w, h in face:
cv2.rectangle(img, (x, y),(x + w, y + h),(0, 255,0), 1)
cv2.imshow("img",img)
cv2.imshow("img_gray",img_gray)
cv2.waitKey(0)
cv2.destroyAllWindows()
上記の実行結果は以下の画像です。顔検出できた部分は緑の枠で囲まれています。RMさんだけ検出されませんでした。
BTSの新しいシングルのイメージ画像を使わせていただきました。
###最後に
まだまだ画像処理を理解するには道のりが長そうです。
引き続き勉強します。
間違いなどありましたら、ご指摘いただけると幸いです。