[w6d1] Filters


(Ubuntu 18.04.6 LTS)
2022.03.21.
C++、VSコードを使用
プログラマー自主走行Defcos 3期

Mean filter


画像上の一点値を周囲画素の算術平均値と平滑化することでノイズの影響を低減する.
https://en.wikipedia.org/wiki/Geometric_mean_filter#/media/File:Gmf.jpg
OpenCVが提供するmean filterはblur形式で提供される.
void cv::blur 	( 	InputArray  	src,
		OutputArray  	dst,
		Size  	ksize,
		Point  	anchor = Point(-1,-1),
		int  	borderType = BORDER_DEFAULT 
	) 		
https://docs.opencv.org/4.x/d4/d86/group__imgproc__filter.html#ga8c45db9afe636703801b0b2e440fce37

Sizeはカーネルのサイズであり、cv::Size(n,n)をパラメータとするとnxnカーネルが適用されます.
#include <iostream>
#include "opencv2/opencv.hpp"

int main()
{
    cv::Mat src = cv::imread("./resources/lenna.bmp",cv::IMREAD_COLOR);

    if (src.empty()){
        std::cerr << "Image load failed!" << std::endl;
        return -1;
    }

    cv::Mat dst;
    cv::Mat Planes[5];
    for (int i=0;i<5;i++){
        cv::String str = cv::format("Mean filter : %d x %d",2*i+3,2*i+3);
        cv::String title = cv::format("Mean filter : %d x %d",2*i+3,2*i+3);
        cv::blur(src,dst,cv::Size(2*i+3,2*i+3));
        Planes[i]=dst.clone();
        cv::putText(Planes[i],str,cv::Point(0,30),cv::FONT_HERSHEY_DUPLEX,0.7,cv::Scalar(255,0,0),1.7);
        cv::imshow(title,Planes[i]);
    }

    cv::imshow("src",src);
    while (cv::waitKey()!=27) {
        continue;
    }
    cv::destroyAllWindows();
    return 0;
}
Mean filterカーネルサイズの効果は上記コードの結果で確認できます.カーネルのサイズが大きいほどぼやけているのがわかります.

2 D filterの演算量はやや大きく,Separable filterであれば2つの1 D filterに分けて計算することができ,時間的複雑度が大幅に低下する.N×Mサイズの画像では、n×mサイズのフィルタを用いて演算する場合、2次元フィルタはO(M・N・m・n)であり、2回の1次元フィルタの適用では、O(M・n・(m+n)の時間的複雑さを有する.

上記の例では、左側の1 Dフィルタを使用する場合、各フィルタに対して乗算を3回、加算を2回、合計6回行う必要があり、加算4回に比べて右側が9回、加算を8回行う.
https://en.wikipedia.org/wiki/Separable_filter

Gaussain filter


平均フィルタリングに基づくチーク処理は、距離の大きさにかかわらず、適用されるフィルタは同じ重み値を有するため、遠い画素に大きな影響を及ぼす.ガウスフィルタは、遠隔画素の影響を低減するために使用することができる.
void cv::GaussianBlur 	( 	InputArray  	src,
		OutputArray  	dst,
		Size  	ksize,
		double  	sigmaX,
		double  	sigmaY = 0,
		int  	borderType = BORDER_DEFAULT 
	) 		
ガウスフィルタは、標準正規分布に基づいて作成され、ピクセル当たり1σで作った.ksizeをcv::size()に設定すると、後のsigma値に基づいて作成されます.画像が階調画像であれば6σ+1、カラー画像は8σ+1の大きさで作ります.sigmaYをデフォルト値0に設定すると、sigmaXと同じ値になります.
#include <iostream>
#include "opencv2/opencv.hpp"

int main()
{
    cv::Mat src = cv::imread("./resources/lenna.bmp",cv::IMREAD_COLOR);

    if (src.empty()){
        std::cerr << "Image load failed!" << std::endl;
        return -1;
    }

    cv::Mat dst;
    cv::Mat Planes[5];
    for (int i=0;i<5;i++){
        cv::String str = cv::format("Gaussian filter : %d",i+1);
        cv::String title = cv::format("Gaussian filter : %d",i+1);
        cv::GaussianBlur(src,dst,cv::Size(),i+1);
        Planes[i]=dst.clone();
        cv::putText(Planes[i],str,cv::Point(0,30),cv::FONT_HERSHEY_DUPLEX,0.7,cv::Scalar(255,0,0),1.7);
        cv::imshow(title,Planes[i]);
    }

    cv::imshow("src",src);
    while (cv::waitKey()!=27) {
        continue;
    }
    cv::destroyAllWindows();
    return 0;
    return 0;
}

GaussianBlurのInputArrayとOutputArrayはデータ深度が同じであるため、小数点を利用するためにはsrcのデータ型をCV 32 Fとして指定する必要がある.
Mean filterやGaussian filterに比べて、Mean filterはカーネルのサイズを大きくしても、細かい直線画像でしか厚くなりません.自然なぼかし処理はより多くの演算量を必要とするが,Gaussフィルタを用いることが望ましい.Gaussian filterもMean filterと同様に1 D Gaussian filterを2回に分けて使用する.

Median filter


Median filterもノイズを除去するために使用されるフィルタであり、フィルタ内の値に中間値を使用する方式である.Salt and pepperノイズはセンサ発熱や電気信号などの強い刺激信号によるノイズであり,明るい値や暗い値を有し,中値フィルタリングにより効果的に除去できる.
https://en.wikipedia.org/wiki/Salt-and-pepper_noise
void cv::medianBlur 	( 	InputArray  	src,
		OutputArray  	dst,
		int  	ksize 
	) 		
ksizeで、カーネルサイズをint typeと入力します.
#include <iostream>
#include "opencv2/opencv.hpp"

int main()
{
    cv::Mat src = cv::imread("./resources/Noise_salt_and_pepper.png",cv::IMREAD_GRAYSCALE);

    if (src.empty()){
        std::cerr << "Image load failed!" << std::endl;
        return -1;
    }

    cv::Mat dst;
    cv::medianBlur(src,dst,3);
    cv::imshow("src",src);
    cv::imshow("dst",dst);
    while (cv::waitKey()!=27){
        continue;
    }
    cv::destroyAllWindows();
    return 0;
}

Unsharp masking


Unshap maskは、ぼかし処理されたUnshap maskを元のビデオに逆回転、スケーリングして追加する方式で、エッジ部分が鋭くなるという特徴があります.
https://en.wikipedia.org/wiki/Unsharp_masking
#include <iostream>
#include "opencv2/opencv.hpp"

int main()
{
    cv::Mat src = cv::imread("./resources/lenna.bmp",cv::IMREAD_COLOR);
    cv::Mat blr, sr_blr;
    cv::blur(src,blr,cv::Size(5,5));
    sr_blr=src-blr;
    
    cv::imshow("src",src);
    cv::imshow("blur",blr);
    cv::imshow("src-blur",sr_blr);
    cv::imshow("src+(src-blur)",src+sr_blr);
    cv::imshow("src+2*(src-blur)",src+2*sr_blr);
    while (cv::waitKey()!=27) continue;
    cv::destroyAllWindows();
    return 0;
}
和弦の様子は以下の通りです.

srcでぼかし処理を行う場合,輝度変化が大きい部分はエッジと値の差が大きいエッジなどの高周波領域である.したがって,srcでblurを削除すると,src−burrウィンドウでその領域のみが残っていることを確認できる.src-blurを適切にスケールすると、これらの領域の強化効果が見られます.

Bilateral filter


従来のガウスフィルターなどを使えば、輝度領域が変化するエッジインタフェースの分割がぼやけて見える.エッジインタフェースを保持しながらノイズを除去する方法として、以下に示すように双方向フィルタがある.
https://en.wikipedia.org/wiki/Bilateral_filter

従来のGaussフィルタでは輝度差の正規分布基準が増加し,輝度差が大きい場合,この部分のフィルタ値はゼロに近づき,この問題を解決した.
void cv::bilateralFilter 	( 	InputArray  	src,
		OutputArray  	dst,
		int  	d,
		double  	sigmaColor,
		double  	sigmaSpace,
		int  	borderType = BORDER_DEFAULT 
	) 	
dはフィルタリング用の画素の直径であり、入力−1はsigmaSpace値に基づいて自動的に決定される.Sigma Colorは輝度領域で標準偏差を決定し,3 sigmaが約99.7%であることを考慮し,輝度差が入力値の3倍以上であればほとんど影響しなかった.sigmaSpaceはGaussianフィルタと同じです.
#include <iostream>
#include "opencv2/opencv.hpp"

int main()
{
    cv::Mat src = cv::imread("./resources/lenna.bmp",cv::IMREAD_GRAYSCALE);

    if (src.empty()){
        std::cerr << "Image load failed!" << std::endl;
        return -1;
    }

    cv::Mat bilateral,Gaussian;
    cv::bilateralFilter(src,bilateral,-1,20,5);
    cv::GaussianBlur(src,Gaussian,cv::Size(),5);
    cv::imshow("bilateral",bilateral);
    cv::imshow("Gaussian",Gaussian);
    cv::imshow("src",src);
    while (cv::waitKey()!=27) continue;
    cv::destroyAllWindows();
    return 0;
}

適用結果から,エッジ部分はGaussianと比較して良好に維持され,同時にぼかし処理も良好であった.