第5章 雑音を除く

雑音除去法とその例

Lennaに胡麻塩雑音を追加
この画像のBMP形式の圧縮ファイルをダウンロードして展開すると、noise.bmpが得られる。

この画像に対して、以下に雑音除去例を示す。

移動平均法

以下に移動平均法によるノイズ除去例を示す。
ここで、テキストに明記されていないが、平均した値は原画像とは別に設けた二次元配列に 格納する。もし、平均値を原画像に直接格納すると、おかしな結果を得る。

移動平均1回
移動平均法1回の例。

移動平均5回
移動平均法5回の例。

移動平均法は注目画素の注目画素と8近傍の平均値を画素の値とすることにより、 ぼかす処理である。
回数を増すほどに、雑音は見えなくなるが、同時に画像がぼけてしまっているのがわかる。

メディアンフィルタ

統計におけるメディアンとは、データを小さい順に並べたとき、 その順の真ん中の値を採用する考え方である。今の場合、3x3画素であるから 9列の真ん中、すなわち5番目に小さい値を採用することになる。 これを用いたメディアンフィルタによるノイズ除去例を示す。

メディアンフィルタ1回
メディアンフィルタ1回の例。

メディアンフィルタ5回
メディアンフィルタ5回の例。

移動平均法と比較して、雑音の値は選択されにくいので、 移動平均より優れた結果が得られた。

2値画像の雑音除去法とその例

3章で用いた2値画像に対して 雑音を追加したものを下の画像に示す。
この画像のBMP形式をダウンロード

閾値85のものにノイズを5%
この画像に対して膨張、収縮処理を施した例を以下に示す。

膨張・収縮処理

膨張、収縮処理例を示す。 左が膨張、右が収縮処理を施した例である。

膨張 収縮
膨張処理は、注目画素の周囲8近傍に一つでも1(白)があったら、その画素を 1にする処理である。例では、文字の白い領域が広ければ、その中の雑音は無くなる。 しかし、文字領域以外の白領域も広がる。
収縮はその逆で、一つでも0(黒)があったら、 その画素を0にする処理である。その効用は、膨張処理と逆である。

膨張収縮処理例

膨張収縮
膨張したことにより、文字中の雑音が消えたのがわかる。 しかし、周囲の雑音は除去できず、面積が広がったものもある。

収縮膨張処理例

収縮膨張
収縮したことにより、背景の雑音が消えたのがわかる。 しかし、文字中の雑音は除去できない。

プログラミング例

移動平均法

#include    "Params.h"

/*--- smooth --- 移動平均法によるノイズ除去 -----------------------------------
    image_in:    入力画像配列
    image_out:    出力画像配列
-----------------------------------------------------------------------------*/
void smooth(unsigned char image_in[Y_SIZE][X_SIZE], 
    unsigned char image_out[Y_SIZE][X_SIZE])
{
    int i, j, buf;
                    
    for (i = 1; i < Y_SIZE-1; i++) {
        for (j = 1; j < X_SIZE-1; j++) {
            buf = (int)image_in[i-1][j-1]
                + (int)image_in[i-1][j]
                + (int)image_in[i-1][j+1]
                + (int)image_in[i][j-1]
                + (int)image_in[i][j]
                + (int)image_in[i][j+1]
                + (int)image_in[i+1][j-1]
                + (int)image_in[i+1][j]
                + (int)image_in[i+1][j+1];
            image_out[i][j] = buf / 9;
        }
    }
}

メディアンフィルタ

#include    "Params.h"

int median_value(unsigned char c[9]);

/*--- median --- メディアンフィルタによるノイズ除去 ---------------------------
    image_in:    入力画像配列
    image_out:    出力画像配列
-----------------------------------------------------------------------------*/
void median(unsigned char image_in[Y_SIZE][X_SIZE], 
    unsigned char image_out[Y_SIZE][X_SIZE])
{
    int             i, j;
    unsigned char   c[9];

    for (i = 1; i < Y_SIZE-1; i++) {
        for (j = 1; j < X_SIZE-1; j++) {
            c[0] = image_in[i-1][j-1];
            c[1] = image_in[i-1][j];
            c[2] = image_in[i-1][j+1];
            c[3] = image_in[i][j-1];
            c[4] = image_in[i][j];
            c[5] = image_in[i][j+1];
            c[6] = image_in[i+1][j-1];
            c[7] = image_in[i+1][j];
            c[8] = image_in[i+1][j+1];
            image_out[i][j] = median_value(c);
        }
    }
}

/*--- median_value --- 9つの画素の中央値(メディアン)を求める ---------------
    c:    画素
-----------------------------------------------------------------------------*/
int median_value(unsigned char c[9])
{
    int     i, j, buf;
    
    for (j = 0; j < 8; j++) {
        for (i = 0; i < 8; i++) {
            if (c[i+1] < c[i]) {
                buf = c[i+1];
                c[i+1] = c[i];
                c[i] = buf;
            }
        }
    }
    return c[4];
}

収縮

#include    "Params.h"

/*--- erosion --- 収縮処理 ----------------------------------------------------
    image_in:    入力画像配列
    image_out:    出力画像配列
-----------------------------------------------------------------------------*/
void erosion(unsigned char image_in[Y_SIZE][X_SIZE], 
    unsigned char image_out[Y_SIZE][X_SIZE])
{
    int    i, j;

    for (i = 1; i < Y_SIZE-1; i++) {
        for (j = 1; j < X_SIZE-1; j++) {
            image_out[i][j] = image_in[i][j];
            if (image_in[i-1][j-1] == LOW) image_out[i][j] = LOW;
            if (image_in[i-1][j  ] == LOW) image_out[i][j] = LOW;
            if (image_in[i-1][j+1] == LOW) image_out[i][j] = LOW;
            if (image_in[i  ][j-1] == LOW) image_out[i][j] = LOW;
            if (image_in[i  ][j+1] == LOW) image_out[i][j] = LOW;
            if (image_in[i+1][j-1] == LOW) image_out[i][j] = LOW;
            if (image_in[i+1][j  ] == LOW) image_out[i][j] = LOW;
            if (image_in[i+1][j+1] == LOW) image_out[i][j] = LOW;
        }
    }
}

膨張

#include    "Params.h"

/*--- dilation --- 膨張処理 ---------------------------------------------------
    image_in:    入力画像配列
    image_out:    出力画像配列
-----------------------------------------------------------------------------*/
void dilation(unsigned char image_in[Y_SIZE][X_SIZE], 
    unsigned char image_out[Y_SIZE][X_SIZE])
{
    int    i, j;

    for (i = 1; i < Y_SIZE-1; i++) {
        for (j = 1; j < X_SIZE-1; j++) {
            image_out[i][j] = image_in[i][j];
            if (image_in[i-1][j-1] == HIGH) image_out[i][j] = HIGH;
            if (image_in[i-1][j  ] == HIGH) image_out[i][j] = HIGH;
            if (image_in[i-1][j+1] == HIGH) image_out[i][j] = HIGH;
            if (image_in[i  ][j-1] == HIGH) image_out[i][j] = HIGH;
            if (image_in[i  ][j+1] == HIGH) image_out[i][j] = HIGH;
            if (image_in[i+1][j-1] == HIGH) image_out[i][j] = HIGH;
            if (image_in[i+1][j  ] == HIGH) image_out[i][j] = HIGH;
            if (image_in[i+1][j+1] == HIGH) image_out[i][j] = HIGH;
      }
    }
}