【画像処理】今さら聞けない画像フォーマット

普段なんとなく使用している画像について少し詳しくなりたい方に向けての解説記事です。
様々な画像の構造を理解することで、適切な用途や注意点が分かります。

# 画像フォーマットとは
画像フォーマットとは画像データを扱うための形式・規格のことです。この規格に合わせてデータを作成することで様々なアプリケーションで広く扱うことが可能になっています。

# ラスタ形式とベクタ形式
画像は`ラスタ形式`と`ベクタ形式`の2種類に大きく分けられます。これらの違いは、データの持ち方にあります。

https://qiita.com/spc_ehara/items/fe05049f5a6bd67c48f3#%E3%83%93%E3%83%83%E3%83%88%E3%83%9E%E3%83%83%E3%83%97%E7%94%BB%E5%83%8F%E3%81%A8%E3%83%99%E3%82%AF%E3%82%BF%E7%94%BB%E5%83%8F

## ラスタ形式
ラスタ形式は、画像の情報を`ピクセル単位(画素)`で保持しています。
例えば、ラスタ形式である「1.1 入力画像」の一部分を拡大してみると、「1.2 拡大画像」には小さなピクセルが集まっているのが見えると思います。

| 1.1 入力画像 | 1.2 拡大画像 |
|:------:|:-----:|
| <img width="512" alt="expantion.png" src="https://user-images.githubusercontent.com/74283288/102318391-991abd00-3fbc-11eb-9bb5-619774ebe379.png"> | <img width="512" alt="expantion.png" src="https://user-images.githubusercontent.com/74283288/102320993-6bd00e00-3fc0-11eb-81b7-5bc50b582b6d.png"> |

このようにラスタ形式の画像は小さなピクセルが数多く集まって構成されています。
具体的には図1のように、
| 図1 |
|:--:|
| ![example_bitmap.png](https://user-images.githubusercontent.com/74283288/102456107-07768280-4084-11eb-882e-410275a0dc35.png) |
* 「x=2, y=2の位置にあるピクセルはR:120,G:60,B:100」
* 「x=4, y=4の位置にあるピクセルはR:200,G:230,B:230」

といった位置情報と色情報の集まりを画像を構成するデータとして保持しています。

このような特徴から色鮮やかな写真など、描画が複雑な画像に対しても細やかな表現が可能です。
画像処理・分析で使用される画像はほぼ全てラスタ形式で、この記事で解説する画像フォーマットに関してもすべてこの形式になります。


## ベクタ形式
ベクタ形式は、画像の情報を`点とその点同士をつなぐ数式`として保持しています。
例えば、ベクタ形式である「2.1 入力画像」の一部分を拡大してみると、「2.2 拡大画像」には小さなピクセルの集まりは見えず、滑らかな画像になっているかと思います。

| 2.1 入力画像 | 2.2 拡大画像 |
|:------:|:-----:|
| ![org.svg](https://user-images.githubusercontent.com/74283288/102459777-64286c00-4089-11eb-975b-e853a036b082.png) | ![expantion.svg](https://user-images.githubusercontent.com/74283288/102459783-668ac600-4089-11eb-8802-de218eaa43ff.png) |

このようにベクタ形式の画像は、描画毎に数式を再計算することで滑らかな画像表示が可能となっています。
具体的には、「2.1 入力画像」の中身は以下のようなコード(sample.svg)になっています。

```XML:sample.svg
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg">
  <!-- 四角形(rect)の位置・大きさ・塗りつぶし色を定義 -->
  <rect x="100" y="100" width="500" height="300" fill="#7BB590" />

  <!-- 円形(circle)の位置・大きさ・線の色・塗りつぶし色を定義 -->
  <circle cx="350" cy="250" r="150" stroke="black" fill="#238447" stroke-width="2" />
</svg>
```

図形の種類(rect, circle等)とその位置・大きさ・塗りつぶし色を数式で定義しているだけということがわかると思います。

このような特徴から拡大・縮小時に画像がぼやけたり、劣化することがありません。
そのためロゴやイラストなど大きさを頻繫に変更する用途がある画像によく使用されています。

# 画像フォーマットの種類
画像フォーマットには数多くの種類があり、ラスタ形式とベクタ形式あわせて100種類近くあります。

https://en.wikipedia.org/wiki/Image_file_format

この記事では、その中でも普段よく目にするフォーマットのみを取り上げて解説します。

|フォーマット|よく言われている特徴|圧縮形式|品質|
|:---:|:---:|:---:|:---:|
|JPG|写真に適したフォーマットで、ファイルサイズを小さくできる。ただし、何度も編集・保存すると画像が劣化してしまう。|非可逆圧縮|劣化する|
|PNG|可逆圧縮のため画像が劣化しない。ただし、JPGと比べて写真などのファイルサイズが大きくなってしまう。|可逆圧縮|劣化しない|
|BMP|最も古い画像形式で、圧縮しないので劣化しない。ただし、他のフォーマットと比べてファイルサイズが大きくなってしまう。|圧縮しない|劣化しない|

画像は基本的に以下のようなデータ構造を持っています。(もちろん細かい部分はフォーマットごとに異なる)

![スクリーンショット 2022-09-20 122900.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866058/858bcfbc-6966-378f-6c79-a208b33a47d6.png)


この基本構造さえ押さえておけば、どのフォーマットの理解にも役立ちます。

### 画像品質と圧縮率について
画像品質と圧縮率はトレードオフの関係にあります。
高い圧縮率の実現は、画像品質が犠牲になります。
また、高品質な画像を保持すると、高い圧縮率は実現できません。

![スクリーンショット 2022-09-20 122939.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866058/ed06530a-f034-07d2-4527-dbbaa880fccc.png)



## JPG
JPGの基本的な考え方は「見た目に影響を与えないように情報量を減らす」ことです。ただし、そこはトレードオフのため、あくまで「品質を多少犠牲にしてでも圧縮したい」という姿勢で作られたフォーマットと言えます。

JPGは圧縮にあたって人間の眼の2つの性質に着目しました。

1. 明るさの変化に敏感である一方、色の変化には鈍感である
1. 図柄の緩やかな変化には敏感である一方、細かく急激な変化には鈍感である

これらの性質を踏まえて、以下の圧縮方法を採用しています。

1. 明るさを変えずに色の変化情報のみを減らす
![スクリーンショット 2022-09-20 123208.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866058/cf565515-e0f3-26ef-f405-88b7b8e82da2.png)

1. 図柄の緩やかな変化を変えずに細かく急激な変化を減らす
![スクリーンショット 2022-09-20 123234.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866058/c5efc020-3cc0-053b-6519-70930735ccf3.png)


この2種類のデータ削減方法を組み合わせることで高い圧縮率を実現しているのです。



https://qiita.com/spc_ehara/items/87d383a59a37a2c82531

もっと詳しく知りたい方はこちら↑

## PNG
PNGの基本的な考え方は「品質を落とさないようにファイルサイズを小さくする」ことです。ただし、そこはトレードオフのため、あくまで「ファイルサイズが多少大きくても品質を落としたくない」という姿勢で作られたフォーマットと言えます。

PNGは歴史的な観点から特許の問題が発生しない圧縮アルゴリズムを採用する必要があったため、今では広く使われている[Deflate](https://ja.wikipedia.org/wiki/Deflate)という圧縮アルゴリズムを採用しています。
これは、同じ並びの文字列が複数回出現した場合、2回目以降の文字列を短い符号に置き換えるというアルゴリズム（[LZ77](https://ja.wikipedia.org/wiki/LZ77)）で、これに[ハフマン符号](https://ja.wikipedia.org/wiki/%E3%83%8F%E3%83%95%E3%83%9E%E3%83%B3%E7%AC%A6%E5%8F%B7)を組み合わせたアルゴリズムとなっています。

#### LZ77
![スクリーンショット 2022-09-20 123735.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866058/f783033f-147f-d435-c843-ee44a98c56fc.png)

#### ハフマン符号化
![スクリーンショット 2022-09-20 123800.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866058/5e5c200c-1fb2-514b-f0c6-5c2fc0c68e55.png)


PNGはこのDeflate圧縮方式を採用することで、品質を落とさずに情報量を減らすことが可能となっているのです。

https://qiita.com/spc_ehara/items/c748ec636283df805926

もっと詳しく知りたい方はこちら↑

## BMP
BMPの基本的な考え方は「シンプルで汎用性が高い構造である」ということです。​
BMPは様々なデバイスで画像を表示するためにwindowsOS用に開発されたファイルフォーマットです。​
基本的に無圧縮で構造がシンプルであるため、画像フォーマットのひな形としてとても参考になりますし、多くの画像フォーマットのもとになっています。​

圧縮方式についてですが、基本的に無圧縮であるため、エンコード・デコードがとても短いです。これが利点と言えます。​
また、特殊な条件下であればシンプルな構造を最大限利用することで、ほかのフォーマットと比較してメリットになる部分もあります。

https://qiita.com/spc_ehara/items/03d179f4901faeadb184

もっと詳しく知りたい方はこちら↑

# フォーマット選択基準（JPG、PNG、BMP）
目的別にフォーマットを選んでいく基準を示します。

## 1. とにかくファイルサイズを小さくしたい（品質劣化を許容できる）
基本的にファイルサイズを小さくしたい場合は「JPG」を選択すれば問題ありません。ただし、イラストのような単純な画像の場合はPNGのDeflate圧縮方式がうまくいき、結果として「JPG」よりも「PNG」のほうが小さくなることがあります。
したがって、基本的に「JPG」を使用して、単純でDeflate圧縮方式が得意な画像であれば「PNG」を採用するといった方法が一番メリットを受けられます。

## 2. 絶対に品質を落としたくない（圧縮は最低限でOK）
「PNG」一択となります。無圧縮の「BMP」もありますが、少しでも圧縮があったほうが便利なので、基本的に「PNG」一択かと思います。​

## 3. エンコード・デコードの速度を最速にしたい
「BMP」を使うメリットがあります。
圧縮処理がある画像は通常、読込時・書込時にエンコード・デコードといった処理が必要になります。
つまり、圧縮されたデータから元データに戻して、処理が終わったら再度圧縮するといった処理のことです。
「BMP」にはそのような処理がないため、HDDやSSDの読込・書込速度（シーケンシャルアクセス）が十分に速い場合は「JPG」や「PNG」に比べてトータルの処理時間は短くなります。
そのような特殊な条件下で、さらに画像フォーマットまでこだわって高速化をしたい等の事情があれば「BMP」を採用するメリットがあります。

# 検証

以下の2つの画像をJPG、PNG、BMP形式で保存して比較することで、上記で述べた選択基準が本当に有用であるかを検証しました。

| 1 | 2 |
|:-:|:-:|
|![](https://user-images.githubusercontent.com/74283288/102950221-8731a080-450d-11eb-8fb8-eb49dc4f9502.png)富士山がきれいに見える色鮮やかな画像|![](https://user-images.githubusercontent.com/74283288/102950217-8567dd00-450d-11eb-8d0e-992a5c26cdcf.png)簡単な図形が描画された画像|

## 検証結果

|画像番号|画像サイズ|JPG|PNG|BMP|
|:-:|:-:|:-:|:-:|:-:|
| 1 |2500*1500|1.07MB|8.15MB|10.7MB|
| 2 |2500*1500|193KB|52.8KB|10.7MB|


#### 1. とにかくファイルサイズを小さくしたい（品質劣化を許容できる）
画像番号１のような写真の場合、検証結果から「PNG」よりも「JPG」のほうが約8倍も効率的に圧縮できることがわかります。
一方で、画像番号２のようなイラストの場合、検証結果から「JPG」より「PNG」のほうが3.7倍も効率的に圧縮することができています。
上記結果より、品質劣化を許容できる場合においてもファイルサイズを効率的に削減するためには画像の種類によってフォーマットを都度選択していくことが大切であることがわかります。

#### 2. 絶対に品質を落としたくない（圧縮は最低限でOK）
品質劣化を許容できない場合、「JPG」という選択はなくなります。残った「PNG」と「BMP」ですが、少しでも圧縮をするために「PNG」一択となります。
画像番号１の場合は「PNG」と「BMP」で2MBの差があります。また、画像番号２の場合、その差は10MB以上になります。
上記結果より、品質劣化を許容できない場合は「PNG」を選ぶことで品質を保ちつつ、Deflate圧縮方式が得意な画像であれば効率的にサイズ削減効果も得ることができます。

#### 3. エンコード・デコードの速度を最速にしたい
画像番号１，２どちらも最もファイルサイズの大きいフォーマットは「BMP」でした。圧縮をしないため画像の種類に関わらず、画像サイズが同じであればファイルサイズも同じになります。
ファイルの読込・書込時間の構成は、「（シーケンシャルアクセスタイム）＋（エンコード・デコード）＋（その他・オーバーヘッド）」となります。
シーケンシャルアクセスタイムとは、HDDやSSDから読込や書込をする時間のことで、エンコード・デコードとはデータの圧縮や展開のことです。
つまり、ファイルサイズが小さいほどシーケンシャルアクセスタイムが小さくなり、圧縮がなければエンコード・デコードの時間がなくなります。
結果として、シーケンシャルアクセスタイムが小さい場合は「BMP」を採用することで合計処理時間が短くなる可能性があるということになります。

# 最適化
フォーマットを変更せず、かつ品質も落とさずにファイルサイズを削減する方法がこの世界には存在します。
それが「最適化」と呼ばれる処理になります。

最適化とは、ファイルサイズを削減するために最適なフォーマット構成になるようデータに変更を加えることです。
どのような構成が最もファイルサイズを削減できるかを計算する処理があるため、時間がかかり、実装も手間なため、通常のアプリケーションで画像を保存する場合は行われないことがほとんどです。
したがって、通常保存されている画像に対して最適化を行うことで効率的なデータ格納となり、結果として更なるサイズ削減が可能となります。

最適化の大まかな手順は以下の通りです。
![スクリーンショット 2022-09-20 124426.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866058/45b5a09c-a0cd-8d88-a130-f116c998da44.png)
①整理されていない不揃いなデータ（通常アプリケーションで保存するとこんな感じ）
②それらのデータの大きさや形を一つ一つ確認して、適切な箱に再度詰め込んだりしながら、整理整頓をします。​
③適切な構成になったデータのかたまりを画像データとして再度保存する​


## 検証
上記ファイルに対して、最適化処理を行うことでどのくらい圧縮が可能になるかを検証しました。
今回検証にあたって、オンラインで簡単に最適化処理を行うことができるOptimizillaを使用しました。ただし、「BMP」は未対応だったため「JPG」と「PNG」のみの検証となります。

https://imagecompressor.com/ja/


|最適化前（JPG）|最適化後（JPG）|
|:-:|:-:|
|![mtFuji.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866058/091a027a-e9b1-6020-df71-acc23d5484ce.jpeg)サイズ：1.07MB|![mtFuji-min.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866058/312ccbb7-a482-11da-8fe1-7b98741f77d3.jpeg)サイズ：332KB|

|最適化前（PNG）|最適化後（PNG）|
|:-:|:-:|
|![mtFuji.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866058/7073db62-36ff-2726-1dbb-a4cdcd5a872d.png)サイズ：8.15MB|![mtFuji-min.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866058/cfb2c9b4-bd26-0704-6074-ff42aaa6c350.png)サイズ：1.4MB|

|最適化前（JPG）|最適化後（JPG）|
|:-:|:-:|
|![figure.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866058/ae8f257e-400d-58ec-7b8c-7956cfbc3baa.jpeg)サイズ：193KB|![figure-min.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866058/c3048d2a-4543-295e-e6f1-b4b2b20e160a.jpeg)サイズ：63.9KB|

|最適化前（PNG）|最適化後（PNG）|
|:-:|:-:|
|![figure.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866058/f7f01d8a-8310-dd1c-5041-1fa946d20fa2.png)サイズ：52.8KB|![figure-min.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866058/fa60ae26-828a-ff19-8ccb-8fee30c14acc.png)サイズ：14.7KB|

![スクリーンショット 2022-09-20 124647.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866058/6712afbe-8100-9b82-9e18-e6e4f1f771b2.png)


最適化前のファイルサイズと比較すると大幅に削減できていることが確認できるかと思います。それに加えて、「JPG」は画像の劣化も目で確認できないくらい少ないことがわかります。
また、最適化を行ったとしてもフォーマットごとの得意不得意が変わるわけではないため、フォーマット間のファイルサイズの大小が変わることはないです。
したがって、適切なフォーマット選択に加えて、最適化を行うことが効率的なファイルサイズ削減には大切であることがわかるかと思います。

# さいごに
今回は、「今さら聞けない画像フォーマット」として簡単に画像フォーマットについて解説しました。

目次は以下の記事からご覧になれます。

https://qiita.com/spc_ehara/items/e425b6dcc0398299c40d#%E7%9B%AE%E6%AC%A1