#環境
- 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でエラーが出た場合は、僕の前回の記事が参考になるかもしれません。)
pillowのインストールが済んだら、次のpythonプログラムを実行します。
from PIL import Image
im = Image.new("RGB",(300,300),"red")
im.save("./test.jpg")
Imageインスタンスを作成して、そのsaveメソッドを呼び出してるだけです。2行。
これで、test.jpg
という赤一色の画像ファイルが生成されました。メッチャ簡単じゃない???すご。。。。。
各メソッドの引数の意味などは、各々公式リファレンスで確認お願いします。
#文字の入った画像を生成する
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"と文字の入った画像が生成されます。文字色とかフォントとかは全て初期値。
ところが。これを、文字列の部分だけ変えて
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
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画像と文字を重ねて表示する
基本的にはこちらを参考にしましたが、
どうも情報が古く、python2系向けの情報になっているようでコードがそのまま使えず、四苦八苦しました。
最終的に動いたコードは以下になります。
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です。この記事のサンプルコードを短くするために、小さな画像にしました。貼り付けられたことがわかれば何でも良いんです。
やってることは、
- 文字列型で、base64化された画像データを用意
- 頭のいらない部分(
data:image/png;base64,
という部分)を除去 - バイト列化(これをやらないと次のステップでデコード関数に渡せない)
- base64としてデコード(これでやっと画像ファイルのバイト列になる)
- ファイルとして開く
という感じ。このためにimport
が3つも増えちゃいましたね。全部使ってます。
以上!!!
プログラミングで何か作る時って、まずこうやって一つずつ調べながら技術習得する時間があって、その後でやっと実装に入ることになるから、時間がかかっちゃうね〜。短くしたい。