Python の pillow を使用して画像サイズを変更するアプリを作成しました。
次の特徴があります。
👍アスペクト比固定で縦か横のサイズを指定してサイズ変更します。
👍画像は exe にドラッグアンドドロップするかファイルダイアログで選択します。
👍Exif 情報を残すか消すか選択できます。
👍画像に文字で透かしを付加できます。
アプリの作り方をサンプルコードも交えて説明します。
📔 画像サイズを変更し文字透かしを入れるアプリ【フリー】 🔗
目次
- ◆動作時の画像
- ◆機能・特長
- ◆考え方
- ◆pillow で画像サイズを変更する
- ◆pillow で画像のEixf情報を出力する
- ◆pillow で文字透かしを付加する
- ◆必要なパッケージ
- ◆全体のソース
- ◆バイナリ作成(pyinstaller)
- ❖ライブラリとして使用する場合の仕様
- ◆さいごに🦉
- ◆参考
◆動作時の画像
◆機能・特長
- アスペクト比固定で縦か横のサイズを指定してサイズ変更します。
- 画像は exe にドラッグアンドドロップするかファイルダイアログで選択します。
- Exif情報を残すか消すか選択できます。
- 画像に文字で透かしを付加できます。
◆考え方
指定された画像を pillow の Image.resize()
メソッドでサイズ変換します。
必要なら Exif 情報を読み込んで保存用オプションに設定します。
文字透かしを施す場合、ImageDraw
オブジェクトを作成し、文字列やフォントなどを設定して、text()
メソッドで書き込みます。
具体的には、次の通りです。
対象画像ファイルパスの取得
- コマンドライン引数があったら、パスのタプルとする
- なかったら、ファイルダイアログを表示
- パスがなかったら終了
保存先フォルダの作成
保存先フォルダがなかったら作成Exif 情報の取得
画像サイズ変更
文字透かしの描画
- サイズ変換した画像を元に
ImageDraw
オブジェクトを作成 - フォントファイル名から
ImageFont.truetype()
メソッドで フォントオブジェクトを作成 - フォントオブジェクトの
getsize()
メソッドで書き込む文字列の長さを計測 - 画像の右下の位置から文字列を書き込む位置を計算
- 文字書き込み用オプションの作成
fill, stroke_width, stroke_fillの設定値を辞書で作成 - 文字の書き込み
ImageDrqwオブジェクト.text()
メソッド
- サイズ変換した画像を元に
保存
- 画像のファイル名にサフィックスを付加した名前のファイルの存在確認
Imageオブジェクト.save()
メソッドで保存
◆pillow で画像サイズを変更する
pillow を使用して画像サイズを変更する手順は、次の通りです。
- 画像を開く
Image.open(パス)
- サイズ変更
imageオブジェクト.resize((幅, 高さ))
- 保存
imageオブジェクト.save(パス)
◇画像サイズ変更
- 【構文】
imageオブジェクト.resize(size, resample=None, box=None, reducing_gap=None)
- 主な引数
size
:(幅, 高さ)のタプルresample
:サンプリングフィルター- フィルター(品質順)
PIL.Image.NEAREST
,PIL.Image.BOX
,PIL.Image.BILINEAR
,PIL.Image.HAMMING
,PIL.Image.BICUBIC
,PIL.Image.LANCZOS
デフォルトはPIL.Image.BICUBIC
- フィルター(品質順)
box
:切り抜き範囲(x, y, 幅, 高さ)のタプル、無指定は全体
- 戻り値
- 新しい image オブジェクトが返ります
◇保存
- 【構文】
imageオブジェクト.save(fp, format=None, **params)
- 主な引数
fp
:ファイル名(文字列、pathlib.Pathオブジェクト、ファイルオブジェクト)format
:画像の種類
Noneの場合、ファイル名の拡張子で判断**params
:ファイルの種類によって有効なオプションが異なる
ただし、無効なオプションを指定してもエラーにはならない
⇒拡張子で判断してオプションを用意しなくても良い
注意事項
- 同じファイルが存在しても上書きする
- 例外:ValueError(拡張子エラー)、OSError(書き込みエラー)
拡張子ごとに異なるオプション
- JPEG
quality
:品質。0(悪い)~95(良い)。デフォルト:75
⇒画質を保持するなら95を設定すると良い
元の画像と同じ品質で保存するには、
img.save('test.jpg', quality=100, subsampling=0)
- JPEG
◎フォーマット変換
読み込み画像のフォーマットと異なる拡張子で保存するとフォーマット変換ができます。
ただし、画像のフォーマットによってはそのままでは保存できません。
◇アスペクト比の維持
ブログなどで使用する画像のサイズ変更は、ほとんどがアスペクト比を維持したままのサイズ変更と思われます。
ここでは、幅か高さのどちらかを指定して、もう一方は計算します。
計算式
- 幅が指定された場合
高さ = round(元の高さ * 幅 / 元の幅) - 高さが指定された場合
幅 = round(元の幅 * 高さ / 元の高さ)
【コード】
def scale_to_width(self, img, width): """ アスペクト比を固定して、幅が指定した値になるようリサイズする。 """ height = round(img.height * width / img.width) return img.resize((width, height)) def scale_to_height(self, img, height): """ アスペクト比を固定して、高さが指定した値になるようリサイズする。 """ width = round(img.width * height / img.height) return img.resize((width, height))
◆pillow で画像のEixf情報を出力する
pillow で画像の Exif 情報を出力するには、読み込んだ画像から Exif 情報を取得して、保存時にその Exif 情報を設定する必要があります。
PNG の場合、保存時に何も指定しないと Exif 情報が出力されます。
したがって、明示的に Exif 情報を保存しない設定が必要です。
◇Exif情報の取得
- 【構文】
Imageオブジェクト.getexif()
- 戻り値
PIL.Image.Exifクラス
※collections.abc.MutableMapping (辞書のようなもの) を継承
◇Exif情報の保存
読み込んだ画像をデフォルトで保存するとExif情報は書かれない。 Exif情報を書き込むには、画像からExif情報を読み込んで保存時に指定する必要がある。
- 【構文】
imageオブジェクト.save(fp, format=None, **params)
-よく使う使い方(サンプル)
【構文】imageオブジェクト.save(fp, exif=exif)
▽読み取りと保存のサンプル
image1 = Image.open(file_name)
exif = image1.getexif()
image1.save(new_file_name, exif=exif)
消すためには、exif = bytes(b"")
とします。
exif = None
だと JPEG でエラーになってしまう
【コード】
save_kwarg = {} # Exif情報を書き込む場合、読んだ画像のExif情報を保存時に設定 if settings_img_conv.exif: save_kwarg["exif"] = img.getexif() else: save_kwarg["exif"] = bytes(b"") # JPEGはオプションなしかこの指定でないとExif無しにできない # PNGはオプションなしだとExifを出力するのでNoneかこの指定。 # 画像を保存 new_path = img_conv.save_image( dst_img, file_name, name_suffix, dst_path, overwrite=settings_img_conv.overwrite, **save_kwarg)
◆pillow で文字透かしを付加する
画像に文字透かしを付加するのは、単純に画像に文字を書き込むことです。
また、文字を書く機能に縁取りを付けるオプションがあります。
【手順】
- 読み込んだ Image オブジェクトから、書き込み用のImageDrawオブジェクトを作成
- 使用するフォントオブジェクトを作成
- 文字の書き込み
◇ImageDrawオブジェクトを作成
- 【構文】
ImageDraw.Draw(imgae))
- 主な引数
imgae
:Imageオブジェクト。このオブジェクトに書き込む
- 主な引数
◇フォントの指定
- 【構文】
ImageFont.truetype( font=None, size=10, index=0, encoding='', layout_engine=None)
-よく使う使い方(サンプル)
【構文】ImageFont.truetype( "msgothic.ttc", 10)
◇文字の書き込み
- 【構文】
ImageDrawオブジェクト.text(xy, text, fill=None, font=None, anchor=None, spacing=4, align='left', direction=None, features=None, language=None, stroke_width=0, stroke_fill=None, embedded_color=False))
- よく使う使い方(サンプル)
【構文】draw_img.text((0, 0), "signetue", font=font, fill="gold", stroke_width=2, stroke_fill="black")
- 引数
- xy:文字の左上の座標のタプル
- text:文字列(改行を含む場合はmultiline_text()を通す)
- fill:文字色
- anchor:アンカーアラインメント(デフォルトはtop,left)
- font:ImageFontオブジェクト
- 縁取り文字用の引数
- stroke_width:縁取りの幅(px)
- stroke_fill:縁取りの色
- 例外エラー:フォントによって例外が出る(たぶんビットマップフォントの場合)
◎色の指定
こちらのサイト『Color Names — HTML Color Codes』のNAME列の英語名で指定できます
#FFFFFF
という記述方法でも可能です
【コード】
# 透かし if settings_img_conv.water_mark: kwargs = {"fill":settings_img_conv.wm_f_color, "stroke_width":settings_img_conv.wm_stroke, "stroke_fill":settings_img_conv.wm_b_color} img_conv.set_watermark_by_str(dst_img, settings_img_conv.water_mark, settings_img_conv.wm_size, settings_img_conv.wm_font_name, settings_img_conv.wm_padx, settings_img_conv.wm_pady, kwargs) def set_watermark_by_str(self, img:Image, water_mark:str, size:int, font_name:str, padx:int, pady:int, kwarges): """ 透かしを文字で入れる """ sig = water_mark draw_img = ImageDraw.Draw(img) try: font = ImageFont.truetype(font_name, size) # フォント名とサイズ(px) except: print(f"【エラー】フォントファイル名を確認してください({font_name})") sys.exit() sig_size = font.getsize(sig) sig_nw = (img.size[0] - sig_size[0] - padx, img.size[1] - sig_size[1] - pady) try: draw_img.text(sig_nw, sig, font=font, **kwarges) except: print("【エラー】透かしが書けませんでした。フォントを変更してみてください。")
◆必要なパッケージ
- pillow
【インストール】pip install Pillow
【インポート】 from PIL import Image, ImageDraw, ImageFont
◆全体のソース
全体のソースはこちらから取得できます。
- ソース:image_resize_sig.py
- 設定ファイル:settings_img_conv.py
- 取得先:GitHub juu7g/Python-Image-resize-sig
◆バイナリ作成(pyinstaller)
バイナリ( exe
) ファイルは、pyinstaller
で作成します。
- 作成コマンド:
pyinstaller -F --exclude-module settings_img_conv ファイル名
◇設定ファイルがある時のpyinstaller
設定ファイル(仮にsettings.py)をバイナリでも使えるようにします。
考え方の基本は、バイナリを作るソースから設定ファイルを除外して、exe と同じパスにある設定ファイルを読めるようにします。
- import settingsを記述しているソースの変更
pyinstallerで作成したexeファイルがあるディレクトリから import できるように修正
sys.executable
が該当ディレクトリなのでsys.path
に追加
例:sys.path.append( os.path.dirname( sys.executable))
import settings
- settings.pyはexeに含めないようにpyinstallerを実行
--exclude-module
オプションを指定
例:--exclude-module settings
※拡張子は指定しないので注意 - 配布はexeとsettings.pyを渡し、同じディレクトリにおいて起動
❖ライブラリとして使用する場合の仕様
- 【クラス】
ImageUI クラス
画像変換クラスを使用して画像変換する操作クラス - コンストラクタ:
ImageUI()
- メソッド
convert_image_from_dialog_or_args(book:dict, paths:list=None)
: 画像変換する
リサイズ、回転、反転、Exif除去、文字透かしを処理する
対象の画像はパスで指定、複数指定可- 引数
book
:設定項目の辞書do_resize
:サイズ変更するか(bool)dest_path
:保存用フォルダ 相対パス(読んだ画像のフォルダから見て)、絶対パスとも可(str)width
:新しい画像の幅(int)height
:新しい画像の高さ 幅が0の時に有効(int)overwrite
:新しい画像のファイル名(_w300などが付加される)(bool)
True:上書き、False:末尾に数字を付加して別名保存exif
:Exif情報の書き込み True:書き込む、False:書かない(bool)mirror
:ミラー反転するか(bool)rotate270
:右回転するか(bool)rotate90
:左回転するか(bool)is_water_mark
:透かし文字付加するか(bool)water_mark
:透かしの文字(str)wm_font_name
:フォントファイル名(str)wm_size
:フォントサイズ(int)wm_f_color
:文字色(str)wm_b_color
:縁取りの色(str)wm_stroke
:縁取りの幅(px)(int)wm_padx
:右下からの隙間(横)(int)wm_pady
:右下からの隙間(縦)(int)
paths
:画像のパスのリスト
- 戻り値
set
:変換後の画像のパスのセットstr
:エラーメッセージ
- 引数
- 【クラス】
ImageConversion クラス
画像変換クラス - コンストラクタ:
ImageConversion()
- メソッド
scale_to_width(img, width)
:アスペクト比を固定して、指定した幅になるようリサイズする- 引数
img
:元の画像width
:幅
- 戻り値
- 変換した画像
- 引数
scale_to_height(img, height)
:アスペクト比を固定して、指定した高さになるようリサイズする- 引数
img
:元の画像height
:高さ
- 戻り値
- 変換した画像
- 引数
get_image(file_path)
:画像を読み込む- 引数
file_path
:画像のパス
- 戻り値
- ロードした画像
- 引数
save_image(img:Image, file_path:str, suffix:str, dst_path:str, overwrite:bool=True, **save_kwargs)
:画像の保存
保存パスはdst_path\ベース名suffix.拡張子
make_dir(target_path:str, base_path:str)
:フォルダの作成
指定フォルダが存在しない場合に作成する- 引数
target_path
:指定されたフォルダbase_path
:基準にするフォルダ。相対パスの場合に使用
- 戻り値
- 作成したフォルダのパス
- 引数
set_watermark_by_str(img:Image, water_mark:str, size:int, font_name:str, padx:int, pady:int, kwarges)
:透かしを文字で入れる- 引数
img
:保存する画像water_mark
:透かし文字列size
:フォントサイズfont_name
:フォントファイル名padx
:画像の右下からの移動距離(水平方向)pady
:画像の右下からの移動距離(垂直方向)kwarges
:Draw クラスの text() メソッドで使える引数
- 戻り値
- エラーメッセージ
- 引数
更新:2022-10-22
◆さいごに🦉
本記事で紹介しているアプリと同じようなアプリは他にも存在します。
紹介しませんが、アプリを作ってブログの記事を書くのに似たようなものがないか調べたら、当然のようにありました。
本記事のアプリは、機能的には他のものと変わりませんが、むしろ低い、ブロガー向けに特化していると思っています。
作成するに当たり、画像のサイズ変更は特に難しくありませんでした。
Exit 情報を書かないようにするのに PNG と JPEG で挙動が違っていて分かるまでに時間が掛かりました。
仕様書もなかなかぴしっと書かれていないのですね。
文字の描画はフォントでてこずりました。
ただ描画するだけならすぐできたのですが、フォントを指定してもらうための方法を提供するのに迷いました。
フォントについては、後日、少し書かせていただきます。
📔 画像ビューアの作り方(Treeviewに画像と疑似チェックボックス)【Python】
◇免責事項
ご利用に際しては、『免責事項』をご確認ください。
お気づきの点がございましたら『お問い合わせ』からお問い合わせください。
◆参考
- pillow 仕様:Pillow — Pillow (PIL Fork) 8.4.0 documentation
- 参考:Python Pillow - 画像に重ねて文字を描く - PythonとRPAで遊ぶ
- エラー解説:OSError: invalid argument when draw stroke with specific font - Python-Pillow/Pillow
- アプリ作成:python - Importing external module in single-file exe created with PyInstaller - Stack Overflow