PHPのImageMagickでHEICの画像ファイルをJPEGに変換する方法!

PHP

ImageMagickを使用してHEICの画像ファイルをJPEGの画像ファイルに変換しました。

HEICはiOS11から使用されている画像の形式で、デフォルトでこの保存方法になっています。
2021年現在、ブラウザではHEICファイルに対応していないため、表示することができません。

スマホから画像をアップロードするような処理を書く場合で、画像を画面に表示するような処理がある時には必須になりそうだと思いました。
ですが、iOS14の端末で確認したところ、inputタグを使っている場合はiPhoneからアップロードするときにjpeg画像になっているようです。
このため、気にしておくことは、パソコンからHEICの画像をどう扱うかということだけで良さそうです。

今回はImageMagickを使用して、HEICファイルからJPEGファイルにファイルを変換します。

Dockerを使用して、PHP8で検証しています。

ImageMagickをDockerに導入する

今回はPHPのコンテナを作るために、Dockerfileを作成しています。
DockerfileにImageMagickで使用するライブラリのインストール処理とlibheif・ImageMagickのビルドをする設定を書いてみました。

FROM php:fpm
# --- 省略 ---
# php module
RUN apt-get update && apt-get install -y \
   zlib1g-dev \
   git \
   libzip* \
   libonig-dev \
   libc-client-dev \
   libjpeg-dev \ 
   libpng-dev \
   libwebp-dev \
   libopenjp2-7-dev \ 
   librsvg2-dev \
   libde265-dev  \
   libkrb5-dev \
   libxml2-dev 

RUN rm -r /var/lib/apt/lists/*

RUN PHP_OPENSSL=yes docker-php-ext-configure imap --with-kerberos --with-imap-ssl \
    && docker-php-ext-install zip pdo_mysql mysqli mbstring imap soap

# composer install
COPY --from=composer /usr/bin/composer /usr/bin/composer

# ---- ここでlibheifのビルドとインストール ---
WORKDIR /etc
RUN git clone https://github.com/strukturag/libheif.git
WORKDIR /etc/libheif 
RUN ./autogen.sh && ./configure && make && make install

# ---- ここでImageMagickのビルドとインストール ---
WORKDIR /etc
RUN curl -OL https://github.com/ImageMagick/ImageMagick/archive/refs/tags/7.1.0-10.zip && unzip 7.1.0-10.zip
WORKDIR /etc/ImageMagick-7.1.0-10 
RUN ./configure && make && make install && ldconfig /user/local/lib

# ---- imagickをPHPの拡張機能として使えるようにする ---
RUN pecl install imagick && docker-php-ext-enable imagick

解説

ImageMagickでJPEGやPNGを使えるように下記のようにライブラリのインストールを書きました。
必要なライブラリがない場合は、ImageMagickでフォーマットとして使えない形式になるようです。

RUN apt-get update && apt-get install -y \
   # --- 省略 ---
   libjpeg-dev \ 
   libpng-dev \
   libwebp-dev \
   libopenjp2-7-dev \ 
   librsvg2-dev 

下記では、libheifというライブラリをビルドして、インストールしています。
libheifはImageMagicでHEIC画像のフォーマットを扱うために必要です。

WORKDIR /etc
RUN git clone https://github.com/strukturag/libheif.git
WORKDIR /etc/libheif 
RUN ./autogen.sh && ./configure && make && make install

作業するディレクトリを/etcで行って、ライブラリをgit cloneで落としてきます。
落としてきた後に/etc/libheifで作業ディレクトリを切り替えて、ビルドしてインストールしている形になります。

ImageMagick本体も、gitのほうで落としてきました。

WORKDIR /etc
RUN curl -OL https://github.com/ImageMagick/ImageMagick/archive/refs/tags/7.1.0-10.zip && unzip 7.1.0-10.zip
WORKDIR /etc/ImageMagick-7.1.0-10 
RUN ./configure && make && make install && ldconfig /user/local/lib

こちらの方はリリースされた(バージョンが決まっている)ものをzip形式で、curlコマンドを使用してダウンロードしました。
libheifと同じように/etcにダウンロードした後に、解答したImageMagickのディレクトリに移動します。
その後に、ビルドしてインストールしている形になります。

ldconfigは共有ライブラリの依存関係情報を更新するコマンドらしいです。
多分インストールした各フォーマットのライブラリとの関連づけを行っているのではないでしょうか。

Dockerfileの全文については、Githubの下記にアップしています。
https://github.com/YasuakiHirano/codelike_web_docker/blob/ImageMagick/.codelike_web_docker/php/Dockerfile

convert(ImageMagick)コマンドを使用して、変換できるかテストする

実際にビルドが通ってインストールが完了したら、convertコマンドが使えるようになっています。
convert -versionでバージョンや使用できるフォーマットが確認できます。

# convert -version 
Version: ImageMagick 7.1.0-10 Q16-HDRI x86_64 2021-10-05 https://imagemagick.org
Copyright: (C) 1999-2021 ImageMagick Studio LLC
License: https://imagemagick.org/script/license.php
Features: Cipher DPC HDRI OpenMP(4.5) 
Delegates (built-in): fontconfig freetype heic jng jp2 jpeg png webp x xml zip zlib
Compiler: gcc (10.2)

Delegatesの箇所を見ると、heicとjpegが使用できるようになっていることが確認できました。
ここにheicやjpegなど、使用したいフォーマットがないと使ったときにエラーになるので、注意です。

convertコマンドを使用して、heicからjpegにファイルを変換するには下記のようにします。

# convert test.HEIC test.jpg

第1引数に変換したいHEICファイルを指定して、第2引数に変換後の名前を拡張子をつけて指定します。
拡張子を見て動いてくれるようで、これでコマンドによる変換が可能です。

ImageMagickがPHPで使用できるようになっているか確認する

おなじみのphpinfo関数を実行すると、ImageMagickの項目があります。

PHPで使うときにはImagickという名前で使用します。
下記のようにImagickの項目が存在して、「ImageMagick supported formats」にHEICやJPEGなど、使いたいフォーマットが書いてあれば準備完了です。

PHPでImageMagickが使えるか確認する

ImageMagickを使用して、HEICからJPEG変換するサンプルコード

実際にHEICファイルをJPEGファイルに変換するサンプルコードを書いてみました。

HTMLファイル

HTMLファイルを下記のように作成しました。
ファイル名はfile_upload.htmlという名前にしました。

<html lang="ja">
  <head>
    <title>heic upload test</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
  </head>
  <body>
    <div class="mt-3">
      <form method="post" enctype="multipart/form-data" action="file_upload.php" class="d-flex">
        <input type="file" name="file" class="ml-5 mr-3 mt-2" />
        <input type="submit" value="送信" class="btn btn-primary pl-3 pr-3" />
      </form>
    </div>
  </body>
</html>

シンプルにファイルだけ指定して送信するフォームです。
POST形式で、file_upload.phpに対して、リクエストを投げます。
formタグのenctype属性として”multipart/form-data”を指定しないとファイルが送られないので、注意が必要です。

PHPファイル

実際にアップロードするサーバーサイドの処理を下記のように作成しました。
ファイル名はfile_upload.phpという名前にしました。

<?php
// uploadするディレクトリ
$uploadDir = './';

$fileName = $_FILES['file']['name'];
$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
$filePath = $_FILES['file']['tmp_name'];

try {
  if (isset($fileName)) {
    $uploadFile = $uploadDir . basename($fileName);

    if ($fileExtension == 'heic') {
      // heicファイルの場合はjpegに変換する
      $imagick = new \Imagick();
      $imagick->readImage($filePath);
      $imagick->setImageFormat('jpg');
      $imagick->writeImage($filePath);

      $imagick->clear();
      $imagick->destroy();

      $uploadFile = $uploadDir . $fileName = preg_replace('/\.[^.]*$/', '', $fileName) . '.jpg';
    }

    if (move_uploaded_file($filePath, $uploadFile)) {
      echo "successfully uploaded.<br>";
    } else {
      echo "upload faild<br>";
    }
  }
} catch (\Throwable $e) {
  var_dump($e);
}

アップロードされたファイルは$_FILESという特別なグローバル変数で取得することが可能です。
$_FILES['file']['name']でアップロードされたファイルの名前を取得します。
strtolower(pathinfo($fileName, PATHINFO_EXTENSION))でファイルの拡張子を小文字形式で取得します。

$_FILES['file']['tmp_name']でファイルのパスを取得します。
ファイルは送信されたときにPHPの一時ディレクトリに保持されています。そのパスです。

if (isset($fileName))で、ファイルがあった場合は、処理をするようにしています。

実際に変換処理をしている箇所は下記のようになっています。

if ($fileExtension == 'heic') {
  // heicファイルの場合はjpegに変換する
  $imagick = new \Imagick();
  $imagick->readImage($filePath);
  $imagick->setImageFormat('jpg');
  $imagick->writeImage($filePath);

  $imagick->clear();
  $imagick->destroy();

  $uploadFile = $uploadDir . $fileName = preg_replace('/\.[^.]*$/', '', $fileName) . '.jpg';
}

拡張子がHEICのファイルだったら変換するようにしています。

ImageMagickはImagickをnewして使います。
readImage関数にファイルパスを入れてあげると読み込みます。
setImageFormat関数で変換後のファイルフォーマットを指定します。
writeImage関数で、引数で渡したファイルパスに画像ファイルを書き込みます。
今回は同じファイルパスを指定しているので、HEICで読み込んで、同じパスにJPEGで出力する感じです。

clear関数で関連づいているリソースをクリアして、destroy関数でnewしたImagickオブジェクトを破棄しています。
その後に、実際にアップロードするパスをuploadFileという変数名で作成しています。

最後にmove_uploaded_file関数を使用して、ファイルを任意の場所に移動して処理完了です。
move_uploaded_file関数は指定したファイルの移動が成功すると、trueで失敗するとfalseが返ってきます。

動作確認する

 ローカルの/file_upload.htmlにアクセスすると、下記のように作成したフォームが表示されます。
PHPでHEICファイル変換サンプルフォーム

HEICファイルを選択します。
PHPでHEICファイルを選択する

選択すると、下記のようになるので、送信ボタンを押します。
HEICファイル変換サンプルフォームでの選択後

送信すると下記のようにファイルの変換処理がうまくいって、メッセージが表示されました。
PHPのHEICファイル変換フォームで送信後の画面表示

実際に変換後のディレクトリを見てみると、指定したHEICファイルと一緒にJPEGファイルができていることが確認できました。
PHPでHEICからJPEGに変換されたファイルを確認

まとめ

今回はDockerfileを使用して、コンテナにHEICが使えるImageMagickを導入しました。
その後に実際にPHPを使用して、HEICファイルをJPEGファイルに変換してみました。

実際に変換処理を書いてみると、Imagick自体の使い方は難しくなく、ImageMagick本体の導入が大変だなぁ…と思いました。
LaravelでもImageMagickを使って、変換処理をやってみましたが、同じようにImagickをnewして変換するだけでした。

コメント

タイトルとURLをコピーしました