第2章 写真のデータを読み込もう

73回生 2lu3

2.1 はじめに

C言語を使って、BitMapという画像ファイルを読み込みます。

BitMapというのは、pngやjpeg,gifなどと同じようなものです。それぞれの違いは、どのようにしてデータを保存するかです。例えば、BitMapはドット(点)ずつに色のデータが集まっています。

C言語とは

プログラミングをするための方法の一つです。パソコンの細かい動作までプログラミングをすることができますが、何かをするのにたくさん書かないといけないので敬遠されることもあります。詳しくはWiki検索をおねがいします。

2.2 どのようにデータが保存されているか

パソコンのデータの全ては、0と1で表現されているのはご存知でしょうか?ですが、001001010110なんていうデータは人間には非常にわかりにくいです。

そこで、いくつかの数字を塊として考えるようにします。具体的には、8個をひとかたまりにした1byte(バイト)というものが使われています。8個の桁それぞれで0か1を選ぶことができるので、2の8乗個、つまり256通りものパターンを表すことができます。さて、下の図は1byteずつBitMapの画像ファイルのデータを前から順番に読んでいったときの図です。ちなみに、2進数での1桁をビット(bit)といいます。つまり、1byteは8bitです。

この図の読み方は、下の図のとおりです。

さて、最初に[42]があり、次に[4D]があります。この[4D]は何でしょうか?正解は、 16進数で表した数 です。今年は2018年ですが、この2018という数字は10進法で表されています。10貯まるごとに、1つ位を上げるから10進数というわけです。じゃあ、16進数はというと、16貯まるごとに1つ位を上げます。しかし、10という数字を1桁で表すことはできません。そこで、0~9に加えてA~Fのアルファベットを使って16個を表します。

さて、16進数のままだとわかりにくいので、文字or10進数に直した図を下に載せました。

2つの画像の色は対応しています。これから、それぞれについて解説をしていきます。

ファイルタイプ

BitMapなので「BM」というわけです。

ファイルサイズ

言葉の通り、ファイルの大きさをbyteで表します。このBitMap画像の場合、72,480,822byte、つまり約72Mbyteになります。

予約領域1・2

おまじないのようなものです。

画像データまでの距離

ファイル部分と情報部分のバイト数を全て足してみて下さい。54になります。要するに、一番最初から画像データ部分まで何バイトあるかという話です。

情報部分のサイズ

情報部分のバイト数をすべて足すと、40になりますね。

画像の幅

ピクセルというのはご存知でしょうか?簡単にいえば、1ピクセルに1つ色があって、ピクセルが集まることによって画像になります。

近くからみた蟻の列は途切れているように見えても、遠くから見た蟻の列は一本の線に見えます。つまり、近くから見たらてんでばらばらな色があるようにしか見えなくても、遠くからみたら一枚の写真に見えます。

さて、話が脱線しましたが、画像の横方向にピクセルがいくつあるのかがここに入っています。

画像の高さ

縦方向にピクセルがいくつあるのかがここに入っています。

プレーン数

これは、よくわかりませんが

現在では使われなくなった概念です by Wiki(意訳)

とのことなので、気にする必要はありません。ちなみに、常に1です。

1画素あたりの色数

正確には、色ビット数と呼びます。24のときは、1677万色が使えるそうです。正直、全部使うことはないでしょう。

圧縮形式

どのようにして圧縮をするかです。

画像データのサイズ

画像データ部分のデータの大きさです。画像データのサイズと画像データまでの距離を足すと、ファイルサイズになることをお確かめ下さい。

水平解像度・垂直解像度

水平解像度(すいへいかいぞうど)とは、アナログ放送時代のテレビ・ビデオなどの映像機器の、画質の指標のひとつである。 参照元:Wiki

とのことです。

パレットの色数

1画素あたりの色数が1,4,8のときに使うらしいです。上で書いたとおり、正確には色ビット数と呼びます。それぞれ、1bit : 2 種類4bit : 16 種類8bit : 256 種類色を登録することができます。

重要なパレットのインデックス

0の場合もあるらしいです。使ったことはありません。

青・緑・赤の輝度

色の三原色はご存知でしょうか?赤(Red)・緑(Green)・青(Blue)で、RGBとまとめて呼ばれることもあります。そして、RGBのそれぞれの色の濃さによって色を表現することができます。ここでは、1ピクセルごとに色を決めています。

2.3 実際の読み方

C言語でやります。

やることは、順番にfread()で読み込んでいくだけです。下にサンプルコードをおいておきます。

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <Windows.h>
#define true 1
#define false 0
#define HEADER_SIZE 54 //heder size 54 = 14 + 40
#define MAX_WIDTH 3000 //upper limit of width(pixel)
#define MAX_HEIGHT 3000 //upper limit of height(pixel)
#define COLOR_CATEGORY_NUM 1000
#define PLUS_MINUS(a, b, c) ((a) + (c) >= b && (a) - (c) <= b)
#define SIZE 10

// r,g,b values of one pixel
typedef struct {
  unsigned char r;
  unsigned char g;
  unsigned char b;
  int color_type;// どの色か
  int color_num;// color_categoryの場合、どれだけのピクセルがその色に入っているのか
  int sum;// r + g + b;
} color;

typedef struct {
  long height;
  long width;
  color data[MAX_HEIGHT][MAX_WIDTH];
  int color_num;// 色の数
  color color_category[COLOR_CATEGORY_NUM];
} img;

int ReadBmp(char *file_name, img *imgp) {
  unsigned char bmp_header_buf[HEADER_SIZE]; // this store header data of bmp
  char bmp_type[2];
  long bmp_height;
  long bmp_width;
  unsigned short bmp_color;
  long real_width;

  unsigned char *bmp_data;
  FILE *bmp_file;

  bmp_file = fopen(file_name, "rb");

  if(bmp_file == NULL) {
    printf("\n\n\n\nFailed to open bmp file\n");
    return 0;
  }

  // ヘッダー(両方)を取得
  fread(bmp_header_buf, sizeof(unsigned char), HEADER_SIZE, bmp_file);

  // ファイルの最初がBMかどうか
  memcpy(&bmp_type, bmp_header_buf, sizeof(bmp_type));
  if(strncmp(bmp_type, "BM", 2) != 0) {
    printf("Error: %s is not a bmp file.\n", file_name);
    return 0;
  }

  memcpy(&imgp->width, bmp_header_buf + 18, sizeof(bmp_width));
  memcpy(&imgp->height, bmp_header_buf + 22, sizeof(bmp_height));
  memcpy(&bmp_color, bmp_header_buf + 28, sizeof(bmp_color));
  if(bmp_color != 24) {
    printf("Error: bmp_color = %d is not implemented in this program.\n", bmp_color);
    return 0;
  }
  if(imgp->width > MAX_WIDTH) {
    printf("Error: bmp_width = %ld > %d = MAX_WIDTH\n", imgp->width, MAX_WIDTH);
    return 0;
  }
  if(imgp->height > MAX_HEIGHT) {
    printf("Error: bmp_height = %ld > %d = MAX_HEIGHT\n", imgp->height, MAX_HEIGHT);
    return 0;
  }
  if(imgp->height < 0) {
    printf("Error hight is under 0\n");
    return 0;
  }

  printf("Finished checking file format\n");

  real_width = imgp->width * 3 + imgp->width % 4;//calculate real width to fit 4 byte border


  if((bmp_data = (unsigned char *)calloc(real_width, sizeof(unsigned char))) == NULL) {
    printf("Eroor: memory allocation failed for bmp_data\n");
    return 0;
  }

  printf("Reading data...\n");
  for(int i = 0; i < imgp->height; i++) {
    fread(bmp_data, 1, real_width, bmp_file);
    for(int j = 0; j < imgp->width; j++) {
      imgp->data[imgp->height-i-1][j].b = bmp_data[j*3];
      imgp->data[imgp->height-i-1][j].g = bmp_data[j*3+1];
      imgp->data[imgp->height-i-1][j].r = bmp_data[j*3+2];
      imgp->data[imgp->height-i-1][j].sum = bmp_data[j*3] + bmp_data[j*3+1] + bmp_data[j*3+2];
    }
  }
  printf("Finished Reading data\n");

  printf("Please wait a few seconds...\n");

  free(bmp_data);
  fclose(bmp_file);
  return 1;
}



int main() {
  FILE *color_data_file;
  FILE *out_put_data_file;
  img *receiver_data;
  receiver_data = (img *)malloc(sizeof(img));

  char file_name[100];
  // Get file name
  printf("Drag and drop a bmp picture which you want to convert\n");
  printf("And push enter key if I don't say start opening\n");
  // If the name of picture have \n or ", it will be failed.
  scanf("%s", file_name);

  if(!ReadBmp(file_name, receiver_data)) {
    //unsuccess
    return -1;
  }
  return 0;
}
// ご苦労さま