この記事の主な内容は、OpenCV-Python チュートリアルの OpenCV の画像処理部分から来ています。この部分の主な内容は次のとおりです。
-
異なる色空間間で画像を変更する方法を学びます。ビデオ内のカラフルなオブジェクトを追跡する方法も学びましょう。
-
回転、平行移動など、さまざまな幾何学的変換を画像に適用する方法を学びます。
-
グローバルしきい値処理、適応しきい値処理、Otsu の 2 値化などを使用して、画像をバイナリ イメージに変換する方法を学びます。
-
画像をぼかしたり、カスタム カーネルを使用して画像をフィルターしたりする方法などを学びます。
-
浸食、拡張、開口、閉鎖などの形態変化を理解します。
-
画像のグラデーションやエッジなどを見つける方法を学びます。
-
Canny エッジ検出を使用してエッジを見つける方法を学びます。
-
画像ピラミッドと、それを画像ブレンドに使用する方法について学びます。
-
OpenCV の輪郭に関するすべて。
-
OpenCV のヒストグラムについてのすべて。
-
OpenCV では、フーリエ変換、コサイン変換など、さまざまな画像変換が行われます。
-
テンプレート マッチングを使用して画像内のオブジェクトを検索する方法を学びます。
-
画像内の線を検出する方法を学びます。
-
画像内の円を検出する方法を学びます。
-
ウォーターシェッドアルゴリズムを使用した画像セグメンテーション
流域セグメンテーション アルゴリズムを使用して画像をセグメント化する方法を学びます。
-
GrabCut アルゴリズムを使用したインタラクティブな前景抽出
GrabCut アルゴリズムを使用して前景を抽出する方法を学びます
目標
勉強:
- さまざまなローパスフィルターを使用して画像をぼかします
- 画像にカスタム フィルターを適用する (2D コンボリューション)
2Dコンボリューション(画像フィルタリング)
1 次元信号と同様に、画像はさまざまなローパス フィルター (LPF)、ハイパス フィルター (HPF) などを使用してフィルター処理できます。LPF はノイズやぼやけた画像などを除去するのに役立ちます。HPF フィルターは、画像内のエッジを見つけるのに役立ちます。
OpenCV は、カーネルとイメージを畳み込むための関数 ** cv.filter2D() ** を提供します。たとえば、画像に平均化フィルターを試してみましょう。5x5 平均化フィルター カーネルは次のようになります。
K = 1 25 [ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ] K = \frac{1}{25} \begin{bmatrix} 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \終了{b行列}K=2 51⎣⎢⎢⎢⎢⎡1111111111111111111111111⎦⎥⎥⎥⎥⎤
操作は次のように行われます。このカーネルを 1 ピクセル上に保持し、このカーネルの下の 25 ピクセルをすべて追加し、平均を取得し、中央のピクセルを新しい平均で置き換えます。画像内のすべてのピクセルに対してこの操作を続けます。このコードを試して結果を確認してください。
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
def image_filtering():
cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
img = cv.imread(cv.samples.findFile('opencv-logo.png'))
kernel = np.ones((5, 5), np.float32) / 25
dst = cv.filter2D(img, -1, kernel)
row, col, _ = img.shape
edge = np.full((row, 10, 3), (255, 255, 255), np.uint8);
images = [img, edge, dst]
dst = cv.hconcat(images)
cv.imshow("Image", dst)
cv.waitKey(-1)
cv.destroyAllWindows()
if __name__ == "__main__":
image_filtering()
結果は次のとおりです。
画像のぼかし(画像の平滑化)
画像のぼかしは、画像をローパス フィルター カーネルで畳み込むことによって実現されます。ノイズを除去するのに役立ちます。実際には、画像から高周波成分 (ノイズ、エッジなど) が除去されます。したがって、この操作ではエッジが少しぼやけます (エッジをぼかさないぼかしテクニックもあります)。OpenCV は、主に 4 種類のぼかし技術を提供します。
1. 平均的
これは、正規化されたボックス フィルターを使用して画像を畳み込むことによって行われます。カーネル領域の下のすべてのピクセルの平均を取得し、中央の要素を置き換えるだけです。これは、関数 cv.blur() または cv.boxFilter()を通じて行われます。詳細については、カーネルに関するドキュメントを確認してください。カーネルの幅と高さを指定する必要があります。3x3 の正規化ボックス フィルターは次のようになります。
K = 1 9 [ 1 1 1 1 1 1 1 1 ] K = \frac{1}{9} \begin{bmatrix} 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \終了{b行列}K=91⎣⎡111111111⎦⎤
注
正規化されたボックスフィルターを使用したくない場合は、cv.boxFilter()を使用してください。パラメータnormalize=Falseを関数に渡します。
5x5 サイズのコアを使用する次のデモ例を見てください。
def averaging_filter():
cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
img = cv.imread(cv.samples.findFile('opencv-logo-white.png'))
blur = cv.blur(img, (5, 5))
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(blur), plt.title('Blurred')
plt.xticks([]), plt.yticks([])
plt.show()
if __name__ == "__main__":
averaging_filter()
結果は次のとおりです。
ガウスぼかし
この方法では、ボックス フィルターを使用する代わりに、ガウス カーネルが使用されます。これは関数 cv.GaussianBlur()を通じて行われます。カーネルの幅と高さを指定する必要があります。これらは正の奇数である必要があります。X 方向と Y 方向の標準偏差、sigmaX と sigmaY も指定する必要があります。sigmaX のみを指定した場合、sigmaY は sigmaX と同じ値になります。両方がゼロの場合、カーネル サイズに基づいて計算されます。ガウスぼかしは、画像からガウス ノイズを除去するのに非常に効果的です。
必要に応じて、関数cv.getGaussianKernel()を使用してガウス カーネルを作成できます。
上記のコードは、ガウスぼかし用に変更できます。
blur = cv.GaussianBlur(img, (5, 5), 0)
関数cv.getGaussianKernel()を使用する場合は、次のコードが必要です。
kernel = cv.getGaussianKernel(5, 0)
kernel_2D = kernel @ kernel.transpose()
blur = cv.filter2D(img, -1, kernel_2D)
結果は次のとおりです。
3.中央ぼかし
ここで、関数cv.medianBlur() は、カーネル領域の下のすべてのピクセルの中央値を取得し、中央の要素を中央値で置き換えます。これは、画像内の塩胡椒ノイズに最適です。興味深いことに、上記のフィルターでは、中心要素は新しく計算された値であり、画像内のピクセル値または新しい値である可能性があります。ただし、メディアンブラーでは、中心要素は常に画像内の何らかのピクセル値に置き換えられます。ノイズを効果的に低減します。そのカーネル サイズは正の奇数である必要があります。
このデモでは、元の画像に 50% のノイズを追加し、中央値のぼかしを適用しました。次のSalt_and_pepper(image, n)関数を使用してノイズ ポイントを追加します。テスト結果:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
import random
def salt_and_pepper(image, n):
print(image.shape)
for i in range(int(n / 2)):
row = random.randint(0, image.shape[0] - 1)
col = random.randint(0, image.shape[1] - 1)
write_black = random.randint(0, 2)
if write_black == 0:
image[row][col] = (255, 255, 255)
else:
image[row][col] = (0, 0, 0)
return image
def median_blurring():
cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
img = cv.imread(cv.samples.findFile('opencv-logo-white.png'))
img = salt_and_pepper(img, img.shape[0] * img.shape[1])
median = cv.medianBlur(img, 5)
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(median), plt.title('Blurred')
plt.xticks([]), plt.yticks([])
plt.show()
if __name__ == "__main__":
median_blurring()
結果は次のとおりです。
4. 双方向フィルタリング
cv.bi LateralFilter()関数は、エッジをシャープに保ちながらノイズを除去するのに非常に効果的です。ただし、この操作は他のフィルターに比べて時間がかかります。ガウス フィルターがピクセルの周囲を取得し、そのガウス加重平均を求めることを見てきました。このガウス フィルターは、フィルター処理時に近くのピクセルを考慮する別の空間関数です。ピクセルがほぼ同じ強度を持つかどうかは考慮されません。ピクセルがエッジ ピクセルであるかどうかは考慮されません。したがって、エッジもぼやけてしまいますが、これは望ましくありません。
バイラテラル フィルタリングでも空間でガウス フィルタを使用しますが、ピクセル差の関数である追加のガウス フィルタがあります。空間のガウス関数では、近くのピクセルのみがぼかしの対象として考慮されますが、強度差のガウス関数では、中心のピクセルと同様の強度を持つピクセルのみがぼかしの対象として考慮されます。エッジのピクセルの強度が大きく変化するため、エッジが維持されます。
以下の例は、バイラテラル フィルターの使用を示しています (パラメーターの詳細についてはドキュメントを参照してください)。
def bilateral_blurring():
cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
img = cv.imread(cv.samples.findFile('bilateral.jpg'))
img = img[0:img.shape[0], 0:int(img.shape[1] / 2)]
blur = cv.bilateralFilter(img, 9, 75, 75)
images = [img, blur]
dest = cv.hconcat(images)
cv.imshow("Image", dest)
cv.waitKey(-1)
cv.destroyAllWindows()
if __name__ == "__main__":
bilateral_blurring()
結果は次のとおりです。
明らかなテクスチャは消えていますが、エッジはまだ残っています。
その他のリソース
- 双方向フィルタリングの詳細情報
練習する
参考資料
ガウス カーネルで cv2.GaussianBlur と cv2.filter2D を使用すると結果が異なりますか?
OpenCVを使用してカラー画像に塩胡椒ノイズを追加する方法
Python-OpenCVを使用した画像へのノイズ付加(ガウスノイズ、塩胡椒ノイズ)の実装
終わり。