【C#】メディアンフィルタで画像のぼかし(ノイズ除去)

C#とメディアンフィルタを用いて、画像からゴマ塩ノイズを除去する方法について紹介します。

メディアンフィルタで輪郭検出(C#)

メディアン(中央値)とは、データを小さい順に並べたとき、その順の真ん中の値を採用する統計量のことです。
これを画像処理に利用したのがメディアンフィルタです。
メディアンフィルタで処理を行うことで、画像内の極端な画素(周囲と大きく異なる画素)が取り除かれ、ゴマ塩ノイズを除去することができます。

原理についてはこちら

【画像処理】メディアンフィルタの原理・特徴・計算式
画像処理におけるメディアンフィルタの原理や計算式についてまとめました。

今回はこれをC#で実装してみました。

ソースコード

プログラムのソースコードです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace MedianFilter
{
    class Program
    {
        static void Main(string[] args)
        {
            // 画像の読み込み(グレースケールに変換)
            byte[,] img = LoadImageGray("src.jpg");
            // フィルタ用のマスク
            const int kernelSize = 3;
            double[,] kernel = new double[kernelSize, kernelSize]{
                                {1.0/9.0,1.0/9.0,1.0/9.0},
                                {1.0/9.0,1.0/9.0,1.0/9.0},
                                {1.0/9.0,1.0/9.0,1.0/9.0}};

            // フィルタ処理
            byte[,] img2 = Filter(img, kernel);
            // 画像保存
            SaveImage(img2, "dst.jpg");
        }

        // 画像をグレースケール変換して読み込み
        static byte[,] LoadImageGray(string filename)
        {
            Bitmap img = new Bitmap(filename);
            int w = img.Width;
            int h = img.Height;
            byte[,] dst = new byte[w, h];

            // bitmapクラスの画像ピクセル値を配列に挿入
            for (int i = 0; i < h; i++)
            {
                for (int j = 0; j < w; j++)
                {
                    // グレイスケールに変換
                    dst[j, i] = (byte)((img.GetPixel(j, i).R + img.GetPixel(j, i).B + img.GetPixel(j, i).G) / 3);
                }
            }
            return dst;
        }

        static void SaveImage(byte[,] src, string filename)
        {
            // 画像データの幅と高さを取得
            int w = src.GetLength(0);
            int h = src.GetLength(1);
            Bitmap img = new Bitmap(w, h);
            // ピクセル値のセット
            for (int i = 0; i < h; i++)
            {
                for (int j = 0; j < w; j++)
                {
                    img.SetPixel(j, i, Color.FromArgb(src[j, i], src[j, i], src[j, i]));
                }
            }

            // 画像の保存
            img.Save(filename);
        }
        static byte[,] Filter(byte[,] src, double[,] kernel)
        {
            // 縦横サイズを配列から読み取り
            int w = src.GetLength(0);
            int h = src.GetLength(1);
            // マスクサイズの取得
            int kernelSize = kernel.GetLength(0);
            // 出力画像用の配列
            byte[,] dst = new byte[w, h];

            // 画像処理
            for (int i = 0; i < h; i++)
            {
                for (int j = 0; j < w; j++)
                {
                    double sum = 0;
                    for (int k = -kernelSize / 2; k <= kernelSize / 2; k++)
                    {
                        for (int n = -kernelSize / 2; n <= kernelSize / 2; n++)
                        {
                            if (j + n >= 0 && j + n < w && i + k >= 0 && i + k < h)
                            {
                                sum += src[j + n, i + k] * kernel[n + kernelSize / 2, k + kernelSize / 2];
                            }
                        }
                    }

                    dst[j, i] = DoubleToByte(sum);
                }
            }
            return dst;
        }

        // double型をbyte型に変換
        static byte DoubleToByte(double num)
        {
            if (num > 255.0) return 255;
            else if (num < 0) return 0;
            else return (byte)num;
        }

    }
}

実行結果

プログラムの実行結果です。
元画像(左)とフィルタをかけた画像(右)
12

- 関連ページ
1 C#で画像処理入門
2 C#入門】基礎文法とサンプル集

コメント

  1. 匿名 より:

    ソースコードが平均値フィルタと同じではありませんか?