script day.log

大学生がなんとなく始めた、趣味やら生活のことを記録していく。

2値化手法について(モード法~閾値の自動決定)

濃淡の表現できない媒体に画像を表示するときや,画像の特徴を解析するときなどには,
濃淡画像を2値(白・黒)の濃度値を持つ画像に変換する.M \times N画素で構成される画像の2値化は
次式の閾値処理によって行われる.

\begin{align}
 g(m,n)=\left\{
\begin{array}{l}
 255 \qquad f(m,n)\geq\theta \\
 0 \qquad\quad f(m,n)<\theta
\end{array}
\right.
\end{align}

ただし,f(m,n),g(m,n)はそれぞれ入力画像データ,作成される2値画像データとする.
ここで閾値\thetaをいかに決めるかが問題となり,その代表的な手法としてモード法がある.

モード法の考え方

与えられた画像の濃度ヒストグラムが2つのピークを持つ場合,
この2つの山の間の谷のところに閾値\thetaを決めればよい.
しかし、閾値を決めるには、濃度ヒストグラムを確認して,谷を見つける必要がある.

閾値の自動決定

Pさん、谷を見つければ良いんですよ!
今回私が谷を見つけるために使用したのは極値です.
最急降下法を実装するのは面倒だったので…
簡単に言うと、極大値が濃度ヒストグラムの山にあたり、
極小値が濃度ヒストグラムの谷に当たるわけですね…
そして極大値と極小値の差を求め、ある程度の高さがあれば、
その時の極小値を谷と考え,閾値に設定するという考え方です.
以下が私が作成したCプログラムです.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define HEIGHT 256
#define WIDTH 256

int maxNo(int left, int right, int data[])
{
    int maxNum = left;
    int max = data[left];
    for (int i = left; i < left + right && i < 256; i++)
    {
        if (data[i] > max)
        {
            max = data[i];
            maxNum = i;
        }
    }
    return maxNum;
}

int minNo(int left, int right, int data[])
{
    int minNum = left;
    int min = data[left];
    for (int i = left; i < right; i++)
    {
        if (min > data[i])
        {
            min = data[i];
            minNum = i;
        }
    }
    return minNum;
}

int main(int argc, char *argv[])
{
    unsigned char image[HEIGHT][WIDTH];
    unsigned char image_w[HEIGHT][WIDTH];
    int histgram[256] = {0};
    int mode;
    FILE *fp;
    int maxRNo[256];
    int minRNo[256];
    int subRNo[256];
    int range = 16;
    int thr = 150;

    if (argc < 2)
    {
        printf("don't read filename\n");
        return 0;
    }
    else if (argc > 2)
    {
        printf("many arguments\n");
        return 0;
    }
    else
    {
        fp = fopen(argv[1], "rb");
        fread(image, sizeof(unsigned char), HEIGHT * WIDTH, fp);
        fclose(fp);

        for (int i = 0; i < HEIGHT; i++)
        {
            for (int j = 0; j < WIDTH; j++)
            {
                histgram[image[i][j]]++;
            }
        }

        int cnt = 0;
        for (int i = 0; i < 256 - range; i++)
        {
            if (maxNo(i, range, histgram) == maxNo(i + 1, range, histgram))
            {
                int check = 0;
                for (int j = 1; j < range; j++)
                {
                    if (maxNo(i, range, histgram) == maxNo(i + j, range, histgram))
                    {
                        check++;
                    }
                }
                if (check == range - 1)
                {
                    maxRNo[cnt] = maxNo(i, range, histgram);
                    cnt++;
                }
            }
        }

        minRNo[0] = minNo(0, maxRNo[0], histgram);
        for (int i = 0; i < 256; i++)
        {
            if (maxRNo[i + 1] == 0)
            {
                break;
            }
            minRNo[i + 1] = minNo(maxRNo[i], maxRNo[i + 1], histgram);
        }

        int subCnt = 0;
        for (int i = 0; i < 256; i++)
        {
            if (histgram[maxRNo[i]] - histgram[minRNo[i]] >= thr)
            {
                subRNo[subCnt] = minRNo[i];
                subCnt++;
            }
        }

        if (subRNo[0] > 0)
        {
            if (subRNo[1] > 0)
            {
                printf("many peaks\n");
            }
            else
            {
                mode = subRNo[0];
                printf("閾値 = %d\n", mode);
            }
        }
        else
        {
            printf("don't appear peak\n");
            return 0;
        }

        for (int i = 0; i < HEIGHT; i++)
        {
            for (int j = 0; j < WIDTH; j++)
            {
                if (image[i][j] > mode)
                {
                    image_w[i][j] = 255;
                }
                else
                {
                    image_w[i][j] = 0;
                }
            }
        }


        return 0;
    }
}

次は判別分析法かP-タイル法を書くつもり…