HEIC形式のファイルをJPGにまとめて変換するShellScript

# はじめに
本記事は、職業：旅人のプログラミングの超素人が、備忘のために記載した記事となります。

普段、旅をこよなく愛し、日本中、時には海外へと旅行へ出かける日々。
カメラも大好きなので、カメラをぶら下げ写真を撮り歩く。そして、blogで記事にしている。

少し前に、AndroidからiPhoneに機種変更をした。
iPhoneで撮影した写真も、使おうと思ったら、HEICという謎な拡張子に！

blogはASPを利用しているがHEIC形式だとアップロードができない。
手動で変更なんて出来ない！ということで、ShellScriptでなんとかしてみよう・・・。

# HEIC形式とは
ひとまず、今時っぽくChatGPTに聞いてみた。
要は、jpgより圧縮される画像形式で最近の携帯に使われているよ、ということですね。
>HEIC（High Efficiency Image Format）とは、静止画像を圧縮するためのファイルフォーマットです。HEICは、MPEG（Moving Picture Experts Group）によって開発されたもので、静止画像の圧縮に最適化されています。HEICは、JPEGに比べて高い圧縮率を実現でき、同じ画質の画像であってもファイルサイズが小さくなるという特徴があります。
>HEICは、iOS 11以降のiPhoneやiPadなどのデバイスで標準的に採用されています。また、Android 9 Pie以降のデバイスでもサポートされています。HEIC形式の画像を閲覧するには、HEIC形式に対応したビューアーやアプリが必要です。一方、HEIC形式に非対応の場合は、変換ツールを使ってJPEGなどの形式に変換する必要があります。

# 早速ShellScriptを作ってみる
1点、前提として「ImageMagick」が必要となります。
画像加工で過去に自分で調べてインストールはしているので、改めて別記事で記載するようにいたします。

VSCodeを使って書いていきます。また、PCはMacとなります。

まずは、Bashシェルスクリプトのお作法としての記載。
```
#!/bin/bash
```
正直、これってなんで書くんだろう。と素朴に思いましたので、調べました。（助けてChatGPT！）
これはBashシェルスクリプトを利用しているものなんだよ、ということを分かる様にしてるんですね。
つまり、Bashスクリプトを書く時は**絶対必要**ってことですね。
>#!/bin/bashは、Bashシェルスクリプトの最初の行に記述される特殊な行で、シェルがこのスクリプトを解釈する際に、どのプログラムを使用してスクリプトを実行するかを指定するためのもの。
>#!はシバン（shebang）と呼ばれる記号で、その後ろに続くパスは実行するプログラムのパスを指定します。この例では、/bin/bashというパスが指定されており、Bashシェルがスクリプトを実行するために使用されます。
>シバンを使用することで、シェルスクリプトを実行する際にbash <スクリプト名>のように明示的にBashシェルを指定する必要がなくなるため、スクリプトの実行が簡単になります。

次に今回のScriptで行う処理を書いていきます。今回行う処理は下記となります。
- 変換元のHEICファイルがあるフォルダを指定する
- 変換後のJPEGファイルを保存するフォルダを指定する
- 変換元のファイルを取得して、JPEGに変換する

では、まず最初は「**変換元のHEICファイルがあるフォルダを指定する**」です。
### 変換元のHEICファイルがあるフォルダを指定する
今回は、変換元の画像を格納するため、work用のディレクトリを準備して指定してみます。
「input_dir」という変数で、HEICファイルのあるディレクトリを指定します。ディレクトリは、それぞれ異なると思いますので、自身の指定したい場所となります。

ちなみにMacでディレクトリが分からない！という時は、ひとまずターミナルを開いて、対象のフォルダをドラッグすると、すぐにパスが表示されます。（もっとスマートなやり方があれば…教えてください！）
```
# 変換元のHEICファイルがあるフォルダを指定する
input_dir="/Users/YourName/Desktop/works"
```
これで「**変換元のHEICファイルがあるフォルダを指定する**」は完了です。
次は、「**変換後のJPEGファイルを保存するフォルダを指定する**」となります。

### 変換後のJPEGファイルを保存するフォルダを指定する
変換したファイルをどこにoutputするか、ですね。自分がわかりやすい場所に保存されれば良いかと思います。
変換前のフォルダの指定と同じようなやり方で、「output_dir」と変数をつけます。
```
# 変換後のJPEGファイルを保存するフォルダを指定する
output_dir="/Users/YourName/Desktop/complete"
```
これで「**変換後のJPEGファイルを保存するフォルダを指定する**」は完了です。
最後は、「**変換元のファイルを取得して、JPEGに変換する**」となります。

### 変換元のファイルを取得して、JPEGに変換する
さて、ここが今回の本題です。HEIC形式のファイルをJPEGに変換して出力をします。
今回やりたいことは、worksディレクトリの中のHEIC形式の画像を全て変換したい、となります。
よって、for文を利用して、フォルダ内の画像の数だけ処理を行うようにします。

for文の定義とは…からスタートとなります。
解説を読んでいくと、値リスト（つまり条件）にマッチするものの１つ目の値を変数に設定する。
doとdoneの間の処理を実行する。次は、２つ目にマッチしたものを変数に設定して処理を行う。これを、全ての値リスト（条件）を完了するまで繰り返す、ということになります。

つまり、今回であれば、変換元のHEICファイルの数だけ処理を繰り返すよ、ということになります。
```
for 変数 in 値リスト
do
  処理
done
```
参考/引用：UNIX&LINUXコマンド・シェルスクリプト リファレンス様
（https://shellscript.sunone.me/for.html）

上記を参考に早速、各処理の内容を入れていきます。

変数は「file」と指定してみます。

次は値リストの部分です。今回は、変換元フォルダのHEIC形式のファイルです。

変換元フォルダは、変数として「input_dir」を定義しています。その中の.HEICファイルです。
アスタリスク（＊）を使うことで、ファイル名のワイルドカードが使えるようです。
となると「$input_dir"/*.HEIC;」という形で定義をしてみます。
→ここで、*.HEICの後のセミコロン（；）をつけていないことでエラーが出ました。。おまけに記載しています。

これで値リストが出来上がりました。続いては処理部分です。
output用のディレクトリに、ファイル名を「hogehoge.HEIC」から「hogehoge.jpg」に変更する。
そして、画像のフォーマットをHEICからJPGに変換します。

まず、outputのファイルの指定です。
この後の変換で必要なので、「output_file」という変数の定義を行っておきます。
「output_file」は、既に定義済みの「output_dir」に格納します。
そしてファイル名も色々と調べていたところ、「basenameコマンド」というのを使えば、拡張子など除外したファイル名を取得できるようです。HEICを除外して、jpgをつけてあげれば、どうにかなりそうですね！
for文の最初に、「file」と変数を定義しています。この変数はHEICのファイルたちです。
ここから「basenameコマンド」で.HEICを除外してみます。最後にjpgと名前をつけておきます。下記となりました！
```
# 変換元のファイルを取得して、JPEGに変換する
for file in "$input_dir"/*.HEIC; do
    output_file="$output_dir/$(basename "$file" .HEIC).jpg"
```
参考：Linuxコマンド.net様
（https://linuxcommand.net/basename/）

最後に「ImageMagick」を使ったフォーマット変換となります。
「ImageMagick」をインストール済みであれば、下記のコマンドで変換可能です。
>convert <変換前の画像名> <変換後の画像名>
>今回の例）convert hogehoge.HEIC hogehoge.jpg

上記のように「convert」コマンドを利用していきます。
HEIC、JPG共に変数を定義しておりますので、それを記載すると下記です。
```
# 変換元のファイルを取得して、JPEGに変換する
for file in "$input_dir"/*.HEIC; do
    output_file="$output_dir/$(basename "$file" .HEIC).jpg"
    convert "$file" "$output_file"
done
```
参考：imagemagickの使い方日本語マニュアル様
（https://imagemagick.biz/archives/151）

これで処理部分も完成です。ShellScriptとして保存をして、実際に実行となります！

# 出来上がったShellScript

まず、今回書いたShellScriptは下記です。
```heic_to_jpg.sh
#!/bin/bash

# 変換元のHEICファイルがあるフォルダを指定する
input_dir="/Users/YourName/Desktop/works"

# 変換後のJPEGファイルを保存するフォルダを指定する
output_dir="/Users/YourName/Desktop/complete"

# 変換元のファイルを取得して、JPEGに変換する
for file in "$input_dir"/*.HEIC; do
    output_file="$output_dir/$(basename "$file" .HEIC).jpg"
    convert "$file" "$output_file"
done
```

worksフォルダに、変換したい画像を格納します。
あとは、Macでターミナルを起動して、いざ実行。
```
$sh heic_to_jpg.sh
```
completeフォルダに続々と変換されたファイルが出来上がっていきました。成功です！
これで、今後も楽に変換して、アップロード出来ます！

# おまけ
### ハマったこと１
最初実行すると、下記のエラーが出ました。予期しない文字列がいるよ！ということで出るそうな。
>/Users/YourName/Desktop/heic_to_jpg.sh: line 11: syntax error near unexpected token `output_file="$output_dir/$(basename "$file" .HEIC).jpg"'
/Users/YourName/Desktop/heic_to_jpg.sh: line 11: `    output_file="$output_dir/$(basename "$file" .HEIC).jpg"'

結論からいくと、.HEICとdoの後にセミコロン（；）が必要でした。
doの行を変えていれば無しでも良かったみたいですが、ここでセミコロンがあるとないで、リストの終わりが分かんねーよということと理解しました。
せっかくハマったので、今回は、あえて行を変えずに行っちゃおうと思います。

```heic_to_jpg.sh（修正前：エラー出ます）
#!/bin/bash

# 変換元のHEICファイルがあるフォルダを指定する
input_dir="/Users/YourName/Desktop/works"

# 変換後のJPEGファイルを保存するフォルダを指定する
output_dir="/Users/YourName/Desktop/complete"

# 変換元のファイルを取得して、JPEGに変換する
for file in "$input_dir"/*.HEIC do
    output_file="$output_dir/$(basename "$file" .HEIC).jpg"
    convert "$file" "$output_file"
done
```

### ハマったこと２
最初試した時は、画像の拡張子が大文字なことを忘れ、小文字で.heicと記載。
出力されたファイルが「picture.heic.jpg」というカッコ悪いことになって、やり直したのは内緒です…。
今後、どちらの拡張子でもいけるように調べてみようと思います。