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

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

メモ

試したOSは下記の通りです。他の環境では試せていませんのでご了承ください。

macOS HighSierra

バージョン10.13.4

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

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インストールのための設定

続いてそのフォルダで下記コマンドを実行します。これにより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分ほどで処理完了します。最後の方のメッセージはこんな感じ。

libtool: 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を使用したプログラム例

続いて LibTIFF を使用したプログラム例を紹介します。

TIFFファイル読み込み・書き込み

まずは TIFF ファイルを読み込み・書き込みを行うプログラムです。ここが LibTIFF を主に用いる部分になります。

ヘッダーファイル

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;
} RAWDATA_t;

int tiffFileReadDecode(RAWDATA_t *, const char*);
int tiffFileEncodeWrite(RAWDATA_t *, COMPRESSION_TYPE_t, const char*);
int freeRawData(RAWDATA_t *);

ポイントだけ説明します。

・LibTIFFの関数を使用するためにヘッダファイル

#include "tiffio.h"

・出力TIFFファイルの圧縮方法を定義

非圧縮とLZW圧縮のみに対応させています。ちなみにこのページで紹介しているプログラムではJPEG圧縮には対応していません。JPEG圧縮を行うためにはlibJpegのインストールが必要です。インストールすればJPEG圧縮も可能に変更できます。

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

LibJPEGのインストールとC言語での使用方法・使用例LibJPEGのインストールとC言語での使用方法・使用例
typedef enum{
  NONE_COMPRESSION,
  LZW_COMPRESSION
} COMPRESSION_TYPE_t;

・RAW形式データの情報格納用構造体を定義

dataにはTIFFをデコードしたRAW形式画像データの先頭ポインタを指すアドレス、widthには画像の横幅、heightには画像の高さ、chには画像の色数(カラーの時は3、グレーの時は1)がそれぞれ格納されます。

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

RAW形式画像データについては下の記事で説明していますので是非参考にしてください。

画像データの構造・画素・RAWデータについて解説画像データの構造・画素・RAWデータについて解説

この記事でも出てきているようにdataのポインタの先は下図のように各ピクセルのRGB順にデータが並んでいます。

ソースファイル

myTiff.c
#include "myTiff.h"


int tiffFileReadDecode(RAWDATA_t *rawData, 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, c;

  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);

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

  return 0;

}

int tiffFileEncodeWrite(RAWDATA_t *rawData, 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, rawData->width)){
    printf("TIFFSetField width error\n");
    TIFFClose(tiff);
    return -1;
  }


  if(!TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, rawData->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, rawData->height)){
    printf("TIFFSetField rowsperstrip error\n");
    TIFFClose(tiff);
    return -1;
  }

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

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

  TIFFClose(tiff);

  return 0;
}

int freeRawData(RAWDATA_t *raw){
  free(raw->data);
  return 0;

}

tiffFileReadDecode関数下記を行う関数です。

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

TIFFOpen()やTIFFReadRGBAImage()、TIFFSetField()、TIFFClose()はLibTIFF提供している関数です。

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

一応MacのプレビューソフトでTIFF形式で書き出したデータの読み込みの動作確認はしています。ただしJPEG圧縮のみ試していません。

(JPEG圧縮は上でも触れているようにlibJpegをインストールしないと対応できません)

メモ

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

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

tiffFileEncodeWrite関数下記を行う関数です。

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

また新たにLibTIFFが提供している関数が出てきましたので紹介します。

TIFFWriteEncodedStrip:TIFFをエンコードして指定したTIFFファイルに書き込む

TIFFSetField:TIFFヘッダーのタグの値を指定してセットする

freeRawData関数は引数のrawのdataが指す先の領域を解放します。ここは説明不要ですね。

main関数

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

main.c
#include "myTiff.h"

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

  RAWDATA_t raw;
  int i, j, c;
  int ave, sum;
  char outname[256];

  FILE *fo;

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

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

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

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

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

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

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

  freeRawData(&raw);

  return 0;
}

処理の流れは下記の通りです。

  1. tiffFileReadDecode関数を使用してTIFFファイルの読み込みデコード
  2. 画像処理
  3. tiffFileEncodeWrite()関数を使用してエンコードとTIFFファイルへの書き込み
  4. freeして終了

画像処理では入力された画像をグレースケールに変換するソースコードを書いています。

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

  /* グレースケールに変換 */
  for(j = 0; j < raw.height; j++){
    for(i = 0; i < raw.width; i++){
      sum = 0;
      for(c = 0; c < raw.ch; c++){
        sum += raw.data[raw.ch * (i + j * raw.width) + c];
      }
      ave = sum / raw.ch;
      for(c = 0; c < raw.ch; c++){
        raw.data[raw.ch * (i + j * raw.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 ファイルの読み込み・書き込みできる環境を整えてみてください!

コメントを残す

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