[CV] Gaussian Filter


最近はCVについて勉強していますが、整理しないと忘れてしまうかもしれないのでブログにまとめたいと思います.私の目標は1ヶ月に2つ以上やることですが、うまくいけばいいです.

1.フィルタ


フィルタはCVにおいて非常に重要な要素である.主にボリュームを利用して,画像を多様な処理が可能である.サイズ変更や画像の乱れなど、さまざまなオプションで使用できます.フィルターとも呼ばれますが、カーネルとも呼ばれます.

2.ガウスフィルター


今回は主に画像の平滑化に用いられるGaussフィルタについて学習した.lena画像に様々な値を変更し、結果をエクスポートしようとしました.
Opencvライブラリにはもちろんガウスフィルタを作成する簡単な方法がありますが、目標は自分でやることです!式を理解して符号化して表現する.

2-1. 1D Gaussian Filter



最も基本的な形式のガウスフィルタ(グラフはμ=0,σ=1\mu = 0,\sigma =1μ=0,σ=1日時.
下記のように数式で表します.
G(x)=12πσe−x22σ2G(x) =\dfrac 1 {\sqrt{2\pi}\sigma} e^{-\frac {x^2} {2\sigma^2}}G(x)=2π​σ1​e−2σ2x2​
すべてのフィルタにサイズとシグマ値があります.ここで、xxxはカーネルの中心から離れています.σ\sigmaσ はプレイヤーが直接変更できる値です.この値はノイズ処理の程度に影響します.
使用するライブラリのリスト
import scipy
import cv2
import numpy as np
import matplotlib.pyplot as plt
import math
1 D Gaussian Filterの作成
def genGausKernel1D(width, sigma):
    arr = np.arange((width//2)*(-1), (width//2)+1)
    #중심에서부터의 거리를 계산함,
    arr = np.array([float(x) for x in arr])
    #float 처리를 해주지 않으면 값이 달라질 수 있어서 처리해주었다
    kernel_1d= np.exp((-arr*arr)/(2*sigma**2)) 
    #위에서 봤던 수식을 그대로 적용했다. 달라진 점은, 앞에 상수로 나눠주는 부분을 생략한건데 
    #어차피 나중에 커널 전체 값의 합을 1로 만들기 위해서 커널 전체의 합으로 나눠주기 때문에 
    #상수부분이 필요 없다고 생각해서 나누지 않았다
    kernel_1d /= kernel_1d.sum()
    #kernel은 모든 element의 합이 1이어야하기 때문에 전체 수의 합으로 나눠준다. 
    #비율이 중요하다는 뜻으로 해석했다.
    return np.array([kernel_1d])
Opencvライブラリを使用して一度に作成!
kernel_1d = cv2.getGaussianKernel(5, 3)
# getGaussiankernel(width, sigma)의 형태임
もちろん、簡単に言えば、ライブラリ内の関数を使うほうがいいですが、理解を確認したいので、自分で実現しました.

その結果、作成した2つのフィルタに同じ値があることがわかりました!:)

2-2. 2D Gaussian Filter


1 D Gaussフィルタとあまり変わらない.

point-pread形式という図形は、一番上の点に拡散した形で存在することを意味します.ボリューム化により得られ,配列に値を格納できるため,さらなる処理が必要である.
下記のように数式で表します!
G(x,y)=12πσ2e−x2+y22σ2G(x,y) =\dfrac 1 {{2\pi}\sigma^2} e^{-\frac {x^2+y^2} {2\sigma^2}}G(x,y)=2πσ21​e−2σ2x2+y2​
前に1 D Gaussian Filterと言いましたが、それを使って2 D Gaussian Filterを求めることもできます.簡単に言えば、ベクトルからマトリクスを得る方式を利用する.
TMI:ベクトルと行列
線形代数では、ベクトルは通常縦長形を呈する.これを利用すると、ベクトル計算には2つの選択肢があります!
-横ベクトル*縦ベクトル(スカラー形式)
:これをベクトルの内積といいます
-縦ベクトル*横ベクトル(行列形式)
:これをベクトルの外積と呼びます
1 D Gaussian Filterの使用
width = 11
sigma = 3 
kernel_x = genGausKernel1D(width, sigma) 
kernel_y = kernel_x.T
# .T는 transpose인데, 벡터를 뒤집어서 가로형은 세로로, 세로형은 가로로 표현한다는 뜻
kernel_2d = np.outer(kernel_x, kernel_y)
#outer는 외적이라는 뜻! inner는 내적
新しい関数を作成して2 D Gaussフィルタを作成する
def genGaussianKernel(width, sigma):
    array = np.arange((width//2)*(-1), (width//2)+1)
    #중심에서부터의 거리 계산
    arr = np.zeros((width, width))
    #x^2+y^2 부분을 미리 계산해둘 매트릭스 initialize
    for x in range(width):
        for y in range(width):
            arr[x,y] = array[x]**2+array[y]**2
            #중심에서부터의 거리를 제곱합으로 계산
    kernel_2d = np.zeros((width, width))
    #커널의 값을 저장할 매트릭스 생성
    for x in range(width):
        for y in range(width):
             kernel_2d[x,y] = np.exp(-arr[x,y]/(2*sigma**2))
             #수식에 맞게 값 저장(역시나 상수 부분은 생략)
    kernel_2d /= kernel_2d.sum()
    #전체 값의 합으로 나누어 필터 전체의 합이 1이 되도록 함
    return kernel_2d

生成されたフィルタのチェック


イメージのロード
lena       = cv2.imread('SourceImages/lena.bmp', 0) 
# 0 represents gray-scale 
#(1 for unchanged, -1 for color - default)
lena_noise = cv2.imread('SourceImages/lena_noise.bmp', 0)
上記の関数を使用してカーネル(フィルタ)を作成するには
kernel_1 = genGaussianKernel(5,1) # 5 by 5 kernel with sigma of 1
kernel_2 = genGaussianKernel(11,3)   # 11 by 11 kernel with sigma of 3
画像変換
res_lena_kernel1 = cv2.filter2D(lena, -1, kernel_1) 
# cv2.filter2D(src, ddepth, kernel)
# src : input
# ddepth = -1 : return type same as the source
# kernel (= mask)
res_lena_kernel2 = cv2.filter2D(lena, -1, kernel_2) 
res_lena_noise_kernel1 = cv2.filter2D(lena_noise, -1, kernel_1) 
res_lena_noise_kernel2 = cv2.filter2D(lena_noise, -1, kernel_2) 

カーネルでは、シグマ値とカーネルサイズを変更するにつれて、画像がどのように変化するかを見ることができます.

2つの生成フィルタを比較



私はまた面白いものを試して、2つのフィルターをつなげることができます.
width = 11
sigma = 3 
kernel_x = genGausKernel1D(width, sigma) 
kernel_y = kernel_x.T
kernel_2d = np.outer(kernel_x, kernel_y)
res_lena_noise_kernel1d_x  = cv2.filter2D(lena_noise, -1, kernel_x) 
res_lena_noise_kernel1d_xy = cv2.filter2D(res_lena_noise_kernel1d_x, -1, kernel_y)
#두번 연속해서 커널을 적용하고 싶을때 쓰는 방법!
res_lena_noise_kernel2d    = cv2.filter2D(lena_noise, -1, kernel_2d)
上図には、オリジナル画像、kernel xのみを適用した画像、kernel yを追加した画像、2次元kernelの画像の4つの画像が表示されます.
最後の2つの結果値は近く、グラフでは差が大きくなく、満足しています.
コードそのものの時間の複雑さやちょっとモノトーンな部分が気になりますが、次回は再梱包してみましょう、今日はここまで!
あ、しかも私は1 Dと2 Dフィルターを実現していますので、私が直接実験したわけではありませんが、グーグルで1 Dフィルターを使うときは細かく(もちろん、処理量が増えるにつれて差が大きくなる)、スピードが速いです.これは、個人の符号化スタイルや時間の複雑さによって異なる場合があります.ブログを参照
REF
https://homepages.inf.ed.ac.uk/rbf/HIPR2/gsmooth.htm
https://docs.opencv.org/4.x/
https://toitoitoi79.tistory.com/90(1 Dと2 Dの運転時間比較)