python3でpillowを使って文字入り画像を生成する(ついでにbase64化された別の画像も重ねる)

#環境

 - CentOS Linux release 7.0.1406 (Core)
 - Python 3.6.2
 - pip 10.0.1
 - 今日の日付 2018年7月29日


(一部、VPSサーバーではなくローカルで表示したエラーメッセージをコピペしたためにバージョンがズレている箇所がありますが、大勢に影響はありません。)

#目標

まず目標を整理します。

- VPSサーバー(CentOS7)が動いているサーバー上で
- python3で
- ある画像の上に別の画像(base64化されたもの)と文字を合成した画像を生成する

base64化された画像を使うのは、Web上でHTML5のcanvas要素からtoDataURLで生成した画像を使って何かする、という想定です。

#ステップ

- 何でも良いから画像を生成する
- 何でも良いから文字の入った画像を生成する
- 日本語の文字の入った画像を生成する
- ある画像にbase64の画像と文字の入った画像を生成する

こんな感じのステップバイステップでいきましょう。

公式ドキュメントはこちら。なんだかんだ一番参考になります。
https://pillow.readthedocs.io/en/5.2.x/index.html

#何でも良いから画像を生成する

今回はPillowというライブラリを使います。調べた感じ、これが今日(2018-07-22)現在のデファクトスタンダードっぽい。

```
pip install Pillow
```

場合によってはルート権限のあるアカウントで実行するか`sudo`をつけて下さい。

(pipでエラーが出た場合は、僕の[前回の記事](https://qiita.com/agajo/items/73d6f14855da703972e0)が参考になるかもしれません。)

pillowのインストールが済んだら、次のpythonプログラムを実行します。

```python
from PIL import Image

im = Image.new("RGB",(300,300),"red")
im.save("./test.jpg")
```
Imageインスタンスを作成して、そのsaveメソッドを呼び出してるだけです。2行。

これで、`test.jpg`という赤一色の画像ファイルが生成されました。メッチャ簡単じゃない？？？すご。。。。。

各メソッドの引数の意味などは、各々[公式リファレンス](https://pillow.readthedocs.io/en/5.2.x/index.html)で確認お願いします。

#文字の入った画像を生成する

```python
from PIL import Image, ImageDraw

im = Image.new("RGB",(300,300),"blue")# Imageインスタンスを作る
draw = ImageDraw.Draw(im)# im上のImageDrawインスタンスを作る
draw.text((0,0),"hogehoge")
im.save("./test.jpg")
```

ここから段々ややこしくなりますね。

Imageインスタンスの上に、ImageDrawインスタンスを作り、そこで文字などの操作を行うようです。

とりあえずこれを実行すると、左上に"hogehoge"と文字の入った画像が生成されます。文字色とかフォントとかは全て初期値。

ところが。これを、文字列の部分だけ変えて

```python
from PIL import Image, ImageDraw

im = Image.new("RGB",(300,300),"blue")# Imageインスタンスを作る
draw = ImageDraw.Draw(im)# im上のImageDrawインスタンスを作る
draw.text((0,0),"日本語の文字だよ")
im.save("./test.jpg")
```

とすると、以下のエラーがでます。

```
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/PIL/ImageDraw.py", line 220, in text
    mask, offset = font.getmask2(text, self.fontmode, *args, **kwargs)
AttributeError: 'ImageFont' object has no attribute 'getmask2'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "pillow_test.py", line 5, in <module>
    draw.text((0,0),"日本語の文字だよ")
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/PIL/ImageDraw.py", line 224, in text
    mask = font.getmask(text, self.fontmode, *args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/PIL/ImageFont.py", line 115, in getmask
    return self.font.getmask(text, mode)
UnicodeEncodeError: 'latin-1' codec can't encode characters in position 0-7: ordinal not in range(256)
```

多分、重要なのは最後の一行ですね。

```
UnicodeEncodeError: 'latin-1' codec can't encode characters in position 0-7: ordinal not in range(256)
```

要は「フォントがない」と言ってるわけです。この人は日本語の文字を知らないわけですね。だから教えてあげる必要があります。日本語のフォントファイルを指定しましょう。

#日本語の文字の入った画像を生成する

日本語フォントは何でも良いですが、今回はこちらのサイトで「こころ明朝体」というフォントをダウンロードして使うことにしました。
[日本語フォント「こころ明朝体」 - フォント無料ダウンロード｜Typing Art](https://typingart.net/?p=46)



```python
from PIL import Image, ImageDraw, ImageFont

im = Image.new("RGB",(300,300),"blue")# Imageインスタンスを作る
draw = ImageDraw.Draw(im)# im上のImageDrawインスタンスを作る
fnt = ImageFont.truetype('./Kokoro.otf',30) #ImageFontインスタンスを作る
draw.text((0,0),"日本語の\n文字だよ",font=fnt) #fontを指定
im.save("./test.png")
```

これで、日本語の文字が入った画像が作れました。

しれっと`\n`という暗号が入ってますが、これは「改行」です。こう書くと、画像の上ではちゃんと改行してくれます。

#ある画像に、base64画像と文字を重ねて表示する

基本的には[こちら](https://code.i-harness.com/en/q/18dce13)を参考にしましたが、

どうも情報が古く、python2系向けの情報になっているようでコードがそのまま使えず、四苦八苦しました。

最終的に動いたコードは以下になります。

```python

from PIL import Image, ImageDraw, ImageFont
import io
import re
import base64

# base64化された画像データを用意
data = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
# 頭のいらない部分を取り除いた上で、バイト列にエンコード
image_data_bytes = re.sub('^data:image/.+;base64,','',data).encode('utf-8')
# バイト列をbase64としてデコード
image_data = base64.b64decode(image_data_bytes)#返り値もバイト列
# ファイルとして開き、pillowのImageインスタンスにする
im2 = Image.open(io.BytesIO(image_data))


im = Image.new("RGB",(300,300),"blue")
im.paste(im2,(30,30))#画像に画像を重ねます。二つ目の引数は位置の指定。
draw = ImageDraw.Draw(im)
fnt = ImageFont.truetype('./Kokoro.otf',30)
draw.text((0,0),"日本語の\n文字だよ",font=fnt)
im.save("./test.png")
```

base64化された画像として用意したこちらは、小さな赤い点が一つあるだけのpngです。この記事のサンプルコードを短くするために、小さな画像にしました。貼り付けられたことがわかれば何でも良いんです。

やってることは、

 1. 文字列型で、base64化された画像データを用意
 2. 頭のいらない部分(`data:image/png;base64,`という部分)を除去
 3. バイト列化(これをやらないと次のステップでデコード関数に渡せない)
 4. base64としてデコード(これでやっと画像ファイルのバイト列になる)
 5. ファイルとして開く

という感じ。このために`import`が3つも増えちゃいましたね。全部使ってます。


以上！！！

プログラミングで何か作る時って、まずこうやって一つずつ調べながら技術習得する時間があって、その後でやっと実装に入ることになるから、時間がかかっちゃうね〜。短くしたい。