【OpenCV-Python】Sobel(エッジ検出)

OpenCVでソーベルフィルタ処理を行うには、Sobel()関数を用います。

ソーベルフィルタは、画像のエッジを検出することで、位置決めや寸法計測などに用いられます。

ソーベルフィルタ処理の構文

Sobel( src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]] ) -> dst

引数

src 入力画像
ddepth 出力画像のビット深度
dx x方向に関する微分の次数
dy y方向に関する微分の次数
ksize カーネルのサイズ 1, 3, 5, 7 のいずれか
scale 出力する値の倍率
delta 出力する値に加算する値
borderType 外周処理の種類を指定


戻り値

dst ソーベルフィルタ処理された画像データ

サンプルプログラム

よくあるソーベルフィルタ処理のサンプルプログラムを以下に示します。

ソーベルフィルタはノイズの影響を受けやすい処理のため、最初にノイズ除去のため、メディアンフィルタ処理を施しています。


import cv2

src = cv2.imread("Text.bmp", cv2.IMREAD_UNCHANGED)

# ノイズ除去(メディアンフィルタ)
src_median = cv2.medianBlur(src, 5)
# ソーベルフィルタ
sobel_x = cv2.Sobel(src_median, cv2.CV_32F, 1, 0) # X方向
sobel_y = cv2.Sobel(src_median, cv2.CV_32F, 0, 1) # Y方向

# 立下りエッジ(白から黒へ変化する部分)がマイナスになるため絶対値を取る
# alphaの値は画像表示に合わせて倍率調整
sobel_x = cv2.convertScaleAbs(sobel_x, alpha = 0.5)
sobel_y = cv2.convertScaleAbs(sobel_y, alpha = 0.5)

# X方向とY方向を足し合わせる
sobel_xy = cv2.add(sobel_x, sobel_y)

# 二値化処理後の画像表示
cv2.imshow("Src Image", src)

cv2.imshow("SobelX", sobel_x)
cv2.imshow("SobelY", sobel_y)
cv2.imshow("SobelXY", sobel_xy)
cv2.waitKey()

実行結果

 

実際のSobelフィルタ処理の使い方

Sobleフィルタは最初にも書きましたが、被写体のエッジ部分を検出することで、位置決めや寸法計測に用います。

そのため、上記に示したようなエッジ部分を白く強調した画像は、ほとんど用いる事はありません。

さらに、上記のサンプルでは、Sobelフィルタ処理の絶対値を取得していますが、実際には、正、負の値が重要になります。

例えば、下図のようなバーコードの画像において、線の幅や位置を知りたいときは、黒い線の右側と左側を区別する必要があります。

画像を左側から右側に見た時、白から黒に変化する部分(立下りエッジ)では、X方向のソーベルフィルタ処理の値は負になります。

同様に黒から白に変化する部分(立上りエッジ)では、X方向のソーベルフィルタ処理の値は正になります。

上図の赤矢印の輝度値の一部分をグラフにすると、下図のようになります。

この輝度部分に関して、ソーベルフィルタ処理を行うと、

のようになり、ソーベルフィルタの値の正負を区別することで、立上りエッジと立下りエッジを認識することができるようになります。

この事を利用したサンプルを以下に示します。

import cv2

cv2.namedWindow("Image", cv2.WINDOW_NORMAL)

src = cv2.imread("image.jpg", cv2.IMREAD_GRAYSCALE) # 処理画像
src_disp = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR) # 結果表示用カラー画像

# ソーベルフィルタ
sobel_x = cv2.Sobel(src, cv2.CV_32F, 1, 0) # X方向

# 中心付近の横1ライン分のデータ
sobel_value = sobel_x[112][:]

thre_high = 300 # 立上りエッジのしきい値
thre_low = -300 # 立下りエッジのしきい値

min = 0
max = 0

# 縦方向の中心付近のY座標
y = 112

for i in range(src.shape[1]):

    if sobel_x[y, i] > thre_high:
        if max < sobel_x[y, i]:
            max = sobel_x[y, i]
            max_x = i
    else:
        if max != 0:
            # 立上りエッジの描画
            cv2.line(src_disp, (max_x, 0), (max_x, src.shape[0] - 1), (255, 0, 0), 1)
            max = 0

    if sobel_x[y, i] < thre_low: if min > sobel_x[y, i]:
            min = sobel_x[y, i]
            min_x = i
    else:
        if min != 0:
            # 立下りエッジの描画
            cv2.line(src_disp, (min_x, 0), (min_x, src.shape[0] - 1), (0, 0, 255), 1)
            min = 0

cv2.imshow("Image", src_disp)
cv2.waitKey()

実行結果

 

今回用いている画像は、ノイズが少ないため、ノイズ除去を行っていませんが、ノイズが多い画像の場合、エッジをぼかさずにノイズを除去するため、エッジ方向に沿ったノイズ除去フィルタを行います。

例えば、上図の画像では、幅1画素、高さ7画素のカーネルでガウシアンフィルタ処理などを行います。

 

参照ページ

https://docs.opencv.org/4.8.0/d4/d86/group__imgproc__filter.html#gacea54f142e81b6758cb6f375ce782c8d

エッジ抽出(Sobel,Prewittなど)

【OpenCV-Python】BorderTypes(画像の外周処理の設定)

【OpenCV-Python】cvtColor(色変換)

OpenCVでカラー→モノクロ変換、カラーのデータの並びを入れ替えるBGR⇔RGB変換、色抽出の前処理として使われるHSV変換や、Bayer画像をカラー画像に変換するなど、主にカラー画像の変換を行うにはcvtColor()関数を用います。

構文

cvtColor( src, code[, dst[, dstCn]] ) -> dst

引数

src 色変換を行う画像データを指定します。
色変換を行う種類(code)に合わせた画像データを指定します。
code 色変換の種類を指定します。
cv2.COLOR_BGR2GRAYなど。詳細は別途
dstCn 出力画像のチャンネル数
この値が0のバイア、チャンネル数はsrcとcodeから自動的に求められます。
と、あるのですが、詳細は分からず。。

 

戻り値

dst 変換された画像データ

 

codeの設定について

codeの設定により、様々な色変換を指定できます。
コードのフォーマットは

変換前 2 変換後

となっています。

さらに、例えばOpenCVのカラー画像の場合、BGRと指定するように、画像データの並びの順番で色を指定する必要があります。

codeの種類は非常に多くあるのですが、個人的に使った事のある、使いそうなcodeを以下に示します。

 

COLOR_BGR2GRAY カラー画像(8bit3ch)をグレースケールの画像に変換します。
COLOR_BGRA2GRAY カラー画像(8bit4ch)をグレースケールの画像に変換します。
COLOR_GRAY2BGR グレースケールの画像をカラー画像(8bit3ch)に変換します。
B,G,Rの値はすべてGRAYの値と同じ
COLOR_GRAY2BGRA グレースケールの画像をカラー画像(8bit4ch)に変換します。
B,G,Rの値はすべてGRAYの値と同じ
Aの値は255
COLOR_BGR2RGB OpenCVのカラー画像(BGR)のデータの並びをR,G,Bの順に変換します。
他のPythonモジュールとデータの受け渡し時に用います。
COLOR_RGB2BGR データの並びがR,G,B順のカラー画像をOpenCVのカラー画像(BGR)に変換します。
COLOR_BGR2HSV カラー(BGR)からHSVに変換します。
色相(Hue)の値は0~360°で計算されますが、R,G,Bの各データが8bitのとき値が0~255の範囲なので、Hueの値は半分の0~128となります。
COLOR_HSV2BGR HSVからカラー(BGR)に変換します。
Hueの値は0~360°の半分の0~180で指定する必要があります。
COLOR_BGR2HSV_FULL カラー(BGR)からHSVに変換します。
色相(Hue)の値は0~360°で計算されますが、R,G,Bの各データが8bitのとき値が0~360°の値を0~255に均等に割り振られます。
COLOR_HSV2BGR_FULL HSVからカラー(BGR)に変換します。
Hueの値は0~360°の値を0~255に均等に割り振られた値で指定する必要があります。
COLOR_BGR2YUV カラー(BGR)からYUVに変換します。
COLOR_YUV2BGR YUVからカラー(BGR)に変換します。
COLOR_BayerBGGR2BGR Bayerパターンからカラー(BGR)に変換します。
COLOR_BayerGBRG2BGR Bayerパターンからカラー(BGR)に変換します。
COLOR_BayerRGGB2BGR Bayerパターンからカラー(BGR)に変換します。
COLOR_BayerGRBG2BGR Bayerパターンからカラー(BGR)に変換します。

他のコードの種類については、下記ページを参照ください。

https://docs.opencv.org/4.8.0/d8/d01/group__imgproc__color__conversions.html#ga4e0972be5de079fed4e3a10e24ef5ef0

色変換式については下記ページを参照してください。

https://docs.opencv.org/4.8.0/de/d25/imgproc_color_conversions.html

 

サンプルプログラム

カラー(BGR)→モノクロ(GRAY)変換を行った例を以下に示します。

import cv2

# 画像ファイルの読込
src = cv2.imread("Mandrill.bmp", cv2.IMREAD_UNCHANGED)

# カラー(BGR)→モノクロ(GRAY)変換
dst = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)

# 処理前画像の表示
cv2.imshow("Src Image", src)
# カラー→モノクロ変換後の画像表示
cv2.imshow("COLOR_BGR2GRAY", dst)

# キー入力待ち
cv2.waitKey()

実行結果

 

参照ページ

https://docs.opencv.org/4.8.0/d8/d01/group__imgproc__color__conversions.html#ga397ae87e1288a81d2363b61574eb8cab

https://docs.opencv.org/4.8.0/d8/d01/group__imgproc__color__conversions.html#ga4e0972be5de079fed4e3a10e24ef5ef0

https://docs.opencv.org/4.8.0/de/d25/imgproc_color_conversions.html

【OpenCV-Python】デモザイキング(Bayer変換)

【OpenCV-Python】cvtColorでBayer変換するときの定数

【OpenCV-Python】filter2D(任意カーネルフィルタ)

OpenCVで任意のカーネルを指定してフィルタ処理を行うには、filter2D()関数を用います。

カーネルそのものは、numpyの二次元配列で指定します。

 

構文

filter2D( src, ddepth, kernel[, dst[, anchor[, delta[, borderType]]]] ) -> dst

引数

src 処理を行う画像データを指定します。
ddepth 出力データの1画素1chあたりのビット深度を指定します。
-1 を指定すると、入力データと同じビット深度になります。
設定できる組み合わせは下記を参照ください。
kernel kernelの値を二次元配列で指定します。
カラーなどの複数チャンネルのデータでは、すべて同じカーネルが適用されます。
anchor kernelの基準位置を指定します。
anchorを指定しないか、(-1, -1)を指定すると、基準位置はカーネルの中心位置となります。
delta kernelで処理された後にdeltaの値が加えられます。
borderType ボーダー処理の種類をBorderTypesのenumで指定します。

ddepthに指定可能な組み合わせ

入力データのビット深度 出力データのビット深度
cv2.CV_8U -1, cv2.CV_16S, cv2.CV_32F, cv2.CV_64F
cv2.CV_16U, cv2.CV_16S -1, cv2.CV_16S, cv2.CV_32F, cv2.CV_64F
cv2.CV_32F -1, cv2.CV_32F
cv2.CV64F -1, cv2.CV_64F


戻り値

dst フィルタ処理された画像データ
ビット深度はddpethで指定された値になります。

サンプルプログラム

カーネルの値を自分で指定したガウシアンフィルタ処理を行うサンプルプログラムを以下に示します。

import cv2
import numpy as np

# 処理前画像の読込
src = cv2.imread("Mandrill.bmp", cv2.IMREAD_UNCHANGED)

# 3x3のガウシアンフィルタのカーネル
kernel = np.array([
    [1, 2, 1],
    [2, 4, 2],
    [1, 2, 1]
    ], dtype = np.float32)
kernel /= 16

# 任意カーネルフィルタの実施
dst = cv2.filter2D(src, -1, kernel)

# 任意カーネルフィルタ処理後の画像表示
cv2.imshow("Src Image", src )
cv2.imshow("filter2D", dst)
cv2.waitKey()

実行結果

 

出力先のビット深度に符号なしの値を指定すると、kernelにより計算された値がビット深度の範囲を超える場合、値はクリップされます。

例えば、出力先のビット数が8bitのとき、ビット深度の範囲は0~255なので、計算結果が負の場合は0に、255を超える場合は255に補正されます。

 

評価プログラム

import cv2
import numpy as np

src = np.array([
    [0, 0, 0, 255, 255, 255, 0, 0, 0],
    [0, 0, 0, 255, 255, 255, 0, 0, 0],
    [0, 0, 0, 255, 255, 255, 0, 0, 0],
    [0, 0, 0, 255, 255, 255, 0, 0, 0],
    [0, 0, 0, 255, 255, 255, 0, 0, 0],
    [0, 0, 0, 255, 255, 255, 0, 0, 0],
    [0, 0, 0, 255, 255, 255, 0, 0, 0],
    [0, 0, 0, 255, 255, 255, 0, 0, 0],
    [0, 0, 0, 255, 255, 255, 0, 0, 0]
    ], dtype = np.uint8)

# ソーベルフィルタのカーネル
kernel = np.array([
    [-1, 0, 1],
    [-2, 0, 2],
    [-1, 0, 1]
    ])

# 任意カーネルフィルタ
dst = cv2.filter2D(src, -1, kernel)

print("-----src------------------------------")
print(src)
print("-----dst------------------------------")
print(dst)

実行結果

—–src——————————
[[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]]
—–dst——————————
[[ 0 0 255 255 0 0 0 0 0]
[ 0 0 255 255 0 0 0 0 0]
[ 0 0 255 255 0 0 0 0 0]
[ 0 0 255 255 0 0 0 0 0]
[ 0 0 255 255 0 0 0 0 0]
[ 0 0 255 255 0 0 0 0 0]
[ 0 0 255 255 0 0 0 0 0]
[ 0 0 255 255 0 0 0 0 0]
[ 0 0 255 255 0 0 0 0 0]]

 

出力先のビット深度を符号ありの16bitにしたときのサンプルは以下の通りです。

import cv2
import numpy as np

src = np.array([
    [0, 0, 0, 255, 255, 255, 0, 0, 0],
    [0, 0, 0, 255, 255, 255, 0, 0, 0],
    [0, 0, 0, 255, 255, 255, 0, 0, 0],
    [0, 0, 0, 255, 255, 255, 0, 0, 0],
    [0, 0, 0, 255, 255, 255, 0, 0, 0],
    [0, 0, 0, 255, 255, 255, 0, 0, 0],
    [0, 0, 0, 255, 255, 255, 0, 0, 0],
    [0, 0, 0, 255, 255, 255, 0, 0, 0],
    [0, 0, 0, 255, 255, 255, 0, 0, 0]
    ], dtype = np.uint8)

# ソーベルフィルタのカーネル
kernel = np.array([
    [-1, 0, 1],
    [-2, 0, 2],
    [-1, 0, 1]
    ])

# 任意カーネルフィルタ
dst = cv2.filter2D(src, cv2.CV_16S, kernel)

print("-----src------------------------------")
print(src)
print("-----dst------------------------------")
print(dst)

実行結果

—–src——————————
[[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]
[ 0 0 0 255 255 255 0 0 0]]
—–dst——————————
[[ 0 0 1020 1020 0 -1020 -1020 0 0]
[ 0 0 1020 1020 0 -1020 -1020 0 0]
[ 0 0 1020 1020 0 -1020 -1020 0 0]
[ 0 0 1020 1020 0 -1020 -1020 0 0]
[ 0 0 1020 1020 0 -1020 -1020 0 0]
[ 0 0 1020 1020 0 -1020 -1020 0 0]
[ 0 0 1020 1020 0 -1020 -1020 0 0]
[ 0 0 1020 1020 0 -1020 -1020 0 0]
[ 0 0 1020 1020 0 -1020 -1020 0 0]]

 

参照ページ

https://docs.opencv.org/4.8.0/d4/d86/group__imgproc__filter.html#ga27c049795ce870216ddfb366086b5a04

https://docs.opencv.org/4.8.0/d4/d86/group__imgproc__filter.html#filter_depths

【OpenCV-Python】medianBlur(メディアンフィルタ)

OpenCVで画像のメディアンフィルタを行うには、medianBlur()関数を用います。

メディアンフィルタは、ごま塩ノイズやスパイクノイズなどと言われる、小さい点々のノイズを除去するのに効果的です。

また、他の平滑化(blur)やガウシアンフィルタ(GaussianBlur)などと比べて、画像のエッジ部分がぼやけることがありません。

元画像 メディアンフィルタ後

メディアンフィルタ処理の構文

medianBlur( src, ksize[, dst] ) -> dst

引数

src メディアンフィルタ処理を行う画像データを指定します。
ksize カーネルのサイズを正の奇数で指定します。1, 3, 5…
カーネルは幅と高さが同じサイズになります。

戻り値

dst メディアン処理された画像データ

サンプルプログラム

メディアンフィルタ処理のサンプルプログラムを以下に示します。

import cv2

src = cv2.imread("Pepper.bmp", cv2.IMREAD_UNCHANGED)

# メディアンフィルタ
dst = cv2.medianBlur(src, 5)

# 二値化処理後の画像表示
cv2.imshow("Src Image", src)
cv2.imshow("medianBlur", dst)
cv2.waitKey()

実行結果

参照ページ

https://docs.opencv.org/4.8.0/d4/d86/group__imgproc__filter.html#ga564869aa33e58769b4469101aac458f9

メディアンフィルタ

【OpenCV-Python】GaussianBlur(ガウシアンフィルタ)

OpenCVで画像のガウシアンフィルタ処理を行うには、GaussianBlur()関数を用います。

ガウシアンフィルタは、「ガウス関数のσの値を変えると、平滑化の効果を変えられる」という説明が多いかと思いますが、ガウシアンフィルタには、それよりも大事な、高周波成分を除去できるという効果があります。

高周波成分を除去するには、σの値は何でもいい訳ではないのですが、OpenCVでは、この高周波成分を除去できるσの値をカーネルのサイズから自動計算で計算させることもできます。

 

ガウシアンフィルタの構文

GaussianBlur( src, ksize, sigmaX[, dst[, sigmaY[, borderType]]] ) -> dst

引数

src ガウシアンフィルタ処理を行う画像データを指定します。
データタイプはuint8, uint16, int16, float32, float64に対応しています。
ksize ガウシアンフィルタのカーネルのサイズを(幅, 高さ)で指定します。
幅、高さの値は正の奇数の値である必要があります。
また、幅、高さに0を指定すると、σの値(sigmaX, sigmaY)から自動で計算します。
sigmaX X方向のσの値を指定します。
-1などのマイナスの値を指定するとカーネルのサイズから自動でσの値を計算します。
σ = 0.3 * (ksize – 1) * 0.5 – 1) * 0.8
sigmaY Y方向のσの値を指定します。
指定しないか、0を指定すると、sigmaXと同じσの値を用います。
sigmaXとsigmaYの両方が0の場合は、それぞれ、カーネルの幅と高さからσの値を自動で計算します。
borderType ボーダー処理の種類をBorderTypesのenumで指定します。


戻り値

dst ガウシアンフィルタ処理された画像データ

サンプルプログラム

ガウシアンフィルタ処理の一般的に行うシンプルなサンプルプログラムを以下に示します。

import cv2

src = cv2.imread("Text.bmp", cv2.IMREAD_UNCHANGED)

# ガウシアンフィルタ
dst = cv2.GaussianBlur(src, (3, 3), -1)

# ガウシアンフィルタ後の画像表示
cv2.imshow("Src Image", src)
cv2.imshow("GaussianBlur", dst)
cv2.waitKey()

実行結果

 

各種パラメータを設定したガウシアンフィルタ処理の例は以下の通りです。

import cv2

src = cv2.imread("Text.bmp", cv2.IMREAD_UNCHANGED)

# ガウシアンフィルタ
dst1 = cv2.GaussianBlur(
    src,    # 入力画像
    (7, 7), # カーネルのサイズ(幅、高さ)
    1.4,    # X方向のσの値(-1など負の値を指定するとカーネルサイズから自動計算)
    0,      # Y方向のσの値(0だとX方向と同じ)
    borderType = cv2.BORDER_DEFAULT  # ボーダー処理の種類
    )
# ガウシアンフィルタ(カーネルサイズを自動計算)
dst2 = cv2.GaussianBlur(
    src,    # 入力画像
    (0, 0), # カーネルのサイズを0にしてσの値からカーネルサイズを自動計算
    2       # X方向のσの値
    )

# 二値化処理後の画像表示
cv2.imshow("Src Image", src)
cv2.imshow("GaussianBlur1", dst1)
cv2.imshow("GaussianBlur2", dst2)
cv2.waitKey()

実行結果

 

ガウシアンフィルタのσの値について

高周波成分を除去するためには、σの値を大きくすれば良いという訳ではなく、最適な値があります。

OpenCVでは、sigmaX, sigmaYの値に負の値を指定するとカーネルのサイズ(幅、高さ)から下記の計算式から最適なσの値を計算してくれます。

σ = 0.3 * (ksize – 1) * 0.5 – 1) * 0.8

実際に、ガウシアンフィルタで用いられるカーネルの値は、getGaussianKernel()関数から取得することができます。

いくつか例を示すと

ksize= 3のとき
0.25, 0.5, 0.25

ksize= 5のとき
0.0625, 0.25, 0.375, 0.25, 0.0625

ksize= 7のとき
0.03125, 0.109375, 0.21875, 0.2825, 0.21875, 0.109375, 0.03125

となります。

 

ガウシアンフィルタのσの値と高周波成分の除去効果について

二次元の画像において、もっとも高周波な画像は、下図のような1画素おきの市松模様の画像となります。

この画像において、σの値を0.6, 0.8(自動計算), 3 と変えながらガウシアンフィルタ処理を行うとどのように変化するのか?を見てみたいと思います。

 

サンプルプログラム

import numpy as np
import cv2

def create_test_pattern():
    # テストパターンの作成
    data = np.array([
        [0, 255],
        [255, 0]
        ], dtype= np.uint8)
    img = np.tile(data, (16, 16))
    return img

# テストパターンの作成
src = create_test_pattern()

cv2.namedWindow("Src Image", cv2.WINDOW_NORMAL)
cv2.namedWindow("Gauss 0.3", cv2.WINDOW_NORMAL)
cv2.namedWindow("Gauss -1", cv2.WINDOW_NORMAL)
cv2.namedWindow("Gauss 3", cv2.WINDOW_NORMAL)

# σを変えながらガウシアン処理
gauss1 = cv2.GaussianBlur(src, (3, 3), 0.6) # σ=0.6
gauss2 = cv2.GaussianBlur(src, (3, 3), -1)  # σは自動計算(σ=0.8)
gauss3 = cv2.GaussianBlur(src, (3, 3), 3)   # σ=3

# ガウシアンフィルタ後の画像表示
cv2.imshow("Src Image", src)
cv2.imshow("Gauss 0.3", gauss1)
cv2.imshow("Gauss -1", gauss2)
cv2.imshow("Gauss 3", gauss3)
cv2.waitKey()

実行結果

左からσ=0.6,  σ=0.8(自動計算),  σ=3

 

このように、σの値は、単に大きい方が高周波成分を除去できるという訳ではなく、最適なσの値があり、このσの値は、OpenCVが自動で計算してくれる値を用いた方が高周波成分を除去できる事が確認できます。

 

参照ページ

https://docs.opencv.org/4.8.0/d4/d86/group__imgproc__filter.html#gaabe8c836e97159a9193fb0b11ac52cf1

https://docs.opencv.org/4.8.0/d4/d86/group__imgproc__filter.html#gac05a120c1ae92a6060dd0db190a61afa

ガウシアンフィルタの処理アルゴリズムとその効果

移動平均フィルタ VS ガウシアンフィルタ

【OpenCV-Python】blur(平滑化、移動平均)

OpenCVで画像の平滑化を行うには、blur()関数を用います。

ここで言う平滑化は、単純な移動平均フィルタとなります。

移動平均フィルタの処理については、下記ページを参照ください。

平滑化(移動平均)フィルタ

平滑化処理の構文

blur( src, ksize[, dst[, anchor[, borderType]]] ) -> dst

引数

src 平滑化を行う画像データを指定します。
データタイプはuint8, uint16, int16, float32, float64に対応しています。
ksize カーネルのサイズを(幅, 高さ)で指定します。
通常、幅と高さは奇数の値を指定してください。
anchor カーネルの基点となる座標を(x, y)で指定します。
(-1, -1)と指定すると、カーネルの中心座標となります。
指定しないと、初期値でカーネルの中心座標となります。
borderType ボーダー処理の種類をBorderTypesのenumで指定します。

戻り値

dst 平滑化された画像データ

サンプルプログラム

平滑化処理のサンプルプログラムを以下に示します。

import cv2

src = cv2.imread("Text.bmp", cv2.IMREAD_UNCHANGED)

# 平滑化(移動平均)
dst = cv2.blur(src, (5, 5))

# 二値化処理後の画像表示
cv2.imshow("Src Image", src)
cv2.imshow("blur", dst)
cv2.waitKey()

実行結果

 

anchorやborderTypeについては、ほぼ、指定することは無いかと思いますが、念のため、それぞれを指定したサンプルを示します。

import cv2

src = cv2.imread("Text.bmp", cv2.IMREAD_UNCHANGED)

# 平滑化(移動平均)
dst = cv2.blur(
    src,                # 入力画像
    (3, 21),            # カーネルのサイズ(幅、高さ)
    anchor = (1, 10),   # カーネルの中心座標
    borderType = cv2.BORDER_DEFAULT  # ボーダー処理の種類
    )

# 二値化処理後の画像表示
cv2.imshow("Src Image", src)
cv2.imshow("blur", dst)
cv2.waitKey()

実行結果

参照ページ

https://docs.opencv.org/4.8.0/d4/d86/group__imgproc__filter.html#ga8c45db9afe636703801b0b2e440fce37

平滑化(移動平均)フィルタ

【OpenCV-Python】BorderTypes(画像の外周処理の設定)

外周画像の処理

【OpenCV-Python】BorderTypes(画像の外周処理の設定)

blur(平滑化)やGaussianBlur(ガウシアンフィルタ)など、カーネルを使った画像フィルタ処理では、画像の最外周部分では、カーネルが画像からはみ出してしまうため、はみ出した部分を、どのように補うか?の設定の種類にBoarderTypes というenumが定義されています。

enum定義

cv2.BORDER_CONSTANT 指定した値(i)で補います。
iiiiii|abcdefgh|iiiiiii
cv2.BORDER_REPLICATE 最も外側の値で補います。
aaaaaa|abcdefgh|hhhhhhh
cv2.BORDER_REFLECT 外側の位置を基準に折り返すように補います。
fedcba|abcdefgh|hgfedcb
cv2.BORDER_WRAP 値が向きが変わらず、繰り返されるように補います。
cdefgh|abcdefgh|abcdefg
cv2.BORDER_REFLECT_101 最も外側の値の位置を中心に折り返すように補います。
gfedcb|abcdefgh|gfedcba
cv2.BORDER_TRANSPARENT 透過(はみ出した部分を別の画像で補います)
uvwxyz|abcdefgh|ijklmno
cv2.BORDER_REFLECT101 cv2.BORDER_REFLECT_101と同じ
cv2.BORDER_DEFAULT cv2.BORDER_REFLECT_101と同じ
cv2.BORDER_ISOLATED 画像の外側を見ない(処理しない)

 

サンプルプログラム

Boarderの処理は、blurなどの関数内部で行われる事が多いので、ボーダー付きの画像をわざわざ自分で作る事は少ないのですが、copyMakeBoarder()関数によって、ボーダー付きの画像を生成する事が出来るので、そのサンプルを下記に示します。

import cv2

src = cv2.imread("Parrots.bmp")

bd = 64 # 枠線の太さ

border0 = cv2.copyMakeBorder(src, bd, bd, bd, bd, cv2.BORDER_CONSTANT, value = [0, 0, 255])
border1 = cv2.copyMakeBorder(src, bd, bd, bd, bd, cv2.BORDER_REPLICATE)
border2 = cv2.copyMakeBorder(src, bd, bd, bd, bd, cv2.BORDER_REFLECT)
border3 = cv2.copyMakeBorder(src, bd, bd, bd, bd, cv2.BORDER_WRAP)
border4 = cv2.copyMakeBorder(src, bd, bd, bd, bd, cv2.BORDER_REFLECT_101)
border6 = cv2.copyMakeBorder(src, bd, bd, bd, bd, cv2.BORDER_REFLECT101)
border7 = cv2.copyMakeBorder(src, bd, bd, bd, bd, cv2.BORDER_DEFAULT)
border8 = cv2.copyMakeBorder(src, bd, bd, bd, bd, cv2.BORDER_ISOLATED)
 
cv2.imshow("Src", src)
cv2.imshow("BORDER_CONSTANT", border0)
cv2.imshow("BORDER_REPLICATE", border1)
cv2.imshow("BORDER_REFLECT", border2)
cv2.imshow("BORDER_WRAP", border3)
cv2.imshow("BORDER_REFLECT_101", border4)
cv2.imshow("BORDER_REFLECT101", border6)
cv2.imshow("BORDER_DEFAULT", border7)
cv2.imshow("BORDER_ISOLATED", border8)

cv2.waitKey()

実行結果

 

個人的にはBORDER_DEFAULTにもなっている、BORDER_REFLECT_101を使う事が多いです。

 

参照ページ

https://docs.opencv.org/4.8.0/d2/de8/group__core__array.html#ga209f2f4869e304c82d07739337eae7c5

外周画像の処理

【OpenCV-Python】threshold(二値化、大津の二値化)

OpenCVで画像の二値化大津の二値化を行うには、threshold()関数を用います。

二値化処理は、欠陥検査などにおいて、ノイズ除去などのフィルタを行った後、二値化処理を行い、面積や形状、位置などから、OK/NGの判定を行う場合などに用いられます。

二値化処理の構文

threshold( src, thresh, maxval, type[, dst] ) -> retval, dst

引数

src 二値化を行う画像データを指定します。
複数チャンネルの8bit、もしくは32bitのfloatのデータです。
大津の二値化の場合は8bit1ch(グレースケール)に限定されます。
thresh 二値化のしきい値を指定します。
THRESH_OTSUのときは無視されます。
maxval 二値化処理後の値を指定します。通常は255を指定
THRESH_BINARY, THRESH_BINARY_INV, THRESH_OTSU のとき有効
type 二値化処理方法を指定します。(下記参照)

typeの設定

cv2.THRESH_BINARY 輝度値がthreshより大きい値をmaxval、それ以外を0にします。
cv2.THRESH_BINARY_INV 輝度値がthreshより大きい値を0、それ以外をmaxvalにします。
cv2.THRESH_TRUNC 輝度値がthreshより大きい値をthresh、それ以外はそのままにします。
cv2.THRESH_TOZERO 輝度値がthreshより大きい値はそのまま、それ以外を0にします。
cv2.THRESH_TOZERO_INV 輝度値がthreshより大きい値を0、それ以外はそのままにします。
cv2.THRESH_OTSU 大津の二値化により、しきい値を求め、
求めたしきい値より大きい値をmaxval、それ以外を0にします。

 

戻り値

retval 二値化に用いたしきい値。
主に大津の二値化の場合のしきい値確認に用います。
dst 二値化された画像データ

サンプルプログラム

二値化処理のサンプルプログラムを以下に示します。

import numpy as np
import cv2

def create_test_pattern():
    # テストパターンの作成
    increase = np.linspace(0, 255, 128, dtype = np.uint8)
    decrease = np.linspace(255, 0, 128, dtype = np.uint8)
    line = np.concatenate([increase, decrease, increase, decrease])
    img = np.tile(line, (128, 1))
    return img

# テストパターンの作成
src =  create_test_pattern()

# 二値化
ret,thresh1 = cv2.threshold(src,127,255,cv2.THRESH_BINARY)
ret,thresh2 = cv2.threshold(src,127,255,cv2.THRESH_BINARY_INV)
ret,thresh3 = cv2.threshold(src,127,255,cv2.THRESH_TRUNC)
ret,thresh4 = cv2.threshold(src,127,255,cv2.THRESH_TOZERO)
ret,thresh5 = cv2.threshold(src,127,255,cv2.THRESH_TOZERO_INV)
ret,thresh6 = cv2.threshold(src,127,255,cv2.THRESH_OTSU)

# 二値化処理後の画像表示
cv2.imshow("Src Image", src)
cv2.imshow("THRESH_BINARY", thresh1)
cv2.imshow("THRESH_BINARY_INV", thresh2)
cv2.imshow("THRESH_TRUNC", thresh3)
cv2.imshow("THRESH_TOZERO", thresh4)
cv2.imshow("THRESH_TOZERO_INV", thresh5)
cv2.imshow("THRESH_OTSU", thresh6)
cv2.waitKey()

実行結果

 

大津の二値化に関しては、テストパターンだと、あまり効果を感じないため、自然画で処理した例を示します。

 

大津の二値化のサンプルプログラム

import cv2

src = cv2.imread("Cameraman.bmp", cv2.IMREAD_GRAYSCALE)

# 大津の二値化
ret,thresh = cv2.threshold(src,0,255,cv2.THRESH_OTSU)

# 二値化処理後の画像表示
cv2.imshow("Src Image", src)
cv2.imshow("THRESH_OTSU", thresh)
cv2.waitKey()

実行結果

参照ページ

https://docs.opencv.org/4.x/d7/d1b/group__imgproc__misc.html#gae8a4a146d1ca78c626a53577199e9c57

https://docs.opencv.org/4.x/d7/d1b/group__imgproc__misc.html#gaa9e58d2860d4afa658ef70a9b1115576

https://docs.opencv.org/4.x/d7/d4d/tutorial_py_thresholding.html

二値化

判別分析法(大津の二値化)