画像の平滑化
画像の平滑化の種類には有名どころとして、前回行った平均化フィルタとガウシアンフィルタがあります。
その他にも、中央値フィルターとバイラテラルフィルターについても紹介していきます。
平均化フィルタ
前回行った、3*3の1/9フィルターを簡単に行うための関数が提供されています。
import cv2
import matplotlib.pyplot as plt
%matplotlib inline
img = cv2.imread('Lenna_(test_image).png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# ブラー処理=>前回1/9と同じ
img_blur = cv2.blur(img, (3, 3))
fig = plt.figure()
plt.gray()
fig.add_subplot(1,2,1)
plt.imshow(img)
plt.gray()
fig.add_subplot(1,2,2)
plt.imshow(img_blur)
ガウシアンフィルター
ガウシアンフィルターの特徴として、カーネルサイズを奇数に設定する必要があります。
また、ぼかし度合いとして標準偏差を大きくすれば、大きくなります。逆に小さくすれば、小さくなります。
内部の処理としては、ガウス分布にしたがってカーネル内の重みを計算します。
平均化フィルタとの違いは、中央の注目画素の重みが最も大きくなり、外側に行くにつれて重みが小さくなります。これにより、中央の情報を残しながら画像をぼかすことが可能になります。
# ガウシアンフィルター
# ガウシアンフィルターはフィルター数は奇数出ないと駄目
# 与える標準偏差が大きくなればぼかし度合いも大きくなる
img_ga = cv2.GaussianBlur(img, (9, 9), 2)
fig = plt.figure()
plt.gray()
fig.add_subplot(1,2,1)
plt.imshow(img)
plt.gray()
fig.add_subplot(1,2,2)
plt.imshow(img_ga)
メディアンフィルター
メディアンフィルターは中央値をすべて塗りつぶすので、他のフィルターより変化が分かりやすいとお思います。
# メディアンフィルター
# 5*5の画像サイズの中央値をすべて塗りつぶす
img_me = cv2.medianBlur(img, 5)
fig = plt.figure()
plt.gray()
fig.add_subplot(1,2,1)
plt.imshow(img)
plt.gray()
fig.add_subplot(1,2,2)
plt.imshow(img_me)
バイラテラルフィルター
バイラテラルフィルターはガウシアンフィルターを2つ使用します。色空間に着目したものと距離に着目したものになります。この2つを考慮してフィルタリングが行われます。
これにより、今までのフィルタだとエッジの部分もぼけてしまうが、輝度の変化が急激な部分(エッジ)は残すことができます。
# 今までのフィルタだとエッジの部分もぼけてしまうが、輝度の変化が急激な部分(エッジ)は残す
# バイラテラルフィルター
# カーネルのサイズは20を指定し、次の30はそれぞれ色空間の標準偏差、距離の標準偏差
img_bi = cv2.bilateralFilter(img, 20, 30, 30)
fig = plt.figure()
plt.gray()
fig.add_subplot(1,2,1)
plt.imshow(img)
plt.gray()
fig.add_subplot(1,2,2)
plt.imshow(img_bi)
画像の微分
画像を微分するとエッジが検出できます。
傾き = 隣の画素 – 現在の画素値
下記のような画像で、微分をすれば赤枠の部分を検出でき、黒と白の境界線だけが残ります。
エッジの検出(Sobel・Laplacian・Canny)
Sobelフィルター
Sobelフィルターは微分する方向を自分で選ぶことができるので、縦方向・横方向それぞれにエッジ検出をすることができます。
import cv2
import matplotlib.pyplot as plt
%matplotlib inline
img = cv2.imread('Lenna_(test_image).png', 0)
# sobelフィルター
img_sobel_x = cv2.Sobel(img, cv2.CV_32F, 1, 0, ksize=3) # x方向への微分
img_sobel_y = cv2.Sobel(img, cv2.CV_32F, 0, 1, ksize=3) # y方向への微分
# 負の値が入っているので、0-255の値に直します
img_sobel_x
# array([[ 0., -4., -8., ..., -64., -148., 0.],
# [ 0., -4., -8., ..., -64., -148., 0.],
# [ 0., -4., -8., ..., -64., -148., 0.],
# ...,
# [ 0., 26., 23., ..., -4., -2., 0.],
# [ 0., 39., 30., ..., 7., 14., 0.],
# [ 0., 44., 32., ..., 12., 20., 0.]], dtype=float32)
img_sobel_x = cv2.convertScaleAbs(img_sobel_x)
img_sobel_y = cv2.convertScaleAbs(img_sobel_y)
fig = plt.figure(figsize=(15, 9))
plt.gray()
fig.add_subplot(1,3,1)
plt.imshow(img)
plt.gray()
fig.add_subplot(1,3,2)
plt.imshow(img_sobel_x)
plt.gray()
fig.add_subplot(1,3,3)
plt.imshow(img_sobel_y)
Laplacianフィルター
sobelフィルターと違う点は、微分方向の指定がないため全てのエッジが検出可能ですが、値は小さくなります。
# Laplacianフィルター
# 2次の微分:方向指定はなし
img_lap = cv2.Laplacian(img, cv2.CV_32F)
img_lap = cv2.convertScaleAbs(img_lap)
fig = plt.figure(figsize=(15, 9))
plt.gray()
fig.add_subplot(1,2,1)
plt.imshow(img)
plt.gray()
fig.add_subplot(1,2,2)
plt.imshow(img_lap)
エッジの検出が見づらい時は、検出した値を2倍にする方法があります。
ただし、余計なノイズが入り込みます。
img_lap *= 2
fig = plt.figure(figsize=(15, 9))
plt.gray()
fig.add_subplot(1,2,1)
plt.imshow(img)
plt.gray()
fig.add_subplot(1,2,2)
plt.imshow(img_lap)
Laplacian Gaussian
先程のLaplacianフィルターを2倍したやり方だと、余計なノイズが入ってしまったので、それを取り除いた状態にするのが、Laplacian Gaussianです。
# Laplacian Gaussian
img_blur = cv2.GaussianBlur(img, (3, 3), 2)
img_lap3 = cv2.Laplacian(img_blur, cv2.CV_32F)
img_lap3 = cv2.convertScaleAbs(img_lap3)
img_lap3 *= 2
fig = plt.figure(figsize=(15, 9))
plt.gray()
fig.add_subplot(1,2,1)
plt.imshow(img_lap2)
plt.gray()
fig.add_subplot(1,2,2)
plt.imshow(img_lap3)
Canny
Cannyはノイズを上手く取り除いて、エッジのみを検出します。
Laplacianは二次微分を使用している事もあり、輝度に対して敏感です。
対して、Cannyは輝度の変化が激しいところに反応します。
Cannyのアルゴリズムは大きく分けて4つに分類されます。
1、ガウシアンフィルターでぼかす
2、Sobelフィルターで微分(x軸、y軸どちらも行い、2乗和の平方根をとる)
3、極大点を探す
4、2段階の閾値処理でエッジを残す
import cv2
import matplotlib.pyplot as plt
%matplotlib inline
img = cv2.imread('Lenna_(test_image).png', 0)
# 閾値設定が重要 => エッジを残せるかどうかに関わってくる
img_canny = cv2.Canny(img, 10, 180) # 下限が10, 上限が180
img_canny2 = cv2.Canny(img, 100, 200)
fig = plt.figure(figsize=(15, 9))
plt.gray()
fig.add_subplot(1,2,1)
plt.imshow(img_canny)
plt.gray()
fig.add_subplot(1,2,2)
plt.imshow(img_canny2)