ユーニックス総合研究所

  • home
  • archives
  • python-pillow-waku

PythonのPillowで画像に枠線を付ける

  • 作成日: 2020-09-24
  • 更新日: 2024-01-02
  • カテゴリ: Python

Pillowで枠線を付ける

Pythonの画像処理ライブラリであるPillowを使って画像に枠線をつけてみます。
Pillowには絵を描くためのモジュールImageDrawがあってこれを使うと枠線など自由に描くことが可能です。

環境にpipなどでPillowをインストールしておきます。

$ pip install Pillow  

使用する画像

今回はこの画像を使用します。

実際の実装

実装は↓のようになります。

from PIL import Image, ImageDraw  

fname = 'img/sample.jpg'  # 画像のファイル名  
line_width = 4  # line width(枠線一本の幅)  
waku_color = (0, 128, 255)  # 枠線の色(RGB)  

# 元になる画像からImageオブジェクトを作成  
src_im = Image.open(fname)  
sw, sh = src_im.size  # 元の画像のサイズを取得  

# キャンバスを作成  
# この画像に枠線と元の画像を合成する  
cw, ch = sw + line_width * 2, sh + line_width * 2  # キャンバスのサイズを元の画像から生成  
canvas_im = Image.new('RGB', (cw, ch))  

# キャンバスのImageDrawオブジェクトを作成  
canvas = ImageDraw.Draw(canvas_im)  

# キャンバスを単色で塗りつぶす  
canvas.rectangle([(0, 0), (cw, ch)], fill=waku_color)  

# キャンバスの画像に元の画像を貼り付け  
canvas_im.paste(src_im, (line_width, line_width))  

# 保存  
canvas_im.save('out.jpg')  

まず1行目ですが、

from PIL import Image, ImageDraw  

ここではPillowImageモジュールとImageDrawモジュールをインポートしています。
Imageモジュールは画像を開いたり生成したり合成したりするのに使います。
ImageDrawモジュールは画像に枠線などの絵を描くのに使います。

fname = 'img/sample.jpg'  # 画像のファイル名  
line_width = 4  # line width(枠線一本の幅)  
waku_color = (0, 128, 255)  # 枠線の色(RGB)  

使用する変数を定義しています。

# 元になる画像からImageオブジェクトを作成  
src_im = Image.open(fname)  
sw, sh = src_im.size  # 元の画像のサイズを取得  

fnameのファイルを開きます。
これはImageモジュールのopen関数を使います。
open関数は画像を開くのに成功するとその画像の拡張子に対応したオブジェクトを返します。
上の場合、fnamejpg画像なので返ってくるオブジェクトは<class 'PIL.JpegImagePlugin.JpegImageFile'>になります。

# キャンバスを作成  
# この画像に枠線と元の画像を合成する  
cw, ch = sw + line_width * 2, sh + line_width * 2  # キャンバスのサイズを元の画像から生成  
canvas_im = Image.new('RGB', (cw, ch))  

先ほど開いた画像の横幅と高さから、キャンバスの画像を生成します。
今回の枠線の描き方は、まず元の画像より枠線分大きい画像(キャンバス)を生成します。
そしてその大きい画像を単色で塗りつぶし、その上から元の画像を貼り付けて合成するようにします。
こうすれば元の画像の画素を上書きすることなく画像に枠線をはめることができます。

# キャンバスのImageDrawオブジェクトを作成  
canvas = ImageDraw.Draw(canvas_im)  

キャンバスの画像からImageDrawオブジェクトを生成します。
ImageDrawオブジェクトは引数の画像に線や矩形などを自由に描けるオブジェクトです。
今回は矩形で単色で塗りつぶすのに使います。

# キャンバスを単色で塗りつぶす  
canvas.rectangle([(0, 0), (cw, ch)], fill=waku_color)  

ImageDrawオブジェクトであるキャンバスのメソッドrectangleを使って矩形を描画します。
第1引数の[(0, 0), (cw, ch)]には矩形の左上の座標と、右下の座標を指定します。

fillに色を表す文字列を指定するとその色で矩形が塗りつぶされます(red, blueなど)。
またはfill=(0, 128, 255)のようにRGB値を指定することも可能です。

# キャンバスの画像に元の画像を貼り付け  
canvas_im.paste(src_im, (line_width, line_width))  

キャンバスを通じてキャンバスの画像に色が塗られたので、キャンバスの画像に元の画像を貼り付けます。
(line_width, line_width)で貼り付ける位置を左上から枠線の幅分ずらしています。
キャンバスの画像は枠線の幅 * 2分の大きさが横幅と高さに確保されているので、こうすることで枠線が上下左右にはみ出ることになります。

# 保存  
canvas_im.save('out.jpg')  

生成したキャンバスの画像を保存します。
保存した画像は↓のようになります。
周りに青い枠線が描かれてますね。

元の画像ファイル名を使いたい場合

元の画像ファイル名を維持し、たとえば_wakuなどをファイル名に付加して画像を保存したい場合があります。
そういう時は↓のようにos.path.splitextを使います。

from PIL import Image, ImageDraw  
import os  

fname = 'img/sample.jpg'  # 画像のファイル名  
line_width = 4  # line width(枠線一本の幅)  
waku_color = (0, 128, 255)  # 枠線の色(RGB)  

# 元になる画像からImageオブジェクトを作成  
src_im = Image.open(fname)  
sw, sh = src_im.size  # 元の画像のサイズを取得  

# キャンバスを作成  
# この画像に枠線と元の画像を合成する  
cw, ch = sw + line_width * 2, sh + line_width * 2  # キャンバスのサイズを元の画像から生成  
canvas_im = Image.new('RGB', (cw, ch))  

# キャンバスのImageDrawオブジェクトを作成  
canvas = ImageDraw.Draw(canvas_im)  

# キャンバスを単色で塗りつぶす  
canvas.rectangle([(0, 0), (cw, ch)], fill=waku_color)  

# キャンバスの画像に元の画像を貼り付け  
canvas_im.paste(src_im, (line_width, line_width))  

# 保存  
leftside, ext = os.path.splitext(fname)  # ファイル名部分と拡張子を分離  
dst_fname = leftside + '_waku' + ext  # ファイル名を再合成  
canvas_im.save(dst_fname)  

スクリプトにしたい場合

元の画像ファイル名と出力先ファイル名をコマンドラインから指定できるようにするには↓のようにsys.argvを使います。

from PIL import Image, ImageDraw  
import sys  

if len(sys.argv) < 3:  
    print('usage: waku.py [src-fname] [dst-fname]')  
    sys.exit(0)  

fname = sys.argv[1]  
dst_fname = sys.argv[2]  
line_width = 4  # line width(枠線一本の幅)  
waku_color = (0, 128, 255)  # 枠線の色(RGB)  

# 元になる画像からImageオブジェクトを作成  
src_im = Image.open(fname)  
sw, sh = src_im.size  # 元の画像のサイズを取得  

# キャンバスを作成  
# この画像に枠線と元の画像を合成する  
cw, ch = sw + line_width * 2, sh + line_width * 2  # キャンバスのサイズを元の画像から生成  
canvas_im = Image.new('RGB', (cw, ch))  

# キャンバスのImageDrawオブジェクトを作成  
canvas = ImageDraw.Draw(canvas_im)  

# キャンバスを単色で塗りつぶす  
canvas.rectangle([(0, 0), (cw, ch)], fill=waku_color)  

# キャンバスの画像に元の画像を貼り付け  
canvas_im.paste(src_im, (line_width, line_width))  

# 保存  
canvas_im.save(dst_fname)  

実行例。

$ python waku.py img/sample.jpg out.jpg  

問題

Q1: ImageDrawモジュールの第1引数として適当なものを答えよ

  1. Imageオブジェクト(に類するもの)
  2. sys.argv
  3. os.path.splitext

Q2: 画像に画像を貼り付けたい場合に適当な操作を答えよ

  1. Imageオブジェクトのopenを使う
  2. Imageオブジェクトのpasteを使う
  3. Imageオブジェクトのnewを使う

Q3: 画像を単色で塗りつぶしたい場合に適当な操作を答えよ

  1. Imageオブジェクトのrectangleメソッドを使う
  2. ImageDrawオブジェクトのrectangleメソッドを使う
  3. ImageDrawオブジェクトのlineメソッドを使う

正解はこちら↓

Q1: 1
Q2: 2
Q3: 2