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

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

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

メモ

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

  • macOS Mojave(バージョン10.14.1)

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

libwebp とは

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

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

libwebp のインストール

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

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

  • ダウンロード
  • インストール
  • インストールの確認

スポンサーリンク

libwebp のダウンロード

libwebp は下記のサイトからダウンロードすることができます。

https://developers.google.com/speed/webp/download?hl=ja

libwebp の場合は、「コンパイル済みライブラリ」をダウンロードしてインストールする方法と「ソースコード」をダウンロードして自身でコンパイルする方法があります。

今回は前者の方法を解説していきたいと思います。

MEMO

後者の場合は「設定」や「ライブラリ生成」を自身で行う必要があります

この方法に関しては libjpeg や libpng 等と同様になりますので、こちらの方法を知りたい方は、まとめで紹介しているリンク先にある他のライブラリのインストール解説ページを参照していただければと思います

コンパイル済みライブラリは、「Download for OS名」と書かれたリンクをクリックすればダウンロードすることができます。

例えば Mac OS X を使用しているのであれば、下の画像の右下にある「Download for Mac OSX」からダウンロードできます。

2018/12/4だと、libwebp-1.0.0-mac-10.13.tarという名のファイルがダウンロードされました。

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


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

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

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

tar xvf libwebp-1.0.0-mac-10.13.tar

フォルダが作成され、その中に様々なファイルが展開されるはずです。私の場合は “libwebp-1.0.0-mac-10.13” というフォルダが作成されました。

フォルダの中身は下図のようになっていると思います。

各フォルダに格納されているファイルの中身は下記の通りです。

  • lib:libwebp のライブラリ
  • include:libwebp のヘッダーファイル
  • bin:libwebp の実行可能ファイル
  • doc:libwebp のドキュメント

プログラムから主に使用するのは libinclude です。

include のフォルダに入っているヘッダーファイルをソースコードから #include し、コンパイル(リンク)の時に lib フォルダに入っているライブラリをリンクします。

libwebp のインストール

フォルダの中の includelib(ついでに bin も)を /usr/local 以下にコピーすればインストールすることができます。

例えば tar コマンドで作成されたフォルダ(例えば “libwebp-1.0.0-mac-10.13” フォルダ)で下記コマンドを実行すればコピーできます。

sudo cp -r include/* /usr/local/include/
sudo cp lib/* /usr/local/lib
sudo cp bin/* /usr/local/bin

これでインストールは完了です。

インストールの確認

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

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

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

gcc japanese.c -o japanese.exe -lwebp

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

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

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

スポンサーリンク

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

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

ヘッダーファイル

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

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

ヘッダーファイル

mywebp.h
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

#include "webp/decode.h"
#include "webp/encode.h"

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

int webpFileReadDecode(BITMAPDATA_t *, const char*);
int webpFileEncodeWrite(BITMAPDATA_t *, const char*);
int freeBitmapData(BITMAPDATA_t *);

ポイントを説明します。

decode.hencode.h のインクルード

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

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

これらのヘッダーファイルはインストールを行ったフォルダの webp というフォルダ内にありますので、インクルードする時のヘッダーのパスは webp/decode.hwebp/encode.h と指定する必要があります。

ここまでの説明通りにインストールを行った場合、ヘッダーファイルは /usr/local/include/webp にあると思います。

decode.hとencode.hのインクルード
#include "webp/decode.h"
#include "webp/encode.h"

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

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

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

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

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

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

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

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

アルファチャネル付きのファイルの場合は各ピクセルが下図のように RGBα 順にデータが並んでいます。

ソースファイル

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

mywebp.c
#include "mywebp.h"

int webpFileReadDecode(BITMAPDATA_t *bitmapData, const char* filename){

  FILE *fi; /* 入力ファイルポインタ */
  VP8StatusCode ret; /* webp関数の戻り値格納 */
  WebPBitstreamFeatures features; /* 入力webpファイルの情報 */
  struct stat statinfo; /* ファイルサイズ取得用 */
  long fileSize; /* ファイルサイズ */
  uint8_t *data; /* 入力webpファイルデータ */
  uint8_t *decodedData; /* 入力webpデコードデータ */
  int width; /* webpデコードデータの幅 */
  int height; /* webpデコードデータの高さ */

  /* WebPファイルオープン */
  fi = fopen(filename, "rb");
  if(fi == NULL) {
    printf("%sは開けません\n", filename);
    return -1;
  }

  /* WebPファイルのサイズ取得 */
  if (stat(filename, &statinfo) != 0) {
    printf("%sのファイルサイズ取得に失敗しました\n", filename);
    fclose(fi);
    return -1;
  }
  fileSize = statinfo.st_size;

  /* WebPファイルのデータ格納用メモリ確保 */
  data = (uint8_t*)malloc(sizeof(uint8_t) * fileSize);
  if(data == NULL) {
    printf("malloc error\n");
    fclose(fi);
    return -1;
  }

  /* WebPファイル読み込み */
  fread(data, fileSize, 1, fi);
  fclose(fi);

  /* WebPデータの情報取得 */
  ret = WebPGetFeatures(data, fileSize, &features);
  if(ret != VP8_STATUS_OK){
    printf("WebPGetFeatures error\n");
    free(data);
    return -1;
  }

  /* アルファチャンネルでデコード方法を切り替えてデコード */
  if(features.has_alpha == 0){
    decodedData = WebPDecodeRGB(data, fileSize, &width, &height);
  } else {
    decodedData = WebPDecodeRGBA(data, fileSize, &width, &height);
  }
  free(data);

  /* BITMAPDATA_t構造体への情報セット */
  bitmapData->width = width;
  bitmapData->height = height;
  if(features.has_alpha == 0) {
    bitmapData->ch = 3;
  } else {
    bitmapData->ch = 4;
  }
  printf("width = %d, height = %d, ch = %d\n", bitmapData->width, bitmapData->height, bitmapData->ch);

  /* デコードデータ用メモリ確保 */
  bitmapData->data =
    (unsigned char*)malloc(sizeof(unsigned char) * bitmapData->width * bitmapData->height * bitmapData->ch);
  if(bitmapData->data == NULL){
    printf("data malloc error\n");
    WebPFree(decodedData);
    return -1;
  }

  /* BITMAPDATA_t構造体へデコードデータセット */
  memcpy(bitmapData->data, decodedData, bitmapData->height *bitmapData->width * bitmapData->ch);

  /* デコードデータ解放 */
  WebPFree(decodedData);

  fclose(fi);

  return 0;
}

int webpFileEncodeWrite(BITMAPDATA_t *bitmapData, const char *filename){
  FILE *fo; /* 出力ファイルポインタ */
  uint8_t *encodedData; /* WebPエンコードデータ */
  size_t encodedSize; /* エンコードサイズ */

  /* エンコード実行 */
  if(bitmapData->ch == 3) {
    encodedSize = WebPEncodeLosslessRGB(
      bitmapData->data,
      bitmapData->width,
      bitmapData->height,
      bitmapData->width * bitmapData->ch,
      &encodedData);
  } else if(bitmapData->ch == 4) {
    encodedSize = WebPEncodeLosslessRGBA(
      bitmapData->data,
      bitmapData->width,
      bitmapData->height,
      bitmapData->width * bitmapData->ch,
      &encodedData);
  }
  /* 出力ファイルオープン */
  fo = fopen(filename, "wb");
  if(fo == NULL){
    WebPFree(encodedData);
    printf("%sが開けません\n", filename);
    return -1;
  }

  /* ファイルへエンコードデータ書き出し */
  fwrite(encodedData, encodedSize, 1, fo);

  /* エンコードデータ解放 */
  WebPFree(encodedData);
  
  fclose(fo);
  return 0;
}


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

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

スポンサーリンク

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

webpFileReadDecode 関数は WebP ファイルを読み込む関数になります。

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

  • ファイルの読み込み
  • WebP ファイルの情報取得
  • デコード
  • 画像情報を BITMAPDATA_t へ格納
  • デコード結果格納用のメモリ確保
  • デコード結果をメモリへコピー
  • デコーダの終了

ここから各処理の詳細を解説していきます。

ファイルの読み込み

まずはファイルの読み込みを行っています。

ファイルサイズは stat 関数を用いて取得していますが、Windows 環境ではおそらく使えないので、代わりに GetFileSize 関数等を利用して取得すれば良いと思います。

WebP ファイルの情報取得

続いて読み込んだ WebP ファイルの情報を下記で取得しています。

WebPファイルの情報取得
  /* WebPデータの情報取得 */
  ret = WebPGetFeatures(data, fileSize, &features);

実行すると、第3引数の features に情報が格納されます。今回は読み込んだ WebP がアルファチャンネルを持つかどうかを判断するために features を使用します。 

デコード

続いて下記で実際に WebP のデコードを行っています。

decodedData にはデコードして得られた BITMAP データが格納されているアドレスが格納されます。

デコード
  /* アルファチャンネルでデコード方法を切り替えてデコード */
  if(features.has_alpha == 0){
    decodedData = WebPDecodeRGB(data, fileSize, &width, &height);
  } else {
    decodedData = WebPDecodeRGBA(data, fileSize, &width, &height);
  }

libwebp においては、WebP ファイルがアルファチャンネルを持っているかどうかでデコードに使用する関数が異なるため、上記のように features.has_alpha の値に応じて WebPDecodeRGBWebPDecodeRGBA を呼び分けています。

画像情報を BITMAPDATA_t へ格納

次に下記で画像の情報(幅・高さ・色数)を BITMAPDATA_tの各メンバに格納しています。

画像情報の格納
  /* BITMAPDATA_t構造体への情報セット */
  bitmapData->width = width;
  bitmapData->height = height;
  if(features.has_alpha == 0) {
    bitmapData->ch = 3;
  } else {
    bitmapData->ch = 4;
  }

デコード後の BITMAP データを使って画像処理等を行う際等には、この BITMAPDATA_t 構造体のメンバを参照して情報を取得します。

デコード結果格納用のメモリ確保

さらにデコード結果(BITMAP データ)を格納するためのメモリを下記で確保しています。

メモリの確保
  /* デコードデータ用メモリ確保 */
  bitmapData->data =
    (unsigned char*)malloc(sizeof(unsigned char) * bitmapData->width * bitmapData->height * bitmapData->ch);
MEMO

WebPDecodeRGBWebPDecodeRGBA 関数実行時にもメモリが確保されていますので、上記のメモリ確保や次に説明するデータコピーをスキップして、そのメモリをそのまま使用しても良いです

そのメモリのアドレスは decodedData に格納されています

デコード結果をメモリへコピー

次に先程確保したメモリにデコード結果(BITMAP データ)を下記でコピーします。

デコード結果のコピー
  /* BITMAPDATA_t構造体へデコードデータセット */
  memcpy(bitmapData->data, decodedData, bitmapData->height *bitmapData->width * bitmapData->ch);

これにより BITMAPDATA_t のメンバ data がデコード結果(BITMAP データ)を指すことになります。

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

webpFileEncodeWrite 関数は WebP ファイルを書き込む(保存する)関数になります。

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

  • エンコード
  • エンコード結果のファイル書き込み
  • エンコード結果のメモリ解放

ここから各処理の詳細を解説していきます。

エンコード

下記で BITMAP データを WebP にエンコードしています。

WebPEncodeLosslessRGBWebPEncodeLosslessRGBA は両方とも BITMAP データをWebP にエンコードする関数で、関数名に Lossless とついているように画質の劣化なしにエンコードをすることができます(つまり WebP 形式に可逆圧縮を行う関数)。

エンコード
  /* エンコード実行 */
  if(bitmapData->ch == 3) {
    encodedSize = WebPEncodeLosslessRGB(
      bitmapData->data,
      bitmapData->width,
      bitmapData->height,
      bitmapData->width * bitmapData->ch,
      &encodedData);
  } else if(bitmapData->ch == 4) {
    encodedSize = WebPEncodeLosslessRGBA(
      bitmapData->data,
      bitmapData->width,
      bitmapData->height,
      bitmapData->width * bitmapData->ch,
      &encodedData);
  }
[/coebox]

BITMAP データにアルファチャンネルがついていない場合は WebPEncodeLosslessRGB を、アルファチャンネルがついている場合は WebPEncodeLosslessRGBA を実行するように、BITMAPDATA_t 構造体の ch の値に応じてそれぞれの関数を呼び分けるようにしています。

WebPEncodeLosslessRGB 関数・WebPEncodeLosslessRGBA 関数ともに引数は下記のようになります。

  • 第1引数:BITMAP データが格納されているアドレス
  • 第2引数:画像の幅
  • 第3引数:画像の高さ
  • 第4引数:画像の1ライン分のバイト数
  • 第5引数:エンコード結果が格納されるアドレスを指すポインタ

戻り値は、エンコード結果のサイズ(バイト単位)になります。

引数の第5引数に指定するのは「アドレスを指すポインタ」なので、つまりは型としてはポインタのポインタになります。

例えば上記のように関数を実行すれば、encodeData にエンコード結果が格納されるアドレスが格納されることになります。

このエンコード結果が格納されているメモリは libwebp が確保してくれるメモリになり、エンコード結果が不要になった際には encodeData が指すメモリは WebPFree 関数で解放してやる必要があります。

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

続いてエンコード結果のファイル書き込みを行っています。

要はファイルを fopen で開いて、fwriteencodeData の指すメモリのデータを encodeSize 分書き込んでいるだけです(encodeSizeWebPEncodeLosslessRGBWebPEncodeLosslessRGBA の戻り値)。

エンコード結果のメモリ解放

エンコード結果のファイルへの書き込みが完了したら、エンコード結果が不要になりますのでエンコード結果が格納されているメモリを下記で解放しています。

[codebox title="メモリ解放"]
  /* エンコードデータ解放 */
  WebPFree(encodedData);

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

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

main.c
#include "mywebp.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(webpFileReadDecode(&bitmap, argv[1]) == -1){
    printf("webpFileReadDecode 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);

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

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

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

  freeBitmapData(&bitmap);

  return 0;
}

処理の流れ

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

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

画像処理

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

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

[codebox title="画像処理"]
  /* グレースケールに変換 */
  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;
      }
    }
  }

コンパイル

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

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

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

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

gcc main.c -c
gcc mywebp.c -c
gcc main.o mywebp.o -o webp.exe -lwebp
./webp.exe test.webp

コンパイル時に -lwebp を付けて libwebp にリンクすることを忘れないようにしましょう!

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

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

スポンサーリンク

WebP ファイルの入手

Web P読み込むプログラムを作ったけど、肝心の WebP ファイルがない・・・、という方はコチラの画像を使ってみてください。

Google Chromeだと画像は見れるはずです。画像が見れなくても右クリックすれば保存可能です。

libwepb が提供する関数

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

参考 WebP API Documentationgoogle developer
注意

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

libwebp を使用する方は自己責任でご使用ください

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

まとめ

このページでは次世代画像フォーマットの WebP を読み込み・書き込みするライブラリである libwebp のインストール方法やプログラム中での使用方法について解説しました。

今後 WebP の普及率が高まる可能性は高いと思いますので、WebP を使ってみたいという方は是非このページを参考にして libwebp をインストールしてみてください!

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

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