知っていることだけ

勉強していて役に立つ、理解の助けになるようなポイントを書いていきます。

画像処理 移動平均法とメディアンフィルタの

このページは以下の読者を対象にしています

実験方法

  1. 画像を用意する。
  2. 粒状雑音を付加する。
  3. フィルタサイズを3×3,5×5,7×7,9×9に変えながら移動平均法とメディアンフィルタをかける
  4. 結果を画像ファイルとして出力する。

使用プログラム

visual studio 2017. opencv 4.10で動作確認済み

#include <opencv2/opencv.hpp>
#include <string> 
#include <iostream>
#include <random>
using namespace cv;
//粒状雑音用
std::mt19937 mt{ std::random_device{}() };

//粒状雑音作成
Mat AddNoise(const Mat & img, float ratio) {
    //乱数生成
    std::uniform_int_distribution<int> randrow(0, img.rows - 1);
    std::uniform_int_distribution<int> randcol(0, img.cols - 1);

    //点の数を計算
    int count = (int)(img.rows * img.cols * ratio);
    Mat ret = img.clone();
    //指定場所にノイズを加える
    for (int i = 0; i < count; i++) {
        int x = randcol(mt);
        int y = randrow(mt);
        ret.at<cv::Vec3b>(y, x) = cv::Vec3b(255, 255, 255);
    }
    return ret;
}


class Imgs {
public:
    static std::string  filepath;
    Imgs(std::string name, Mat img) :
        name(name), img(img) {};
    Imgs() {};
    Mat img;
    std::string name;
    void ImOutPut() {
        //imshow(name, img);//ウィンドウに表示する
        imwrite(filepath + name + ".png", img);//画像を出力
    }

};

class Filters {
public:
    static Imgs origin;
    static Imgs target;
    static int kernelsize;

    Imgs filteredimg;
    Imgs abs;
    //元画像との差分を取得
    void SetAbs() {
        abs.name = "abs_" + filteredimg.name;
        absdiff(origin.img, filteredimg.img, abs.img);//元画像との差分を取得
    };
    //フィルター後と差分画像を出力
    void ImOutPut() {
        filteredimg.ImOutPut();
        abs.ImOutPut();
    }

};
//メディアンフィルタ
class MedianImg : public Filters {
public:
    MedianImg() {
        filteredimg.name = "med_" + std::to_string(kernelsize);
        medianBlur(target.img, filteredimg.img, kernelsize);    //メディアンフィルタ
        SetAbs();   //元画像との差分を取得
    }
};
// 移動平均法
class MoveAveImg :public Filters {
public:
    MoveAveImg() {
        filteredimg.name = "ave_" + std::to_string(kernelsize);
        blur(target.img, filteredimg.img, Size(kernelsize, kernelsize));        // 移動平均法
        SetAbs();   //元画像との差分を取得
    }
};

Imgs Filters::origin;
Imgs Filters::target;
int Filters::kernelsize;
std::string Imgs::filepath;//データの保管場所path.ここを変えて複数の画像を試す
std::string files[3] = { "./data1/", "./data2/", "./data3/" };

int main(int argc, char *argv[])
{
    for (int i = 0; i < 3; i++) {
        Imgs::filepath = files[i];
        //元画像取得
        Filters::origin = Imgs("origin", imread(Imgs::filepath + "img.jpg"));
        Mat origin_img = Filters::origin.img;
        //縦横比は維持するように画像を縮小
        const float kSize = 125;
        float targetsize = kSize/ origin_img.cols;
        resize(origin_img, Filters::origin.img, Size(), targetsize, targetsize);

        //ノイズ付加
        Filters::target = Imgs("target", AddNoise(Filters::origin.img, 0.05f));
        for (int i = 3; i < 10; i += 2) {
            Filters::kernelsize = i;//カーネルサイズを変えてみる
            // 移動平均法
            MoveAveImg moveave;
            //メディアンフィルタ
            MedianImg median;
            //結果表示
            Filters::origin.ImOutPut();
            Filters::target.ImOutPut();
            moveave.ImOutPut();
            median.ImOutPut();
        }
    }
    return 0;
}

結果

f:id:protoidea:20190623175145p:plain
移動平均法とメディアンフィルタの比較

移動平均法とメディアンフィルタの違い

結論

粒状雑音除去には、メディアンフィルタの方がよい
雑音の大きさに応じてカーネルサイズを変えるべき