はじめに
通常は ImageMagick で処理しているものを,Python の matplotlib と PIL でプログラムを組んでやってみました.
ツインタワーの写真以外は,
こちら
の2軸棒グラフがネタになっています.
やってみた内容は以下の通り.
- 画像に文字列を挿入
- ファイルに格納された文字列を画像にする
- 画像の余白削除・リサイズ・結合
作業環境は以下の通り.
- MacBook Pro (Retina, 13-inch, Mid 2014)
- macOS High Sierra
- Python 3.6.4
- ImageMagick 7.0.7-22
画像に文字列を挿入
テストに使っているのは,クアラルンプールにあるペトロナス・ツインタワーの写真です.
私は,夜のこの景色が大好きです.
なお,この写真は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
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)
ファイルに格納された文字列を画像にする
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
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)
画像の余白削除・リサイズ・結合
事前に準備された数表の画像 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://note.nkmk.me/python-pillow-image-resize/)
- 余白追加 (https://note.nkmk.me/python-pillow-add-margin-expand-canvas/)
以下のPythonプログラムにおいての関数の機能以下の通り.
- trim:余白削除
- resz:リサイズ
- marg:余白追加
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で台紙を作成しそこに2枚の画像を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()
以 上