第6章 見やすい画像を作る

コントラストの調整とその例

下の画像e-low.bmpはBMP形式の圧縮ファイルをダウンロードして展開して得られる。
元画像e-low.bmp
このコントラストの低い画像に対して、コントラストを調整 した例を以下に示す。
また、このbmp画像に対して、3章で作成した ヒストグラムをファイル出力する関数を用いて出力し、 gnuplotでヒストグラムを表示させた。
元画像のヒストグラム
この画像データでは、濃度値の低い範囲に画素が集中しているのがわかる。

コントラストを強調する

(6.1)式に基づき、画像の明るさをn倍した例を示す。 ただし、255を越えた値は255とする。

n = 1, n = 2, n = 3, n = 4

n = 1 n = 2 n = 3 n = 4

n = 5, n = 6, n = 7, n = 8

n = 5 n = 6 n = 7 n = 8


上の例だとn=4が見やすいように思われる。
このときのヒストグラムは下のようになり、元画像のヒストグラムよりも範囲が広がっているのがわかる。
明るさを4倍した画像のヒストグラム

コントラスト強調を自動化する

(6.2)式に基づいて、濃度値を0〜255まで広げることにより、コントラストを自動調整した例を示す。

コントラスト自動調整
また、この画像のヒストグラムを示す。
自動調整した画像のヒストグラム

濃度ヒストグラムを平坦化する

今回は原画像が256x256 pixel、256階調なので、平坦化後の各レベルの画素数は256になる。
下に例を示す。
ヒストグラムを平坦化
また、この画像のヒストグラムを示す。
ヒストグラムを平坦化

プログラミング例

明るさをn倍する

#include "Params.h"

/*--- amplify --- 画像の明るさをn倍する --------------------------------------
    image_in:    入力画像配列
    image_out:    出力画像配列
    n:            倍率
-----------------------------------------------------------------------------*/
void amplify(unsigned char image_in[Y_SIZE][X_SIZE], 
    unsigned char image_out[Y_SIZE][X_SIZE], float n)
{
    int i, j, nf;

    for (i = 0; i < Y_SIZE; i++){
        for (j = 0; j < X_SIZE; j++){
            nf = (int)(image_in[i][j] * n);
            if (nf > 255) nf = 255;
            image_out[i][j] = (unsigned char)nf;
        }
    }
}

明るさの範囲を求める

#include "Params.h"

/*--- range --- 画像の明るさの範囲を求める ------------------------------------
    image_in:    入力画像配列
    fmax:        入力画像の濃度の最大値
    fmin:        入力画像の濃度の最小値
-----------------------------------------------------------------------------*/
void range(unsigned char image_in[Y_SIZE][X_SIZE], int *fmax, int *fmin)
{
    int i, j, nf;

    *fmax = 0;
    *fmin = 255;
    for (i = 0; i < Y_SIZE; i++){
        for (j = 0; j < X_SIZE; j++){
            nf =(int)image_in[i][j];
            if (nf > *fmax) *fmax = nf;
            if (nf < *fmin) *fmin = nf;
        }
    }
}

明るさの範囲を広げる

#include "Params.h"

/*--- expand --- 濃度を0から255の範囲に変換する ---------------------------
    image_in:    入力画像配列
    image_out:    出力画像配列
    fmax:        入力画像の濃度の最大値
    fmin:        入力画像の濃度の最小値
-----------------------------------------------------------------------------*/
void expand(unsigned char image_in[Y_SIZE][X_SIZE], 
    unsigned char image_out[Y_SIZE][X_SIZE], int fmax, int fmin)
{
    int i, j;
    float d;

    for (i = 0; i < Y_SIZE; i++) {
        for (j = 0; j < X_SIZE; j++) {
            d = (float)255 / (float)(fmax - fmin) 
                * ((int)image_in[i][j] - fmin);
            if (d > 255) image_out[i][j] = 255;
            else if (d < 0) image_out[i][j] = 0;
            else            image_out[i][j] = (unsigned char)d;
        }
    }
}

ヒストグラムを平坦化する

本書では、画像のサイズが縦横 128 pixelであるが標準画像のサイズは 縦横が倍の 256 pixelなので、pixel数は4倍になるため、 平坦化に使用するバッファのサイズ(BUFF_MAX)を4倍にした

#include "Params.h"

#define BUFF_MAX 10000/* ヒストグラム平坦化に使用するバッファの大きさ */

struct xyw {
    int  x, y, w;        /* 画素の位置(x,y)と周辺画素の濃度の和(weight) */
} buf[BUFF_MAX];        /* バッファ配列 */

void sort(unsigned char image_in[Y_SIZE][X_SIZE], struct xyw data[],
    int level);
void weight(unsigned char image_in[Y_SIZE][X_SIZE], int i, int j,int *wt);

/*--- plane --- 濃度ヒストグラムを平坦化する ----------------------------------
    image_in:    入力画像配列
    image_out:    出力画像配列
    image_buf:    ワーク画像配列
    hist:        濃度ヒストグラム配列
---------------------------------- ------------------------------------------*/
void plane(unsigned char image_in[Y_SIZE][X_SIZE], 
    unsigned char image_out[Y_SIZE][X_SIZE], 
    unsigned char image_buf[Y_SIZE][X_SIZE], long hist[256])
{
    int i, j, iy, jx, sum;
    int delt;              /* 周辺画素レベルにより選ばれる画素数 */
    int low, high;         /* 処理レベルの範囲 */
    int av;                /* 平坦化後の1濃度レベルの画素数 */

    av = (int)((Y_SIZE) * (X_SIZE) / 256);
    high = 255;
    low = 255;
    for (i = 0; i < Y_SIZE; i++){
        for (j = 0; j < X_SIZE; j++){
            image_out[i][j] = 0;
            image_buf[i][j] = image_in[i][j];
        }
    }

    for (i = 255; i > 0; i--){
        for (sum = 0; sum < av; low--) sum = sum + hist[low];
        low++;
        delt = hist[low] - (sum - av);
        sort(image_buf, buf, low);
        if (low < high){
            for (iy = 0; iy < Y_SIZE; iy++){
                for (jx = 0; jx < X_SIZE; jx++){
                    if (((int)image_buf[iy][jx] >= low + 1 ) &&
                        ((int)image_buf[iy][jx] <= high))
                    image_out[iy][jx] = (unsigned char)i;
                }
            }
        }
        for (j = 0; j < delt; j++){
            image_out[buf[j].y][buf[j].x] =(unsigned char)i;
            image_buf[buf[j].y][buf[j].x] =(unsigned char)0;
        }
        hist[low] = hist[low] - delt;
        high = low;
    }
}

/*--- sort --- 周りの画素の濃度の高い順に並び変える ---------------------------
    image_in:    入力画像配列
    data:        位置及び周辺画素の濃度和の配列
    level:        並び変える画素の濃度
-----------------------------------------------------------------------------*/
void sort(unsigned char image_in[Y_SIZE][X_SIZE], struct xyw data[], int level)
{
    int i, j, inum, wt;
    struct xyw temp;

    inum = 0;
    for (i = 0; i < Y_SIZE; i++ ){
        for (j = 0; j < X_SIZE; j++ ){
            if ((int)image_in[i][j] == level){
                weight(image_in, i, j, &wt);    /* 周辺画素の濃度の和を計算 */
                data[inum].y = i;
                data[inum].x = j;
                data[inum].w = wt;
                inum++;
                }
            }
        }

    for (i = 0; i < inum - 1; i++ ){         /* 並び変え */
        for (j = i + 1; j < inum; j++){
            if (data[i].w <= data[j].w){
                temp.y = data[i].y;
                temp.x = data[i].x;
                temp.w = data[i].w;
                data[i].y = data[j].y;
                data[i].x = data[j].x;
                data[i].w = data[j].w;
                data[j].y = temp.y;
                data[j].x = temp.x;
                data[j].w = temp.w;
                }
            }
        }
}

/*--- weight --- 周辺画素の濃度の和を計算する ---------------------------------
    image_in:    入力画像配列
    i, j:        画素位置
    wt:            濃度和
-----------------------------------------------------------------------------*/
void weight(unsigned char image_in[Y_SIZE][X_SIZE], int i, int j, int *wt)
{
    int dim, djm;
    int dip, djp;
    int k, d[8];

    dim = i - 1;
    djm = j - 1;
    dip = i + 1;
    djp = j + 1;
    if (dim < 0) dim = i;
    if (djm < 0) djm = j;
    if (dip > Y_SIZE-1) dip = i;
    if (djp > X_SIZE-1) djp = j;

    d[0] = (int)image_in[dim][djm];
    d[1] = (int)image_in[dim][j];
    d[2] = (int)image_in[dim][djp];
    d[3] = (int)image_in[i][djm];
    d[4] = (int)image_in[i][djp];
    d[5] = (int)image_in[dip][djm];
    d[6] = (int)image_in[dip][j];
    d[7] = (int)image_in[dip][djp];
    for (k = 0; k < 8; k++) *wt = *wt + d[i];
}