11月7日のお散歩写真です。
ご近所を散歩しました。
アパートから徒歩2分ぐらいにある公園の木
なんか、つやつやに写りました。
腐ってもデジタル用レンズだけのことはある?
畑の柿の実
武蔵野公園に行ってみました。
武蔵野公園の隣の緑道
Panasonic DMC-GX7, OLYMPUS 40-150mm
11月7日のお散歩写真です。
ご近所を散歩しました。
アパートから徒歩2分ぐらいにある公園の木
なんか、つやつやに写りました。
腐ってもデジタル用レンズだけのことはある?
武蔵野公園の隣の緑道
Panasonic DMC-GX7, OLYMPUS 40-150mm
今のPCに切り替えてから、ずっとGIMP はPortable版の2.8を
使っていたのですが、最近になって Portable 版の2.10 を
インストールして使い始めています。
昔 GIMP 2.6 の頃にダウンロードしたトーンカーブを
GIMP Portable 2.10 トーンカーブの「現在の設定をファイル
からインポート」で読み込むと、「〇〇の読み込み中にエラーが発生しました。
想定していたよりも早くストリームの最後に到達しました。」
というエラーになってしまいました。
で、GIMP Portable 2.10 でエクスポートしたファイルと見比べて、
以下のことに気を付けてインポートできるようになりました。
(1) 改行コードは LF
(2) 最初の行は "# GIMP Curves File" 以外のことを書かない
(3) 数値データ行の最後にスペースを1文字入れる
(1) について
自分で作ったファイルは CR+LF だったので、エラーになりました。
(2) について
# で始まっている行なので、コメント行かと思って、
適当にコメントを書き足していたら、エラーになりました。
(3) について
昔ダウンロードしたファイルが読めなかったのはこれが原因だったようです。
---
GIMP のトーンカーブがダウンロードできるところを
探してみたら、ここが見つかりました。
https://gist.github.com/neo22s/f7d9a1348b4376f8085b1e1387edf9a5
このページの "gimp-curves-tool.settings" と書いてある右側の Raw
ボタンをクリックすると、設定ファイルだけのページに移るので、それを保存します。
GIMP Portable 2.10 だと、このファイルを GimpPortable を
インストールしたディレクトリの
Data\.gimp\filters\GimpCurvesConfig.settings
に上書きすると、トーンカーブが使えるようになります。
【注】トーンカーブの履歴が消えてしまうので、消えると困る場合は上書きしないでください。
設定したトーンカーブを呼び出すには、GIMP Portableで画像ファイルを
開いたところで、メニューの
「色(C)」 → 「トーンカーブ(C)」
を選択し、トーンカーブのダイアログを開きます。
トーンカーブのダイアログの「プリセット」のドロップダウンリストに
ダウンロードしたトーンカーブのプリセットが入っています。
「Cross Process」のトーンカーブは、昔ダウンロードしたものと同様のカーブでした。
トーンカーブを適用すると、このプリセットのドロップダウンリストに
履歴が追加されるようです。
トーンカーブを1つだけエクスポートしたり、不要なトーンカーブを
削除するには、プリセットのドロップダウンリストの右側の
「プリセットの管理」をクリックします。
クリックすると、プリセットに関するメニューが出るので、
エクスポートしたり、削除したりする機能を呼び出せます。
最近、お出かけすることが減って、Pythonを勉強しています。 そのPython言語の、Pillowという画像ライブラリを使ったスクリプトです。 Pythonをインストールして、Pillowのライブラリをpipでインストールしておく必要があります。 記事を書いている時点のPythonは3.8です。 前回までは、塗りつぶしした画像やぼかし加工した画像を元の画像とブレンドしていますが、 今回のスクリプトは、元画像を折り返ししたものを元画像と並べるだけなので、 比較的、簡単な処理になっています。 折り返して貼り合わせるだけなので、あんまり面白くないかなぁって思っていましたが、 元の画像によっては、意外と面白い画像が出来上がりました。 以下にスクリプトのコードを書きます。
"""
鏡に映したように左右対称の画像にする
tkinterを使ったGUI版
Usage : コマンドラインから以下のコマンドで起動
python mirror_gui.py
"""
import os
from PIL import Image, ImageTk, ImageOps
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog, messagebox
class FormClass:
"""
鏡に映したように加工する画面のフォームを作成するクラス
"""
def __init__(self, root):
# インスタンス変数に初期値設定
self.root = root # Windowオブジェクト
self.filepath = "" # 入力ファイル名
self.readimg = None # 入力画像
self.dst_img = None # 加工結果画像
rowno = 0 # 表示位置(行番号)
# ラベルを作成
tk.Label(root, text='入力ファイルPATH'
).grid(column=0, row=rowno)
# 入力ファイル名
self.src_path = tk.StringVar()
# テキストボックスを作成
tb_widget = tk.Entry(root,
width=120,
textvariable=self.src_path)
tb_widget.grid(column=1, row=rowno)
# 入力ファイル選択ボタンを作成
btn_widget = tk.Button(root,
text='入力ファイル選択',
command=self.select_file)
btn_widget.grid(column=2, row=rowno)
rowno += 1
# 左位置 スケールの作成
tk.Label(root, text='左位置(%)'
).grid(column=0, row=rowno)
self.left = tk.DoubleVar()
left_sc = tk.Scale(
root,
variable=self.left,
orient=tk.HORIZONTAL,
from_=0,
to=100,
command=self.draw_effect)
left_sc.grid(column=1,
row=rowno,
sticky=(tk.N, tk.E, tk.S, tk.W))
self.left.set(0)
rowno += 1
# 右位置 スケールの作成
tk.Label(root, text='右位置(%)'
).grid(column=0, row=rowno)
self.right = tk.DoubleVar()
right_sc = tk.Scale(
root,
variable=self.right,
orient=tk.HORIZONTAL,
from_=0,
to=100,
command=self.draw_effect)
right_sc.grid(column=1,
row=rowno,
sticky=(tk.N, tk.E, tk.S, tk.W))
self.right.set(50)
rowno += 1
# 反転方向 コンボボックスの作成
tk.Label(root, text='反転方向'
).grid(column=0, row=rowno, pady=5)
self.direct_select = tk.StringVar()
direct_combo = ttk.Combobox(
root,
textvariable=self.direct_select)
direct_combo.grid(column=1,
row=rowno,
pady=5,
sticky=(tk.N, tk.W))
direct_combo['values'] = ('左右', '上下')
direct_combo.current(0)
direct_combo.bind("<>", self.draw_effect)
self.direct_select.set('左右')
rowno += 1
# 左右反転 コンボボックスの作成
tk.Label(root, text='左右反転'
).grid(column=0, row=rowno, pady=5)
self.reverse_select = tk.StringVar()
reverse_combo = ttk.Combobox(
root,
textvariable=self.reverse_select)
reverse_combo.grid(column=1,
row=rowno,
pady=5,
sticky=(tk.N, tk.W))
reverse_combo['values'] = ('右', '左')
reverse_combo.current(0)
reverse_combo.bind("<>", self.draw_effect)
self.reverse_select.set('右')
rowno += 1
# ファイル保存ボタンを作成
savebtn_widget = tk.Button(root,
text='ファイル保存',
command=self.show_save_dialog)
savebtn_widget.grid(column=0, row=rowno,
sticky=(tk.N))
self.CANVAS_WIDTH = 800
self.CANVAS_HEIGHT = 600
# 画像を表示するためのキャンバスの作成
self.canvas = tk.Canvas(self.root,
width=self.CANVAS_WIDTH,
height=self.CANVAS_HEIGHT)
self.canvas.grid(column=1, row=rowno, columnspan=2)
# 入力ファイル選択ボタン押下イベント
def select_file(self):
# ファイル選択ダイアログを表示
ftypes = [("All Files", ".*"),
("JPEG Image Files", ".jpg .jpeg"),
("PNG Image Files", ".png")]
if len(self.filepath) == 0:
idir = os.path.abspath(os.path.dirname(__file__))
else:
idir = os.path.dirname(self.filepath)
self.filepath = filedialog.askopenfilename(filetypes = ftypes,
initialdir = idir)
if len(self.filepath) == 0 :
return
self.src_path.set(self.filepath)
# 画像を読み取り
self.readimg = Image.open(self.filepath)
# Exif情報を取り出し
if 'exif' in self.readimg.info :
self.exif = self.readimg.info['exif']
else :
self.exif = bytes()
# 加工した画像を表示
self.draw_effect()
# 加工した画像を表示する
def draw_effect(self,val=0):
if self.readimg is None :
return
# 元画像のサイズ
w,h = self.readimg.size
if self.direct_select.get() == '左右' :
# 左右方向に折り返す
left = w * self.left.get() / 100.0
right = w * self.right.get() / 100.0
w_start = int(min(left,right))
w_end = int(max(left,right))
w = (w_end - w_start) * 2
# 元の画像と、折り返した画像を合成する
im_crop = self.readimg.crop((w_start, 0, w_end, h))
im_flip = ImageOps.mirror(im_crop)
# 出力画像の幅か高さが 0 なら return
if w == 0 or h == 0:
return
self.dst_img = Image.new("RGB", (w, h), 0)
if self.reverse_select.get() == '右':
self.dst_img.paste(im_crop)
self.dst_img.paste(im_flip, ((w_end - w_start), 0))
else :
self.dst_img.paste(im_flip)
self.dst_img.paste(im_crop, ((w_end - w_start), 0))
else :
# 上下方向に折り返す
upper = h * self.left.get() / 100.0
lower = h * self.right.get() / 100.0
h_start = int(min(upper,lower))
h_end = int(max(upper,lower))
h = (h_end - h_start) * 2
# 元の画像と、折り返した画像を合成する
im_crop = self.readimg.crop((0, h_start, w, h_end))
im_flip = ImageOps.flip(im_crop)
# 出力画像の幅か高さが 0 なら return
if w == 0 or h == 0:
return
self.dst_img = Image.new("RGB", (w, h), 0)
if self.reverse_select.get() == '右':
self.dst_img.paste(im_crop)
self.dst_img.paste(im_flip, (0, (h_end - h_start)))
else :
self.dst_img.paste(im_flip)
self.dst_img.paste(im_crop, (0, (h_end - h_start)))
# 画像の表示倍率を計算する
rate = min(1.0,
self.CANVAS_WIDTH / self.dst_img.width,
self.CANVAS_HEIGHT / self.dst_img.height)
# 表示用に画像を縮小する
new_w = int(self.dst_img.width * rate)
new_h = int(self.dst_img.height * rate)
small_resized = self.dst_img.resize((new_w, new_h))
# キャンバスに画像を表示する。
self.im = ImageTk.PhotoImage(image=small_resized)
self.canvas.create_image(0, 0, anchor=tk.NW, image=self.im)
# ファイル保存ボタンイベント
def show_save_dialog(self):
if self.dst_img is not None :
ftypes = [("All Files", ".*"),
("JPEG Image Files", ".jpg .jpeg"),
("PNG Image Files", ".png")]
ini_fname = os.path.basename(self.filepath)
filename = filedialog.asksaveasfilename(filetypes=ftypes,
initialfile=ini_fname)
if filename:
# 保存先ファイルが指定されたら、加工した画像を保存
self.dst_img.save(filename, quality=100, exif=self.exif)
else :
tk.messagebox.showerror(title="エラー",
message="入力ファイルを指定してください")
# windowを描画
window = tk.Tk()
# windowサイズを変更
window.geometry("1000x800")
# windowタイトルを設定
window.title("Mirror effect")
# フォームを作成、表示
FormClass(window)
# 画面を操作されるのを待つ
window.mainloop()
上のスクリプトをUTF-8のエンコーディングで、mirror_gui.py というファイルに保存します。 Windowsだとコマンドプロンプト等を開いて、スクリプトを保存したフォルダにcdコマンドで移動して、 python mirror_gui.py で実行します。 実行すると、
このような画面を表示します。 入力ファイル選択ボタンで、ファイル選択ダイアログを開きます。 そこでファイルを選択すると、
このように画像のプレビューを表示します。 「左位置」を大きくすると、切り出す左側の位置が右に移動します。 「右位置」を大きくすると、切り出す右側の位置が右に移動します。 「反転方向」で、左右反転と上下反転を切り替えます。 「左右反転」で右画像を折り返して左に貼り付けるか、 左画像を折り返して右に貼り付けるかを選択します。 「ファイル保存ボタン」をクリックすると、保存先選択ダイアログを開きます。
以下、このスクリプトで加工した画像をアップします。
新橋辺り
ガクアジサイ
コオニユリ
最近、お出かけすることが減って、Pythonを勉強しています。 そのPython言語の、Pillowという画像ライブラリを使ったスクリプトです。 Pythonをインストールして、Pillowのライブラリをpipでインストールしておく必要があります。 記事を書いている時点のPythonは3.8です。 前回のToycamera風に加工するスクリプトは、黒あるいは白で塗りつぶした画像を 丸いマスクでブレンドしていましたが、今回はぼかした画像を 横長な長方形のマスクでブレンドしています。 今回のスクリプトは、おもちゃっぽくするために、ブレンドした後の画像の彩度を上げて、 メディアンフィルターで質感を変えています。 以下にスクリプトのコードを書きます。
"""
ミニチュア風に上下をぼかす
tkinterを使ったGUI版
Usage : コマンドラインから以下のコマンドで起動
python miniture_gui.py
"""
import os
from PIL import Image, ImageDraw, ImageFilter, ImageTk, ImageEnhance
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog, messagebox
class FormClass:
"""
ミニチュア風に加工する画面のフォームを作成するクラス
"""
def __init__(self, root):
# インスタンス変数に初期値設定
self.root = root # Windowオブジェクト
self.filepath = "" # 入力ファイル名
self.readimg = None # 入力画像
self.dst_img = None # 加工結果画像
rowno = 0 # 表示位置(行番号)
# ラベルを作成
tk.Label(root, text='入力ファイルPATH'
).grid(column=0, row=rowno)
# 入力ファイル名
self.src_path = tk.StringVar()
# テキストボックスを作成
tb_widget = tk.Entry(root,
width=120,
textvariable=self.src_path)
tb_widget.grid(column=1, row=rowno)
# 入力ファイル選択ボタンを作成
btn_widget = tk.Button(root,
text='入力ファイル選択',
command=self.select_file)
btn_widget.grid(column=2, row=rowno)
rowno += 1
# 上位置 スケールの作成
tk.Label(root, text='上位置(%)'
).grid(column=0, row=rowno)
self.upper = tk.DoubleVar()
upper_sc = tk.Scale(
root,
variable=self.upper,
orient=tk.HORIZONTAL,
from_=0.0,
to=100,
command=self.draw_effect)
upper_sc.grid(column=1,
row=rowno,
sticky=(tk.N, tk.E, tk.S, tk.W))
self.upper.set(40)
rowno += 1
# 下位置 スケールの作成
tk.Label(root, text='下位置(%)'
).grid(column=0, row=rowno)
self.lower = tk.DoubleVar()
lower_sc = tk.Scale(
root,
variable=self.lower,
orient=tk.HORIZONTAL,
from_=0,
to=100.0,
command=self.draw_effect)
lower_sc.grid(column=1,
row=rowno,
sticky=(tk.N, tk.E, tk.S, tk.W))
self.lower.set(70)
rowno += 1
# 境界ぼかし スケールの作成
tk.Label(root, text='境界ぼかし'
).grid(column=0, row=rowno)
self.blur_radius = tk.IntVar()
blur_sc = tk.Scale(
root,
variable=self.blur_radius,
orient=tk.HORIZONTAL,
from_=0,
to=100,
command=self.draw_effect)
blur_sc.grid(column=1,
row=rowno,
sticky=(tk.N, tk.E, tk.S, tk.W))
self.blur_radius.set(35)
rowno += 1
# 効果の強さ スケールの作成
tk.Label(root, text='効果の強さ'
).grid(column=0, row=rowno)
self.strength = tk.IntVar()
strength_sc = tk.Scale(
root,
variable=self.strength,
orient=tk.HORIZONTAL,
from_=0,
to=50,
command=self.draw_effect)
strength_sc.grid(column=1,
row=rowno,
sticky=(tk.N, tk.E, tk.S, tk.W))
self.strength.set(10)
rowno += 1
# ファイル保存ボタンを作成
savebtn_widget = tk.Button(root,
text='ファイル保存',
command=self.show_save_dialog)
savebtn_widget.grid(column=0, row=rowno,
sticky=(tk.N))
self.CANVAS_WIDTH = 800
self.CANVAS_HEIGHT = 600
# 画像を表示するためのキャンバスの作成
self.canvas = tk.Canvas(self.root,
width=self.CANVAS_WIDTH,
height=self.CANVAS_HEIGHT)
self.canvas.grid(column=1, row=rowno, columnspan=2)
# 入力ファイル選択ボタン押下イベント
def select_file(self):
# ファイル選択ダイアログを表示
ftypes = [("All Files", ".*"),
("JPEG Image Files", ".jpg .jpeg"),
("PNG Image Files", ".png")]
if len(self.filepath) == 0:
idir = os.path.abspath(os.path.dirname(__file__))
else:
idir = os.path.dirname(self.filepath)
self.filepath = filedialog.askopenfilename(filetypes = ftypes,
initialdir = idir)
if len(self.filepath) == 0 :
return
self.src_path.set(self.filepath)
# 画像を読み取り
self.readimg = Image.open(self.filepath)
# 加工した画像を表示
self.draw_effect()
# 加工した画像を表示する
def draw_effect(self,val=0):
if self.readimg is None :
return
# 円を描画する開始位置、終了位置を計算する
w,h = self.readimg.size
upper = h * self.upper.get() / 100.0
lower = h * self.lower.get() / 100.0
h_start = int(min(upper,lower))
h_end = int(max(upper,lower))
# マスク画像を描画する
mask = Image.new("L", (w, h), 0)
draw = ImageDraw.Draw(mask)
draw.rectangle((0, h_start, w, h_end), fill=255)
mask_blur = mask.filter(
ImageFilter.GaussianBlur(
self.blur_radius.get()))
# ぼかし画像を作成する
blur_img = self.readimg.filter(ImageFilter.GaussianBlur(self.strength.get()))
# 元の画像と、塗りつぶし画像を合成する
self.dst_img = Image.composite(self.readimg, blur_img, mask_blur)
# 合成した画像の彩度を上げる
img_enhancer = ImageEnhance.Color(self.dst_img)
self.dst_img = img_enhancer.enhance(1.5)
# medianフィルタを適用する
self.dst_img = self.dst_img.filter(ImageFilter.MedianFilter(3))
# 画像の表示倍率を計算する
rate = min(1.0,
self.CANVAS_WIDTH / self.readimg.width,
self.CANVAS_HEIGHT / self.readimg.height)
# 表示用に画像を縮小する
new_w = int(self.readimg.width * rate)
new_h = int(self.readimg.height * rate)
small_resized = self.dst_img.resize((new_w, new_h))
# キャンバスに画像を表示する。
self.im = ImageTk.PhotoImage(image=small_resized)
self.canvas.create_image(0, 0, anchor=tk.NW, image=self.im)
# ファイル保存ボタンイベント
def show_save_dialog(self):
if self.dst_img is not None :
ftypes = [("All Files", ".*"),
("JPEG Image Files", ".jpg .jpeg"),
("PNG Image Files", ".png")]
ini_fname = os.path.basename(self.filepath)
filename = filedialog.asksaveasfilename(filetypes=ftypes,
initialfile=ini_fname)
if filename:
# 保存先ファイルが指定されたら、加工した画像を保存
self.dst_img.save(filename, quality=100)
else :
tk.messagebox.showerror(title="エラー",
message="入力ファイルを指定してください")
# windowを描画
window = tk.Tk()
# windowサイズを変更
window.geometry("1000x800")
# windowタイトルを設定
window.title("Miniture effect")
# フォームを作成、表示
FormClass(window)
# 画面を操作されるのを待つ
window.mainloop()
上のスクリプトをUTF-8のエンコーディングで、miniture_gui.py というファイルに保存します。 Windowsだとコマンドプロンプト等を開いて、スクリプトを保存したフォルダにcdコマンドで移動して、 python miniture_gui.py で実行します。 実行すると、
このような画面を表示します。 入力ファイル選択ボタンで、ファイル選択ダイアログを開きます。 そこでファイルを選択すると、
このように画像のプレビューを表示します。 「上位置」を大きくすると、上側のぼける面積が広くなります。 「下位置」を大きくすると、下側のぼける面積が狭くなります。 「境界ぼかし」を大きくすると、ぼける領域とぼけない領域の境がわかりにくくなります。 「ファイル保存ボタン」をクリックすると、保存先選択ダイアログを開きます。 以下、このスクリプトで加工した画像をアップします。
江の島にて
羽村堰の玉川上水
福生の公園
"""
トイカメラ風に周辺減光させる
tkinterを使ったGUI版
Usage : コマンドラインから以下のコマンドで起動
python toycamera_gui.py
"""
import os
from PIL import Image, ImageDraw, ImageFilter, ImageTk
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog, messagebox
class FormClass:
"""
Toycamera風に加工する画面のフォームを作成するクラス
"""
def __init__(self, root):
# インスタンス変数に初期値設定
self.root = root # Windowオブジェクト
self.readimg = None # 入力画像
self.dst_img = None # 加工結果画像
rowno = 0 # 表示位置(行番号)
# ラベルを作成
tk.Label(root, text='入力ファイルPATH'
).grid(column=0, row=rowno)
# 入力ファイル名
self.src_path = tk.StringVar()
# テキストボックスを作成
tb_widget = tk.Entry(root,
width=120,
textvariable=self.src_path)
tb_widget.grid(column=1, row=rowno)
# 入力ファイル選択ボタンを作成
btn_widget = tk.Button(root,
text='入力ファイル選択',
command=self.select_file)
btn_widget.grid(column=2, row=rowno)
rowno += 1
# 塗り残す範囲の比率 スケールの作成
tk.Label(root, text='塗り残す範囲の比率(%)'
).grid(column=0, row=rowno)
self.rate = tk.DoubleVar()
rate_sc = tk.Scale(
root,
variable=self.rate,
orient=tk.HORIZONTAL,
from_=0.0,
to=100,
command=self.draw_effect)
rate_sc.grid(column=1,
row=rowno,
sticky=(tk.N, tk.E, tk.S, tk.W))
self.rate.set(40)
rowno += 1
# ぼかし半径 スケールの作成
tk.Label(root, text='ぼかし半径'
).grid(column=0, row=rowno)
self.blur_radius = tk.IntVar()
blur_sc = tk.Scale(
root,
variable=self.blur_radius,
orient=tk.HORIZONTAL,
from_=0,
to=40.0,
command=self.draw_effect)
blur_sc.grid(column=1,
row=rowno,
sticky=(tk.N, tk.E, tk.S, tk.W))
self.blur_radius.set(20)
rowno += 1
# 効果の強さ スケールの作成
tk.Label(root, text='効果の強さ'
).grid(column=0, row=rowno)
self.strength = tk.IntVar()
strength_sc = tk.Scale(
root,
variable=self.strength,
orient=tk.HORIZONTAL,
from_=0,
to=255,
command=self.draw_effect)
strength_sc.grid(column=1,
row=rowno,
sticky=(tk.N, tk.E, tk.S, tk.W))
self.strength.set(80)
rowno += 1
# 塗りつぶし色 コンボボックスの作成
tk.Label(root, text='塗りつぶし色'
).grid(column=0, row=rowno)
self.select_color = tk.StringVar()
color_combo = ttk.Combobox(
root,
textvariable=self.select_color)
color_combo.grid(column=1,
row=rowno,
sticky=(tk.N, tk.W))
color_combo['values'] = ('白', '黒')
color_combo.current(1)
color_combo.bind("<>", self.draw_effect)
rowno += 1
# ファイル保存ボタンを作成
savebtn_widget = tk.Button(root,
text='ファイル保存',
command=self.show_save_dialog)
savebtn_widget.grid(column=0, row=rowno,
sticky=(tk.N))
self.CANVAS_WIDTH = 800
self.CANVAS_HEIGHT = 600
# 画像を表示するためのキャンバスの作成
self.canvas = tk.Canvas(self.root,
width=self.CANVAS_WIDTH,
height=self.CANVAS_HEIGHT)
self.canvas.grid(column=1, row=rowno, columnspan=2)
# 入力ファイル選択ボタン押下イベント
def select_file(self):
# ファイル選択ダイアログを表示
ftypes = [("All Files", ".*"),
("JPEG Image Files", ".jpg .jpeg"),
("PNG Image Files", ".png")]
filepath = self.src_path.get()
if len(filepath) == 0:
idir = os.path.abspath(os.path.dirname(__file__))
else:
idir = os.path.dirname(filepath)
filepath = filedialog.askopenfilename(filetypes = ftypes,
initialdir = idir)
if len(filepath) == 0 :
return
self.src_path.set(filepath)
# 画像を読み取り
self.readimg = Image.open(filepath)
# 加工した画像を表示
self.draw_effect()
# 加工した画像を表示する
def draw_effect(self,val=0):
if self.readimg is None :
return
# 塗りつぶし色
fill_color = None
if self.select_color.get() == '白' :
fill_color = (255, 255, 255)
else :
# 黒く塗りつぶす場合
fill_color = (0, 0, 0)
# 円を描画する開始位置、終了位置を計算する
w,h = self.readimg.size
radius = max(w,h) * self.rate.get() / 100.0
w_start = (w / 2) - radius
w_end = (w / 2) + radius
h_start = (h / 2) - radius
h_end = (h / 2) + radius
# マスク画像を描画する
mask = Image.new("L", (w, h), 255-self.strength.get())
draw = ImageDraw.Draw(mask)
draw.ellipse((w_start, h_start, w_end, h_end), fill=255)
mask_blur = mask.filter(
ImageFilter.GaussianBlur(
self.blur_radius.get()))
# 塗りつぶし画像を作成する
plane = Image.new("RGB", (w, h), fill_color)
# 元の画像と、塗りつぶし画像を合成する
self.dst_img = Image.composite(self.readimg, plane, mask_blur)
# 画像の表示倍率を計算する
rate = min(1.0,
self.CANVAS_WIDTH / self.readimg.width,
self.CANVAS_HEIGHT / self.readimg.height)
# 表示用に画像を縮小する
new_w = int(self.readimg.width * rate)
new_h = int(self.readimg.height * rate)
small_resized = self.dst_img.resize((new_w, new_h))
# キャンバスに画像を表示する。
self.im = ImageTk.PhotoImage(image=small_resized)
self.canvas.create_image(0, 0, anchor=tk.NW, image=self.im)
# ファイル保存ボタンイベント
def show_save_dialog(self):
if self.dst_img is not None :
ftypes = [("All Files", ".*"),
("JPEG Image Files", ".jpg .jpeg"),
("PNG Image Files", ".png")]
ini_fname = os.path.basename(self.src_path.get())
filename = filedialog.asksaveasfilename(filetypes=ftypes,
initialfile=ini_fname)
if filename:
# 保存先ファイルが指定されたら、加工した画像を保存
self.dst_img.save(filename, quality=100)
else :
tk.messagebox.showerror(title="エラー",
message="入力ファイルを指定してください")
# windowを描画
window = tk.Tk()
# windowサイズを変更
window.geometry("1000x800")
# windowタイトルを設定
window.title("Toycamera effect")
# フォームを作成、表示
FormClass(window)
# 画面を操作されるのを待つ
window.mainloop()
"""
トイカメラ風に周辺減光させる
Usage :
python toycamera.py 入力画像 出力画像 塗り残す比率 ぼかし半径
起動パラメータ
塗り残す比率 : 0.3 から 0.5 程度
ぼかし半径 : 20 から 50 程度
大きくすると塗りつぶしの境界がなだらかな調子になります
"""
import os
import sys
from PIL import Image, ImageDraw, ImageFilter
# 起動パラメータ数チェック
if len(sys.argv) < 3 :
print("usage : python toycamera.py 入力ファイル 出力ファイル 塗り残す比率 ぼかし半径")
sys.exit()
# 塗り残す範囲の比率
if len(sys.argv) > 3 :
r_rate = float(sys.argv[3])
else:
r_rate = 0.5
# ぼかし半径
if len(sys.argv) > 4 :
blur_radius = int(sys.argv[4])
else:
blur_radius = 50
if os.path.isfile(sys.argv[1]) is False :
print("入力ファイル名にファイルではない名前が指定されています。")
print("実行を中止します。")
sys.exit()
if os.path.isdir(sys.argv[2]):
print("出力ファイル名にディレクトリ名が指定されています。")
print("実行を中止します。")
sys.exit()
STRENGTH = 120 # 効果の強さ 0(最強)~255(効果なし)
print("以下の条件で処理を行います。")
print(f"入力ファイル {sys.argv[1]}")
print(f"出力ファイル {sys.argv[2]}")
print(f"塗り残す比率 {r_rate}")
print(f"ぼかし半径 {blur_radius}")
print(f"効果の強さ {STRENGTH}")
inkey = input("よろしいですか?")
if inkey not in ("y", "Y") :
print("実行を中止します。")
sys.exit()
if os.path.exists(sys.argv[2]):
print("\n出力ファイルが存在します。")
inkey = input("上書きしてよろしいですか?")
if inkey not in ("y", "Y") :
print("実行を中止します。")
sys.exit()
# 塗りつぶし色
# 白く塗りつぶす場合
#fill_color = (255, 255, 255)
# 黒く塗りつぶす場合
fill_color = (0, 0, 0)
# 画像を読み取り
im = Image.open(sys.argv[1])
# 円を描画する開始位置、終了位置を計算する
w,h = im.size
radius = max(w,h) * r_rate
w_start = (w / 2) - radius
w_end = (w / 2) + radius
h_start = (h / 2) - radius
h_end = (h / 2) + radius
# マスク画像を描画する
mask = Image.new("L", (w, h), STRENGTH)
draw = ImageDraw.Draw(mask)
draw.ellipse((w_start, h_start, w_end, h_end), fill=255)
mask_blur = mask.filter(ImageFilter.GaussianBlur(blur_radius))
# 塗りつぶし画像を作成する
plane = Image.new("RGB", (w, h), fill_color)
# 元の画像と、塗りつぶし画像を合成する
im2 = Image.composite(im, plane, mask_blur)
# 画像を表示
im2.show()
im2.save(sys.argv[2], quality=100)
最近、お出かけすることが減って、Pythonを勉強しています。
そのPython言語の、Pillowという画像ライブラリを使ったスクリプトです。
Pythonをインストールして、Pillowのライブラリをpipでインストールしておく
必要があります。
記事を書いている時点のPythonは3.8です。
前回はいろいろな大きさの円形をランダムな位置に描画するスクリプトを作りました、
今回は赤、緑、青、灰色の色スケールを描画するスクリプトです。
以下にスクリプトのコードを書きます。
"""
PillowでRGBスケールを描画する
"""
import random
from PIL import Image, ImageDraw
def main():
"""
画像作成処理
"""
BLOCK_WIDTH = 100 # ブロックの幅
BLOCK_GAP = 5 # ブロック間の幅
IMG_WIDTH = 4*BLOCK_WIDTH + 3*BLOCK_GAP # 画像の幅
BLOCK_HEIGHT = 32 # ブロックの高さ
BLOCK_STEP = 16 # ブロックの色の増分
IMG_HEIGHT = int(BLOCK_HEIGHT*(256/BLOCK_STEP+1)) # 画像の高さ
BACKGROUND = (255, 255, 255) # 背景色
# 画像を作成
im = Image.new("RGB", (IMG_WIDTH, IMG_HEIGHT), BACKGROUND)
draw = ImageDraw.Draw(im)
# 4つの色で繰り返し描画する
pos_x_st = 0
for i in range(4):
if i > 0 :
pos_x_st = pos_x_st + BLOCK_WIDTH + BLOCK_GAP
pos_x_en = pos_x_st + BLOCK_WIDTH
# 色ごとに明るさを変えて描画する
light = 0
r = 0
g = 0
b = 0
for j in range(int(256 / BLOCK_STEP + 1)):
pos_y_st = j * BLOCK_HEIGHT
pos_y_en = (j+1) * BLOCK_HEIGHT
# 各色 36から255の乱数を生成
if i == 0 :
# 赤スケール
r = light
elif i == 1 :
# 緑スケール
g = light
elif i == 2 :
# 青スケール
b = light
else :
# 白黒スケール
r = light
g = light
b = light
# 1つのブロックを描画する
draw.rectangle((pos_x_st, pos_y_st, pos_x_en, pos_y_en), (r,g,b))
# 次のブロックの明るさを計算する
light = light + BLOCK_STEP
if light > 255 :
light = 255
# 画像を保存
im.save("rgb_scale.png")
# 画像を表示
im.show()
if __name__ == '__main__':
# 画像作成処理を実行
main()
上のスクリプトをUTF-8のエンコーディングで、拡張子 .py のファイルにして保存します。
Windowsだとコマンドプロンプト等を開いて、スクリプトを保存したフォルダにcdコマンドで移動して、
python スクリプトファイル名
で実行します。 実行すると、
こんな感じの画像が表示されると思います。
スクリプトの中の、BLOCK_HEIGHT を 32 から 4 に、
BLOCK_STEP を 16 から 2 に変更して、実行すると、
こんな感じの画像になります。
出力画像の大きさが少し変わってしまうのは、ご愛敬ということで。
まじめにディスプレイの調整をしたいのであれば、
https://www.eizo.co.jp/eizolibrary/other/itmedia04/
こちらのページから、いろいろな確認用の画像が入手できます。