Python matplotlibとPILで画像処理（vs ImageMagick）

# はじめに
通常は ImageMagick で処理しているものを，Python の matplotlib と PIL でプログラムを組んでやってみました．

ツインタワーの写真以外は，
[こちら](https://qiita.com/damyarou/items/1269820e403f697bc9e6)
の２軸棒グラフがネタになっています．

やってみた内容は以下の通り．

+ 画像に文字列を挿入
+ ファイルに格納された文字列を画像にする
+ 画像の余白削除・リサイズ・結合

作業環境は以下の通り．

+ MacBook Pro (Retina, 13-inch, Mid 2014)
+ macOS High Sierra
+ Python 3.6.4
+ ImageMagick 7.0.7-22

# 画像に文字列を挿入


<img src="https://qiita-image-store.s3.amazonaws.com/0/129300/fb583e42-63bd-7d17-8856-8c05a882b172.png" width=70%>


テストに使っているのは，クアラルンプールにあるペトロナス・ツインタワーの写真です．
私は，夜のこの景色が大好きです．

なお，この写真はMacのPreviewでみると縦長の画像なのですが，ImagemagickでもPILでも読み込んでみると横長画像として認識されています．
そこで，ImageMagickでは，元画像はいじらず，書き込む文字列画像を90度回転させて書き込んでいます．

Pythonでは，読み込んだ画像を90度回転させてから，文字列の書き込みを行っています．また余白がついてくるので，最後に余白の削除を行っています．


## ImageMagick

```
%%bash

flow=IMG_0694.JPG
fupp=_text.png
ffin=fig_KL_im.jpg
fknd=/System/Library/Fonts/Helvetica.ttc
convert -font $fknd -fill 'yellow' -background 'transparent' -pointsize 200 label:'Kuala Lumpur (KLCC)' $fupp
convert $fupp -rotate -90 $fupp
convert $flow $fupp -gravity southwest -geometry +40+40 -compose over -composite $ffin
```

## Python

```python:
from PIL import Image, ImageChops
from matplotlib import pyplot as plt

def trim(im, border):
  bg = Image.new(im.mode, im.size, border)
  diff = ImageChops.difference(im, bg)
  bbox = diff.getbbox()
  if bbox:
    return im.crop(bbox)

flist=[
'IMG_0694.JPG'
]
fsz=40
for fn in flist:
    fig=fn
    img=Image.open(fig,'r')
    img=img.rotate(-90, expand=True)
    print(img.size)
    iw=800
    ih=int(img.size[1]/img.size[0]*iw)
    fig = plt.figure(figsize=(iw/100,ih/100),facecolor='w')
    plt.rcParams['font.family'] = 'Tahoma'
    plt.axis('off')
    img = img.resize((iw,ih))
    plt.text(20,20, 'Kuala Lumpur (KLCC)', rotation=0,color='yellow',fontsize=fsz,va='top', ha='left')
    plt.imshow(img)
    fnameF='fig_KL_py.jpg'
    plt.savefig(fnameF,dpi=150,bbox_inches='tight',pad_inches=0)
    plt.show()

    img=Image.open(fnameF,'r')
    img=trim(img,'#ffffff')
    img.save(fnameF, 'JPEG', quality=100, optimize=True)
```



# ファイルに格納された文字列を画像にする

<img src="https://qiita-image-store.s3.amazonaws.com/0/129300/4c1bfca3-1f8d-d943-d01f-cafca97ccba8.png" width=70%>


## ImageMagick

```
%%bash

for fn in bar1
do
    fr=${fn}.txt
    fw=ft_${fn}.png
    
    convert -font /System/Library/Fonts/Menlo.ttc -pointsize 16 -interline-spacing 5 label:@$fr $fw
#    convert -font courier-new -pointsize 16 -interline-spacing 0 label:@$fr $fw
done
```

## Python

```python:
import matplotlib.pyplot as plt

def DRAW(sp,fn):
    fsz=20
    w=fsz*len(sp[0])/100*0.5
    h=(fsz+10)*len(sp)/100
    fig = plt.figure(figsize=(w,h),facecolor='w')
    plt.rcParams['font.family'] = 'monospace'
#    plt.rcParams['font.family'] = 'Courier New'
#    plt.rcParams['font.family'] = 'Ricty Diminished'
    xmin=0
    xmax=float(len(sp[1]))
    ymin=0
    ymax=float(len(sp))
    plt.xlim(xmin,xmax)
    plt.ylim(ymax,ymin)
    plt.axis('off')
    for i,strp in enumerate(sp):
        plt.text(1,i+1,strp,ha='left',va='top')
    plt.tight_layout()
    fnameF='ftpy_'+fn+'.png'
    plt.savefig(fnameF, dpi=100, bbox_inches="tight", pad_inches=0)
    plt.show()


flist=[
'bar1'
]
for fn in flist:
    fnameR=fn+'.txt'
    fr=open(fnameR,'r')
    lines=fr.readlines()
    fr.close()
    sp=[]
    for text in lines:
        sp=sp+[text.replace('\n','')]
    DRAW(sp,fn)
```


# 画像の余白削除・リサイズ・結合


<img src="https://qiita-image-store.s3.amazonaws.com/0/129300/efc072f2-3cec-8d75-3148-031377f273b4.png" width=70%>

事前に準備された数表の画像 *ft_bar1.png* と棒グラフの画像 *fig_bar1.png* を縦に結合して，新しい画像 *fig_bar1_b.png* を作ります．


## ImageMagick

```
for fn in bar1
do
    f1=ft_${fn}.png
    f2=fig_${fn}.png
    f3=fig_${fn}_a.png
    convert -trim -resize 760x -gravity center -background white -extent 800x200 $f1 _$f1
    convert -trim -resize 760x -gravity center -background white -extent 800x800 $f2 _$f2
    convert -append _$f1 _$f2 $f3
done
rm _*
```


## Python
プログラム作成にあたり，以下のサイトを参考にしました（というか関数をいただきました）

+ [トリム (https://gist.github.com/mattjmorrison/932345)](https://gist.github.com/mattjmorrison/932345)
+ [リサイズ (https://note.nkmk.me/python-pillow-image-resize/)](https://note.nkmk.me/python-pillow-image-resize/)
+ [余白追加 (https://note.nkmk.me/python-pillow-add-margin-expand-canvas/)](https://note.nkmk.me/python-pillow-add-margin-expand-canvas/)

以下のPythonプログラムにおいての関数の機能以下の通り．

+ trim：余白削除
+ resz：リサイズ
+ marg：余白追加

```python
from PIL import Image, ImageChops

def trim(im, border):
  bg = Image.new(im.mode, im.size, border)
  diff = ImageChops.difference(im, bg)
  bbox = diff.getbbox()
  if bbox:
    return im.crop(bbox)

def resz(im,ix):
    iw=im.size[0]
    ih=im.size[1]
    im_res = img_new.resize((int(ix),int(ih/iw*ix)), Image.LANCZOS)
    return im_res

def marg(im, top, right, bottom, left, color):
    width, height = im.size
    new_width = width + right + left
    new_height = height + top + bottom
    result = Image.new(im.mode, (new_width, new_height), color)
    result.paste(im, (left, top))
    return result

# 上側画像を横幅800pxで作成（余白含む）
ix=760
fig='ftpy_bar1.png'
img_org=Image.open(fig,'r')
img_new=trim(img_org,'#ffffff')
img_res=resz(img_new,ix)
img_fin=marg(img_res, 15, 20, 15, 20, '#ffffff')
print(img_org.format,img_org.size, img_org.mode)
print(img_new.format,img_new.size, img_new.mode)
print(img_res.format,img_res.size, img_res.mode)
print(img_fin.format,img_fin.size, img_fin.mode)
img1=img_fin

# 下側画像を横幅800pxで作成（余白含む）
ix=760
fig='fig_bar1.png'
img_org=Image.open(fig,'r')
img_new=trim(img_org,'#ffffff')
img_res=resz(img_new,ix)
img_fin=marg(img_res, 20, 20, 20, 20, '#ffffff')
print(img_org.format,img_org.size, img_org.mode)
print(img_new.format,img_new.size, img_new.mode)
print(img_res.format,img_res.size, img_res.mode)
print(img_fin.format,img_fin.size, img_fin.mode)
img2=img_fin

# 上下で画像を結合（Image.newで台紙を作成しそこに２枚の画像をpasteで貼り込む）
new_width=img1.size[0]
new_height=img1.size[1]+img2.size[1]
img3=Image.new('RGBA', (new_width, new_height), '#ffffff')
img3.paste(img1,(0,0))
img3.paste(img2,(0,img1.size[1]))
img3.save('fig_bar1_b.png', 'PNG', quality=100, optimize=True)

img3.show()
```

以　上