Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

環境

  • 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です。この記事のサンプルコードを短くするために、小さな画像にしました。貼り付けられたことがわかれば何でも良いんです。

やってることは、

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

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

以上!!!

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

agajo
あんなに勉強して、親に高い予備校代も出してもらって東大に入り、卒業したのに、今では家と食事を親に頼りながら、年金と住民税を払うためにトイレ掃除をしている者です。
https://portal.oka-ryunoske.work/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした