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

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

メモ

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

macOS HighSierra

バージョン10.13.4

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

libJPEGの入手・インストール

まずは libJPEG の入手方法とインストール方法について解説していきます。

libJPEGのダウンロード

まずlibJPEGをダウンロードするためにhttp://www.ijg.org/files/にアクセスしましょう。

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

このページからJFIF(JPEGの規格の一つ)の仕様書もダウンロードするできます。

私が見た時だと「jfif3.pdf」がダウンロードできました。JPEGの仕様も理解したい人は読んでみてください。

続いてターミナルでダウンロードしたファイルがあるフォルダにアクセスします。ターミナルアプリは「アプリケーション」→「ユーティリティ」にあります。


私がダウンロードした.tarファイルをダウンロードフォルダに保存しましたので下記コマンドでフォルダにアクセスしました。

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

続いてダウンロードした.tar.gzを下記コマンドにより展開します。「jpegsrc.v9c.tar」の部分はダウンロードしてきたファイル名に合わせて変更してください

tar xvzf jpegsrc.v9c.tar 

フォルダが作成され、その中に様々なファイルが展開されるはずです。私の場合は「jpeg-9c」というフォルダが作成されました。展開が完了したら、その作成したフォルダにcdコマンドで移動しましょう。

libJPEGインストールのための設定

続いてそのフォルダで下記コマンドを実行します。これによりLibTIFFをインストールするための設定が行われます。

./configure

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

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

checking for objdir… .libs

checking if gcc supports -fno-rtti -fno-exceptions… yes

checking for gcc option to produce PIC… -fno-common -DPIC

checking if gcc PIC flag -fno-common -DPIC works… yes

checking if gcc static flag -static works… no

checking if gcc supports -c -o file.o… yes

checking if gcc supports -c -o file.o… (cached) yes

checking whether the gcc linker (/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld) supports shared libraries… yes

checking dynamic linker characteristics… darwin17.5.0 dyld

checking how to hardcode library paths into programs… immediate

checking whether stripping libraries is possible… yes

checking if libtool supports shared libraries… yes

checking whether to build shared libraries… yes

checking whether to build static libraries… yes

checking libjpeg version number… 12:0:3

checking that generated files are newer than configure… done

configure: creating ./config.status

config.status: creating Makefile

config.status: creating libjpeg.pc

config.status: creating jconfig.h

config.status: executing depfiles commands

config.status: executing libtool commands

libJPEGライブラリのmake

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

make

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

  CCLD     libjpeg.la

  CC       cjpeg.o

  CC       rdppm.o

  CC       rdgif.o

  CC       rdtarga.o

  CC       rdrle.o

  CC       rdbmp.o

  CC       rdswitch.o

  CC       cdjpeg.o

  CCLD     cjpeg

  CC       djpeg.o

  CC       wrppm.o

  CC       wrgif.o

  CC       wrtarga.o

  CC       wrrle.o

  CC       wrbmp.o

  CC       rdcolmap.o

  CCLD     djpeg

  CC       jpegtran.o

  CC       transupp.o

  CCLD     jpegtran

  CC       rdjpgcom.o

  CCLD     rdjpgcom

  CC       wrjpgcom.o

  CCLD     wrjpgcom

libJPEGのインストール

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

sudo make install

sudoをコマンドの前につけると、管理者権限でコマンドを実行を行うことになります。コマンドを実行するとパスワードが求められますので、管理者のパスワード(Macのパスワード)を入力してください。管理者権限でないとファイルの編集や追加削除の権限がなくてコマンド実行しても失敗します。sudoも使用頻度が高いコマンドですので覚えておきましょう。

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

libtool: install: /usr/bin/install -c .libs/libjpeg.a /usr/local/lib/libjpeg.a

libtool: install: chmod 644 /usr/local/lib/libjpeg.a

libtool: install: ranlib /usr/local/lib/libjpeg.a

 ./install-sh -c -d ‘/usr/local/bin’

  /bin/sh ./libtool   –mode=install /usr/bin/install -c cjpeg djpeg jpegtran rdjpgcom wrjpgcom ‘/usr/local/bin’

libtool: install: /usr/bin/install -c .libs/cjpeg /usr/local/bin/cjpeg

libtool: install: /usr/bin/install -c .libs/djpeg /usr/local/bin/djpeg

libtool: install: /usr/bin/install -c .libs/jpegtran /usr/local/bin/jpegtran

libtool: install: /usr/bin/install -c rdjpgcom /usr/local/bin/rdjpgcom

libtool: install: /usr/bin/install -c wrjpgcom /usr/local/bin/wrjpgcom

/bin/sh /Users/uedakazuhiro/Downloads/jpeg-9c/install-sh -d /usr/local/include

/usr/bin/install -c -m 644 jconfig.h /usr/local/include/jconfig.h

 ./install-sh -c -d ‘/usr/local/include’

 /usr/bin/install -c -m 644 jerror.h jmorecfg.h jpeglib.h ‘/usr/local/include’

 ./install-sh -c -d ‘/usr/local/share/man/man1’

 /usr/bin/install -c -m 644 cjpeg.1 djpeg.1 jpegtran.1 rdjpgcom.1 wrjpgcom.1 ‘/usr/local/share/man/man1’

 ./install-sh -c -d ‘/usr/local/lib/pkgconfig’

 /usr/bin/install -c -m 644 libjpeg.pc ‘/usr/local/lib/pkgconfig’

インストールの確認

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

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

-ljpegを付けるとlibJPEGをリンクしてやることが可能になります。これにより自分のプログラムでjpeg関連の様々な関数が使用可能になります。

gcc japanese.c -o japanese.exe -ljpeg

エラーが表示されずコンパイルが完了すれば、libJPEGのインストールに成功です。

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

ld: library not found for -ljpeg

clang: error: linker command failed with exit code 1 (use -v to see invocation)

libJPEGを使用したプログラム例

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

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

まずは JPEG を読み込み・書き込みするプログラムについて紹介します。

ヘッダーファイル

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

#include "jpeglib.h"

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

int jpegFileReadDecode(BITMAPDATA_t *, const char*);
int jpegFileEncodeWrite(BITMAPDATA_t *, const char*);
int freeBitmapData(BITMAPDATA_t *);

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

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

#include "jpeglib.h"

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

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

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

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

画像データの構造・画素・ビットマップデータについて解説画像データの構造・画素・ビットマップデータについて解説

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

ソースファイル

myJpeg.c
#include "myJpeg.h"

int jpegFileReadDecode(BITMAPDATA_t *bitmapData, const char* filename){
  struct jpeg_decompress_struct jpeg;
  struct jpeg_error_mgr err;

  FILE *fi;
  int j;
  JSAMPLE *tmp;

  jpeg.err = jpeg_std_error(&err);

  fi = fopen(filename, "rb");
  if(fi == NULL){
    printf("%sは開けません\n", filename);
    return -1;
  }

  jpeg_create_decompress(&jpeg);
  jpeg_stdio_src(&jpeg, fi);
  jpeg_read_header(&jpeg, TRUE);
  jpeg_start_decompress(&jpeg);

  printf("width = %d, height = %d, ch = %d\n", jpeg.output_width, jpeg.output_height, jpeg.out_color_components);

  bitmapData->data =
    (unsigned char*)malloc(sizeof(unsigned char) * jpeg.output_width * jpeg.output_height * jpeg.out_color_components);
  if(bitmapData->data == NULL){
    printf("data malloc error\n");
    fclose(fi);
    jpeg_destroy_decompress(&jpeg);
    return -1;
  }

  for(j = 0; j < jpeg.output_height; j++){
    tmp = bitmapData->data + j * jpeg.out_color_components * jpeg.output_width;
    jpeg_read_scanlines(&jpeg, &tmp, 1);
  }

  bitmapData->height = jpeg.output_height;
  bitmapData->width = jpeg.output_width;
  bitmapData->ch = jpeg.out_color_components;

  jpeg_finish_decompress(&jpeg);
  jpeg_destroy_decompress(&jpeg);

  fclose(fi);

  return 0;
}

int jpegFileEncodeWrite(BITMAPDATA_t *bitmapData, const char *filename){
  struct jpeg_compress_struct jpeg;
  struct jpeg_error_mgr err;
  FILE *fo;
  JSAMPLE *address;
  int j;

  jpeg.err = jpeg_std_error(&err);
  jpeg_create_compress(&jpeg);

  fo = fopen(filename, "wb");
  if(fo == NULL){
    printf("%sは開けません\n", filename);
    jpeg_destroy_compress(&jpeg);
    return -1;
  }

  jpeg_stdio_dest(&jpeg, fo);

  jpeg.image_width = bitmapData->width;
  jpeg.image_height = bitmapData->height;
  jpeg.input_components = bitmapData->ch;
  jpeg.in_color_space = JCS_RGB;
  jpeg_set_defaults(&jpeg);

  jpeg_set_quality(&jpeg, 50, TRUE);

  jpeg_start_compress(&jpeg, TRUE);

  for (j = 0; j < jpeg.image_height; j++ ) {
    address = bitmapData->data + (j * bitmapData->width * bitmapData->ch);
    jpeg_write_scanlines(&jpeg, &address, 1);
  }

  jpeg_finish_compress(&jpeg);

  jpeg_destroy_compress(&jpeg);

  return 0;
}

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

jpegFileReadDecode関数

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

  • 引数で指定されたfilenameファイルの読み込みとデコードの準備のためのlibJPEGの関数実行
  • デコード後のBITMAP形式画像データを格納するためにメモリ領域確保してbitmapDataのdataメンバにアドレスセット
  • 確保したメモリ領域にJPEGを1ラインずつデコード
  • 画像データの画像の幅や高さを引数のbitmapDataにセット

詳細を見ていくと下記のような処理を行っています。

・エラーハンドラの設定

  jpeg.err = jpeg_std_error(&err);

エラー起きた時にエラーの原因を出力してくれるようになります

・デコーダの初期化処理

  jpeg_create_decompress(&jpeg);

・読み込むJPEGファイルの設定

  jpeg_stdio_src(&jpeg, fi);

・JPEGのヘッダの読み込み

  jpeg_read_header(&jpeg, TRUE);

・デコードの開始

  jpeg_start_decompress(&jpeg);

・デコード結果格納メモリ領域確保

  bitmapData->data =
    (unsigned char*)malloc(sizeof(unsigned char) * jpeg.output_width * jpeg.output_height * jpeg.out_color_components);

・デコード結果のメモリ領域へのコピー

  for(j = 0; j < jpeg.output_height; j++){
    tmp = bitmapData->data + j * jpeg.out_color_components * jpeg.output_width;
    jpeg_read_scanlines(&jpeg, &tmp, 1);
  }

1ラインずつデコードしながらメモリ領域へコピーして行っています。

・デコード画像情報をBITMAPDATA_tへ格納

  bitmapData->height = jpeg.output_height;
  bitmapData->width = jpeg.output_width;
  bitmapData->ch = jpeg.out_color_components;

・デコーダの終了処理と削除

  jpeg_finish_decompress(&jpeg);
  jpeg_destroy_decompress(&jpeg);

 

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

jpegFileEncodeWrite関数

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

  • 引数で指定されたfilenameファイルの作成とエンコードの準備のためのLib JPEG関数の実行
  • JPEGのヘッダーの設定
  • JPEGの画質の設定
  • 引数のbitmapDataのdataメンバで指定されるアドレスのデータを1ラインずつエンコードしながらファイルに書き込み

詳細を見ていくと下記のような処理を行っています。

・エラーハンドラの設定

  jpeg.err = jpeg_std_error(&err);

・エンコーダの作成

  jpeg_create_compress(&jpeg);

・エンコーダ結果の出力先ファイル設定

  jpeg_stdio_dest(&jpeg, fo);

・ヘッダ情報の設定

  jpeg.image_width = bitmapData->width;
  jpeg.image_height = bitmapData->height;
  jpeg.input_components = bitmapData->ch;
  jpeg.in_color_space = JCS_RGB;
  jpeg_set_defaults(&jpeg);

jpeg_set_defaults()で残りのヘッダ情報をデフォルト値に設定してくれます。ただしヘッダ情報はin_color_spaceに基づいて設定する部分もありますのでin_color_spaceを設定してからこの関数は実行した方が良いです。

・画質の設定

  jpeg_set_quality(&jpeg, 50, TRUE);

第二引数は0から100までの値が設定でき、大きいほどエンコード後の画質が良くなります。

・エンコード開始

code>  jpeg_start_compress(&jpeg, TRUE);

・エンコード結果のファイルへの書き込み

 for (j = 0; j < jpeg.image_height; j++ ) {
    address = bitmapData->data + (j * bitmapData->width * bitmapData->ch);
    jpeg_write_scanlines(&jpeg, &address, 1);
  }

1ラインずつエンコードを行いながらファイルに書き込ませています。

・エンコーダの終了処理と削除

  jpeg_finish_compress(&jpeg);

  jpeg_destroy_compress(&jpeg);

 

freeBitmapData関数

freeBitmapData関数は引数のbitmapのdataが指す先の領域を解放します。

main関数

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

main.c
#include "myJpeg.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(jpegFileReadDecode(&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.jpg");

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

  freeBitmapData(&bitmap);

  return 0;
}

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

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

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

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

画像処理実装部分
  /* グレースケールに変換 */
  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;
      }
    }
  }

私はJPEG読み込み用・書き込み用のソースファイルを”myJpeg.c”として分けて作成し、ヘッダーファイルを”myJpegh”としています。ですので上記のソースコードにはところどころ”myJpeg.h”が出てきています。違う名前にする場合は変更必要ですのでお気をつけください。このファイル名を用いている時、下記のコマンドでコンパイルとプログラムの実行が可能です。

gcc main.c -c
gcc myJpeg.c -c
gcc main.o myJpeg.o -o jpeg.exe -ljpeg
./jpeg.exe test.jpeg

重要なのはコンパイル時に”-ljpeg“を付けてlibJPEGにリンクすることを忘れないようにしましょう

入力したJPEGと出力結果のJPEGもここに表示しておきます(このファイルであれば動作確認しています)。

・入力JPEG

・出力JPEG

ここで載せたソースコードはあくまで一例です。JPEGファイルを読み込んでデコードするあたりやJPEGファイルをエンコードして書き込むあたりはカスタマイズして自分が使用しやすいように変更してください。

スポンサーリンク

libJPEGが提供する関数

libJPEGが提供する関数の説明はダウンロードしてきたフォルダ内の「libjpeg.txt」に記載されています。英語なので読むのは大変かもですが・・・。

注意

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

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

XCodeでのlibJPEG使用方法

libJPEGをXCodeで使用するためには2つの手順が必要です。

インクルードパスを追加

libjpeg関連の関数をソースコードで使用するためには、これらの関数が定義されているヘッダーファイルをインクルードする必要があります。そのヘッダーファイルがある場所をXCodeのプロジェクトに認識させる必要があります。

上の画像の通りにHeader Search Pathsにヘッダのあるパスを追加します。

MEMO

このページで紹介したインストール方法でインストールした場合は下記にヘッダーが存在すると思います。

/usr/local/include

リンク対象ファイル追加

libjpegを使用するためにライブラリファイルをリンク対象とする必要があります。

上の画像の通りにLink Binary With Librariesに「libjpeg.a」を追加すれば良いです。

MEMO

このページで紹介したインストール方法でインストールした場合は下記にライブラリファイルが存在すると思います。

/usr/local/lib

ただし、ここにライブラリがあるとファイルの選択時にこの場所が選択できないかもしれません。その場合は「書類フォルダ」等の適当な場所にコピーしておくと良いと思います。ターミナルから下記のコマンドでlibjpeg関連のライブラリファイルがコピーができます。

cp /usrlocal/lib/libjpeg.* [コピー先]

まとめ

このページでは libJPEG のインストール方法とその使い方について解説しました。JPEG は現状一番普及している画像フォーマットです。ただその JPEG を読み込むプログラムを作ろうと思うとめちゃくちゃ難しいです。しかし libJPEG を使えば簡単に JPEG の読み込みや作成が出来てしまいます。是非このページを参考にして libJPEG をインストールしてみてください!

コメントを残す

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