9
Help us understand the problem. What are the problem?

posted at

画像処理初心者がOpenCVを使ってみた(2)

この記事は、画像処理エンジニア検定を受験するにあたり、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軸でエッジの検出結果が違うことが分かります。
image.png

ラプラシアン法でエッジ検出

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()

この方法では、画像の軸に関係なくエッジを検出します。
以下の画像が出力結果です。
image.png

さらにエッジ検出をわかりやすくするために、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()

上記のコードの実行結果は以下のようになりました。
上のラプラシアン法よりもノイズが少ないことが分かります。
image.png

キャニー法でエッジ検出

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()

上記のコードを実行した結果が以下です。
image.png

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()

上記のコードを実行した結果が以下です。上の例よりノイズが少ないことが分かります。
image.png

直線の検出

画像から直線を検出する方法を試しました。

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()

上記のコードを実行した結果、以下の画像が出力されました。直線部分が検出されていることが分かります。
image.png

モルフォロジー演算

画像ピクセルを収縮させたり、膨張させて画像内の図形を分離するとき、ノイズを消したりするときに使用します。
詳しくは以下のページを参考に勉強させていただきました。

以下のコードは、元画像(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()

実行した結果が以下の画像です。膨張させた際は画像の白い部分を拾ってきていますが、縮小させた際は黒い部分を拾ってきていることが分かります。
image.png

次にクロージングをしてみます。
画像を膨張させた後に縮小を行ってみました。
以下が実行コードです。

#上のコードの続きです
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画像は、膨張させただけの画像です。比較するために表示させてみました。
image.png

特徴抽出

特徴とはパターン認識に役立つ情報量の多い部分のことを指します。

詳しくまとまっている記事がありましたので、こちらで勉強させていただきました。

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()

実行結果は以下の画像です。
コーナーの部分が赤色で表示されていることが分かります。
image.png

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()

上記のコードの実行結果は以下の画像です。
image.png

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を使用したときより特徴量の検出が少ないのが分かります。
image.png

顔検出

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の新しいシングルのイメージ画像を使わせていただきました。
image.png

最後に

まだまだ画像処理を理解するには道のりが長そうです。
引き続き勉強します。
間違いなどありましたら、ご指摘いただけると幸いです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
9
Help us understand the problem. What are the problem?