下の画像e-low.bmpはBMP形式の圧縮ファイルをダウンロードして展開して得られる。
このコントラストの低い画像に対して、コントラストを調整
した例を以下に示す。
また、このbmp画像に対して、3章で作成した
ヒストグラムをファイル出力する関数を用いて出力し、
gnuplotでヒストグラムを表示させた。
この画像データでは、濃度値の低い範囲に画素が集中しているのがわかる。
(6.1)式に基づき、画像の明るさをn倍した例を示す。 ただし、255を越えた値は255とする。
上の例だとn=4が見やすいように思われる。
このときのヒストグラムは下のようになり、元画像のヒストグラムよりも範囲が広がっているのがわかる。
(6.2)式に基づいて、濃度値を0〜255まで広げることにより、コントラストを自動調整した例を示す。
また、この画像のヒストグラムを示す。
今回は原画像が256x256 pixel、256階調なので、平坦化後の各レベルの画素数は256になる。
下に例を示す。
また、この画像のヒストグラムを示す。
#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]; }