【C言語】LibTIFFのインストールと使用方法・使用例

libtiffの使い方解説ページアイキャッチ

このページでは Mac に LibTIFF をインストールする手順と LibTIFF を用いたプログラムの例を紹介します。

メモ

試したOSは下記の通りです

他の環境では試せていませんのでご了承ください。

  • macOS HighSierra(バージョン10.13.4)

ダウンロードされるフォルダ違うなどの違いがありますが、LINUX 環境でも手順は参考になると思います。

LibTIFF とは

LibTIFF は TIFF 画像ファイルを扱うためのライブラリです。

この LibTIFF を利用することで、C言語などのプログラムから簡単に TIFF 画像を読み込んだり、TIFF 画像を作成したりできるようになります。

LibTIFF のインストール

それでは LibTIFF のインストール方法について解説していきます。

このインストールの基本的な流れは下記になります。

  • ダウンロード
  • 設定
  • ライブラリ生成
  • インストール
  • インストールの確認

スポンサーリンク

LibTIFF のダウンロード

まず LibTIFF をダウンロードするためにhttp://download.osgeo.org/libtiff/にアクセスしましょう。

上のような画面が表示されると思いますので、最新の .zip ファイルをダウンロードします。2018/07/29現在だと 4.0.9 が最新のようでした。

.zip は解凍しておきましょう。

続いてターミナルでダウンロードしたファイルがあるフォルダにアクセスします。私がダウンロードした.zipファイルをダウンロードフォルダに保存し、そこに展開しましたので下記コマンドでアクセスしました。

cd /Users/[私のユーザ名]//Downloads/tiff-4.0.9

LibTIFF の設定

続いてそのフォルダで下記コマンドを実行します。これによりあなたの PC に合わせて LibTIFF の設定が行われます。

./configure

このコマンド実行後たくさんのメッセージが表示されながら処理が実行されますが1分ほどで処理が完了するはずです。

最後の方のメッセージは下記のような感じでした。良ければご参考に。

Libtiff is now configured for i386-apple-darwin17.5.0
Installation directory: /usr/local
Documentation directory:${prefix}/share/doc/tiff-4.0.9
C compiler: gcc -g -O2 -Wall -W
C++ compiler: g++ -g -O2
Enable runtime linker paths:no
Enable linker symbol versioning:no
Support Microsoft Document Imaging: yes
Use win32 IO: no
Support for internal codecs:
CCITT Group 3 & 4 algorithms: yes
Macintosh PackBits algorithm: yes
LZW algorithm:yes
ThunderScan 4-bit RLE algorithm:yes
NeXT 2-bit RLE algorithm: yes
LogLuv high dynamic range encoding: yes
Support for external codecs:
ZLIB support: yes
Pixar log-format algorithm: yes
JPEG support: no
Old JPEG support: no
JPEG 8/12 bit dual mode:no
ISO JBIG support: no
LZMA2 support:no
C++ support:yes
OpenGL support:no

LibTIFF ライブラリの生成

続いて同じフォルダ内で下記の make コマンドを実行します。これにより LibTIFF ライブラリが生成されます。

make

こちらも1分ほどで処理完了します。最後の方のメッセージはこんな感じ。

ibtool: link: gcc -g -O2 -Wall -W -o .libs/iptcutil iptcutil.o ../../libtiff/.libs/libtiff.dylib -lz
Making all in mfs
make[2]: Nothing to be done for `all’.
Making all in pds
make[2]: Nothing to be done for `all’.
Making all in ras
make[2]: Nothing to be done for `all’.
Making all in stream
make[2]: Nothing to be done for `all’.
Making all in tags
make[2]: Nothing to be done for `all’.
Making all in win_dib
make[2]: Nothing to be done for `all’.
make[2]: Nothing to be done for `all-am’.
Making all in test
make[1]: Nothing to be done for `all’.
Making all in man
make[1]: Nothing to be done for `all’.
Making all in html
Making all in images
make[2]: Nothing to be done for `all’.
Making all in man
make[2]: Nothing to be done for `all’.
make[2]: Nothing to be done for `all-am’.
make[1]: Nothing to be done for `all-am’

スポンサーリンク

LibTIFF のインストール

次に LibTIFF をインストールします。次のコマンドによりインストールが行われるはずです。

sudo make install

インストールはすぐ処理が完了すると思います。最後の方のメッセージを念のため載せておきます。

make[2]: Nothing to be done for `install-exec-am'.
config/install-sh -c -d '/usr/local/share/doc/tiff-4.0.9'
/usr/bin/install -c -m 644 COPYRIGHT ChangeLog README README.vms RELEASE-DATE TODO VERSION '/usr/local/share/doc/tiff-4.0.9'
config/install-sh -c -d '/usr/local/lib/pkgconfig'
/usr/bin/install -c -m 644 libtiff-4.pc '/usr/local/lib/pkgconfig'

インストールの確認

最後にインストールされたことを確認しておきましょう。

今まで作成したC言語のプログラムファイルをコンパイルしてやるときに、最後に -ltiff をつけてからコンパイルしてみてください。下記のような感じです。

-ltiff を付けることにより LibTIFF をリンクすることができます。これにより自分のプログラムで TIFF 画像を扱うための様々な関数が使用可能になります。

gcc japanese.c -o japanese.exe -ltiff

エラーが表示されずコンパイルが完了すれば、LibTIFF のインストールに成功です。お疲れ様でした!

インストールがうまくできていなければ下記のようなメッセージが表示されますので再度手順を確認してみてください。

ld: library not found for -ltiff clang: error: linker command failed with exit code 1 (use -v to see invocation)

LibTIFF を使用したTIFFファイル読み込み・書き込み関数の作成

続いて、インストールした LibTIFF を用いて TIFF ファイルを読み込み・書き込みを行う関数の例を紹介していきたいと思います。

スポンサーリンク

ヘッダーファイル

まずは LibTIFF に関連するヘッダーをインクルードしたり、必要な構造体の定義、TIFF ファイルを読み込み・書き込みする関数の定義を行うヘッダーファイルを用意します。

下記がそのヘッダーファイルの例になります。

myTiff.h
#include <stdio.h>
#include <stdlib.h>

#include "tiffio.h"

typedef enum{
  NONE_COMPRESSION,
  LZW_COMPRESSION
} COMPRESSION_TYPE_t;

typedef struct{
  unsigned char *data;
  unsigned int width;
  unsigned int height;
  unsigned int ch;
} BITMAPDATA_t;

int tiffFileReadDecode(BITMAPDATA_t *, const char*);
int tiffFileEncodeWrite(BITMAPDATA_t *, COMPRESSION_TYPE_t, const char*);
int freeBitmapData(BITMAPDATA_t *);

ポイントを説明しておきます。

tiffio.h のインクルード

LibTIFF が提供する関数や定数などは tiffio.h というファイルで宣言・定義されています。

ですので、LibTIFF を利用するためにはこの tiffio.h をインクルードする必要があります。

tiffio.hのインクルード
#include "tiffio.h"
MEMO

もし tiffio.h が見つからない場合は、ヘッダーインクルードパスが tiffio.h がインストールされたフォルダに通っているかを確認してみてください

インストール時にこのヘッダーインクルードパスも設定されるはずですが、環境によっては別途設定する必要があるかもしれません

圧縮方法の定義

さらに、下記で出力TIFFファイルの圧縮方法を enum 型で定義しています。

tiffio.hの圧縮形式の定義
typedef enum{
  NONE_COMPRESSION,
  LZW_COMPRESSION
} COMPRESSION_TYPE_t;

それぞれが下記の圧縮形式に対応しています。

  • NONE_COMPRESSION:非圧縮
  • LZW_COMPRESSION:LZW圧縮

ちなみにこのページで紹介しているプログラムでは JPEG 圧縮には対応していません。

JPEG 圧縮を行うためには libJpeg のインストールが必要です。インストールすれば JPEG 圧縮も可能に変更できます。

LibJPEG のインストールについては次の記事を参考にしていただければと思います。

libjpegの使い方解説ページアイキャッチ【C言語】libjpegのインストールと使用方法・使用例

BITMAP 形式データ構造体の定義

また、下記では BITMAP 形式データの情報格納用構造体 BITMAPDATA_t を定義しています。

BITMAP情報格納用の構造体
typedef struct{
  unsigned char *data;
  unsigned int width;
  unsigned int height;
  unsigned int ch;
} BITMAPDATA_t;

それぞれのメンバの説明は下記のようになります。

  • data:TIFF をデコードした BITMAP 形式画像データの先頭アドレス
  • width:画像の横幅
  • height:画像の高さ
  • ch:画像の色数(カラーの時は 3、グレーの時は 1

BITMAP 形式画像データって何?という方は下記ページで説明していますので是非参考にしてください。

画像データの解説ページアイキャッチ画像データの構造・画素・ビットマップデータについて解説

上のページでも解説しているように data のポインタの先は下図のように各ピクセルの RGB 順にデータが並んでいます。

ソースファイル

次は実際に LibTIFF が提供している関数を利用して TIFF ファイルを読み込んだり、TIFF ファイルとして保存したりする関数を作成していきます。

myTiff.c
#include "myTiff.h"

int tiffFileReadDecode(BITMAPDATA_t *bitmapData, const char* filename){
  TIFF *tiff;
  unsigned int length = 0;
  unsigned int width = 0;
  unsigned int bitpersample = 0;
  unsigned int ch = 0;
  unsigned int *rgbaData = NULL;
  unsigned char *rgbData = NULL;
  int i, j;

  if(filename == NULL){
    printf("ファイル名がNULL\n");
    return -1;
  }

  tiff = TIFFOpen(filename, "r");
  if(tiff == NULL){
    printf("TIFFOpen error\n");
    return -1;
  }

  if(!TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &length)){
    printf("TIFFGetField length error\n");
    TIFFClose(tiff);
    return -1;
  }

  if(!TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width)){
    printf("TIFFGetField width error\n");
    TIFFClose(tiff);
    return -1;
  }

  if(!TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bitpersample)){
    printf("TIFFGetField bitpersample error\n");
    TIFFClose(tiff);
    return -1;
  }

  if(bitpersample != 8){
    printf("1プレーン8bit以外の画像データは扱っていません\n");
    TIFFClose(tiff);
    return -1;
  }

  if(!TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &ch)){
    printf("TIFFGetField samplesperpixel error\n");
    TIFFClose(tiff);
    return -1;
  }

  printf("width = %d, length = %d, ch = %d\n", width, length, ch);

  rgbaData = (unsigned int*)malloc(sizeof(unsigned int) * width * length);
  if(rgbaData == NULL){
    printf("malloc rgbaData error\n");
    TIFFClose(tiff);
    return -1;
  }

  if(!TIFFReadRGBAImage(tiff, width, length, rgbaData, 0)){
    printf("TIFFReadRGBAImage error\n");
    free(rgbData);
    TIFFClose(tiff);
    return -1;
  }
  TIFFClose(tiff);

  rgbData = (unsigned char*)malloc(sizeof(unsigned char) * width * length * 3);
  if(rgbData == NULL){
    printf("malloc rgbData error\n");
    free(rgbaData);
    return -1;
  }

  /* なぜか読み込み画像が上限反転するので強制的にさらに上下回転させてる */
  for(j = 0; j < length; j++){
    for(i = 0; i < width; i++){
      #if 1
      rgbData[3 * (i + j * width) + 0] = TIFFGetR(rgbaData[i + (length - 1 - j) * width]);
      rgbData[3 * (i + j * width) + 1] = TIFFGetG(rgbaData[i + (length - 1 - j) * width]);
      rgbData[3 * (i + j * width) + 2] = TIFFGetB(rgbaData[i + (length - 1 - j) * width]);
      #else
      rgbData[3 * (i + j * width) + 0] = TIFFGetR(rgbaData[i + j * width]);
      rgbData[3 * (i + j * width) + 1] = TIFFGetG(rgbaData[i + j * width]);
      rgbData[3 * (i + j * width) + 2] = TIFFGetB(rgbaData[i + j * width]);
      #endif
    }
  }
  free(rgbaData);

  bitmapData->data = rgbData;
  bitmapData->width = width;
  bitmapData->height = length;
  bitmapData->ch = 3;

  return 0;

}

int tiffFileEncodeWrite(BITMAPDATA_t *bitmapData, COMPRESSION_TYPE_t cmpType, const char* filename){

  TIFF *tiff;
  tsize_t size;

  if(filename == NULL){
    printf("ファイル名がNULL\n");
    return -1;
  }

  tiff = TIFFOpen(filename, "w");
  if(tiff == NULL){
    printf("TIFFOpen error\n");
    return -1;
  }

  if(cmpType == NONE_COMPRESSION){
    if(!TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_NONE)){
      printf("TIFFSetField compression error\n");
      TIFFClose(tiff);
      return -1;
    }
  } else if(cmpType == LZW_COMPRESSION){
    if(!TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_LZW)){
      printf("TIFFSetField compression error\n");
      TIFFClose(tiff);
      return -1;
    }
  } else {
    printf("encode type error:%d\n", cmpType);
    TIFFClose(tiff);
    return -1;
  }

  if(!TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, bitmapData->width)){
    printf("TIFFSetField width error\n");
    TIFFClose(tiff);
    return -1;
  }


  if(!TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, bitmapData->height)){
    printf("TIFFSetField length error\n");
    TIFFClose(tiff);
    return -1;
  }

  if(!TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)){
    printf("TIFFSetField bitspersample error\n");
    TIFFClose(tiff);
    return -1;
  }

  if(!TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 3)){
    printf("TIFFSetField samplesperpixel error\n");
    TIFFClose(tiff);
    return -1;
  }


  if(!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)){
    printf("TIFFSetField photometric error\n");
    TIFFClose(tiff);
    return -1;
  }

  if(!TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)){
    printf("TIFFSetField planarconfig error\n");
    TIFFClose(tiff);
    return -1;
  }

  if(!TIFFSetField(tiff, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB)){
    printf("TIFFSetField fillorder error\n");
    TIFFClose(tiff);
    return -1;
  }

  if(!TIFFSetField(tiff, TIFFTAG_XRESOLUTION, 72.0)){
    printf("TIFFSetField xresolution error\n");
    TIFFClose(tiff);
    return -1;
  }

  if(!TIFFSetField(tiff, TIFFTAG_YRESOLUTION, 72.0)){
    printf("TIFFSetField yresolution error\n");
    TIFFClose(tiff);
    return -1;
  }

  if(!TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, bitmapData->height)){
    printf("TIFFSetField rowsperstrip error\n");
    TIFFClose(tiff);
    return -1;
  }

  size = TIFFWriteEncodedStrip(tiff, 0, bitmapData->data, bitmapData->width * bitmapData->height * 3);
  if(size == -1){
    printf("TIFFWriteEncodedStrip error\n");
    TIFFClose(tiff);
    return -1;
  }

  printf("writeSize = %lu\n", size);

  TIFFClose(tiff);

  return 0;
}

int freeBitmapData(BITMAPDATA_t *bitmap){
  free(bitmap->data);
  return 0;

}

特に TIFF の読み込みを行う tiffFileReadDecode 関数と、TIFF の書き込みを行う tiffFileEncodeWrite 関数の詳細を解説していきます。

TIFF ファイルの読み込みを行う関数

tiffFileReadDecode 関数は TIFF ファイルの読み込みを行う関数になります。

より具体的には、この tiffFileReadDecode 関数は下記を行う関数になります。

  • 引数で指定された filename のファイルを読み込む
  • 読み込んだファイルの TIFF データをデコードし BITMAP 形式画像データに変換
  • BITMAP 形式画像データをさらに関数呼び元で扱いやすいように unsigned int 型から unsigned char 型に変換(RGB それぞれを 1 バイトずつに分けて格納。Aチャネルは捨てています)
  • 画像データのアドレスや画像の幅や高さを引数の bitmapData にセット

tiffFileReadDecode 関数で使用している下記関数は、LibTIFF が提供している関数になります。

  • TIFFOpen:TIFF ファイルを開く
  • TIFFReadRGBAImage:TIFF ファイルを RGBA 形式にデコード
  • TIFFSetField:TIFF ファイルのヘッダー内の指定したタグの値を取得
  • TIFFClose:TIFF ファイルを閉じる

一応 Mac のプレビューソフトで TIFF 形式で書き出したデータの読み込みの動作確認はしています。ただし JPEG 圧縮のみ試していません(前述の通り JPEG 圧縮は上でも触れているように libJpeg をインストールしないと対応できません)。

MEMO

コメントでも書いているのですがなぜか画像が上下逆転する現象が起きたので無理やり直しています

もしかしたら何か勘違いして処理してしまっているのかも

スポンサーリンク

TIFF ファイルの書き込み(保存)を行う関数

tiffFileEncodeWrite 関数は TIFF ファイルの書き込み(保存)を行う関数になります。

より具体的には、この tiffFileEncodeWrite 関数下記を行う関数になります。

  • 引数で指定された filename のファイルを作成
  • 引数の bitmapData にセットされている情報等から TIFF のヘッダのタグの値の設定(圧縮方法は引数の cmpType で指定されたものを使用)
  • 引数の bitmapDatadata メンバで指定されるアドレスのデータをエンコードして開いているファイルに書き込み

tiffFileEncodeWrite 内でも LibTIFF が提供する下記関数を利用しています(tiffFileReadDecode で使用している関数は省略しています)。

  • TIFFWriteEncodedStrip:TIFFをエンコードして指定したTIFFファイルに書き込む
  • TIFFSetField:TIFFヘッダーのタグの値を指定してセットする

作成した TIFF 読み込み・書き込み関数の使用例

上記で作成した関数の使い方がイメージつくように使用例を書いてみました。

main.c
#include "myTiff.h"

int main(int argc, char *argv[]){

  BITMAPDATA_t bitmap;
  int i, j, c;
  int ave, sum;
  char outname[256];

  FILE *fo;

  if(argc != 2){
    printf("ファイル名が指定されていません\n");
    return -1;
  }

  if(tiffFileReadDecode(&bitmap, argv[1]) == -1){
    printf("tiffFileReadDecode error\n");
    return -1;
  }

  printf("bitmap->data = %p\n", bitmap.data);
  printf("bitmap->width = %d\n", bitmap.width);
  printf("bitmap->height = %d\n", bitmap.height);
  printf("bitmap->ch = %d\n", bitmap.ch);

#if 0
  /* ちゃんと読み込めているか確認する場合はここを有効に */
  fo = fopen("testoutput.ppm", "wb");
  fprintf(fo, "P6\n#\n%d %d\n255\n", bitmap.width, bitmap.height);
  fwrite(bitmap.data, bitmap.height * bitmap.width * 3, 1,fo);
  fclose(fo);
#endif

  /* グレースケールに変換 */
  for(j = 0; j < bitmap.height; j++){
    for(i = 0; i < bitmap.width; i++){
      sum = 0;
      for(c = 0; c < bitmap.ch; c++){
        sum += bitmap.data[bitmap.ch * (i + j * bitmap.width) + c];
      }
      ave = sum / bitmap.ch;
      for(c = 0; c < bitmap.ch; c++){
        bitmap.data[bitmap.ch * (i + j * bitmap.width) + c] = ave;
      }
    }
  }

  sprintf(outname, "%s", "output.tiff");

  if(tiffFileEncodeWrite(&bitmap, LZW_COMPRESSION, outname) == -1){
    printf("tiffFileEncodeWrite error\n");
    freeBitmapData(&bitmap);
    return -1;
  }

  freeBitmapData(&bitmap);

  return 0;
}

処理の流れ

この main 関数の処理の流れは下記の通りです。

  1. tiffFileReadDecode 関数を使用して TIFF ファイルの読み込みデコード
  2. 1. でデコードした BITMAP データに対して画像処理
  3. 2. 画像処理後の BITMAP データを tiffFileEncodeWrite 関数を使用してエンコード & TIFF ファイルへの書き込み
  4. freeBitmapData 関数でメモリ解放して終了

画像処理

2. の画像処理では、1. でデコードした画像をグレースケールに変換しています(単純に RGB の輝度値の平均値を求めているだけ)。

画像処理をいろいろ試す場合はここをいろいろ変更して試してみると良いと思います。

画像処理
  /* グレースケールに変換 */
  for(j = 0; j < bitmap.height; j++){
    for(i = 0; i < bitmap.width; i++){
      sum = 0;
      for(c = 0; c < bitmap.ch; c++){
        sum += bitmap.data[bitmap.ch * (i + j * bitmap.width) + c];
      }
      ave = sum / bitmap.ch;
      for(c = 0; c < bitmap.ch; c++){
        bitmap.data[bitmap.ch * (i + j * bitmap.width) + c] = ave;
      }
    }
  }

コンパイル

このページで紹介するソースコードでは、 TIFF 読み込み用・書き込み用の関数を myTiff.c に作成し、それらの関数のプロトタイプ宣言をヘッダーファイルの myTiff.h で行っています。

ですので、上記のソースコードで myTiff.h のインクルードを行なっています。

違う名前にする場合は変更必要ですのでお気をつけください。

このページで紹介しているファイル名でソースコードファイルを作成している時、下記のコマンドでコンパイルとプログラムの実行が可能です。

gcc main.c -c
gcc myTiff.c -c
gcc main.o myTiff.o -o tiff.exe -ltiff
./tiff.exe test.tiff

前にも説明していますが、重要なのはコンパイル時に -ltiff を付けることです。これは LibTIFF にリンクするためのコンパイルオプションになります。

また、ここで載せたソースコードはあくまで一例です。

TIFF ファイルを読み込んでデコードするあたりや TIFF ファイルをエンコードして書き込むあたりはご自身が使いやすいようにカスタマイズしてください。

LibTIFF が提供する関数

LibTIFF が提供する関数にどんなものがあるかや、使用方法は下記の「Man Pages – LibTIFF」に記載されています。

英語なので読むのが大変かもしれませんが、これを参考にすればC言語で TIFF ファイルをもっと巧みに扱えるようになると思います。

参考 Man Pages - LibTIFFlibtiff.org

リクエストが多いようであれば使用方法を別記事でまた作成したいと思います。

注意

このページは LibTIFF のインストール方法や使用例の紹介を目的としたページであり、LibTIFF のライセンス体系については私も詳しくないのでこのページでは言及していません

LibTIFF を使用する方は自己責任で使用してください

例えば商用利用等を行う場合はライセンス体系についてご自分で調査してから使用の可否をご判断ください

スポンサーリンク

まとめ

このページでは LibTIFF のインストール方法やプログラム中での使い方について解説しました。

画像処理プログラミングを始めるにあたって大きな壁になるのは「画像ファイルの読み込みと書き込み」です。

LibTIFF が使えればこれらを簡単に行えるようになりますので、是非このページを参考にして TIFF ファイルの読み込み・書き込みできる環境を整えてみてください!

私のサイトでは他にも libjpeg や libpng、libwebp のインストール方法や使い方についても解説していますので、他の画像フォーマットを利用したい方は是非下記のページも読んでみてください。

libjpegの使い方解説ページアイキャッチ【C言語】libjpegのインストールと使用方法・使用例 libpngの使い方解説ページアイキャッチ【C言語】libpngのインストールと使用方法・使用例 libwebpの使い方解説ページアイキャッチ【C言語】libwebpのインストールと使用方法・使用例

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です