前回、カラー画像を白黒画像(グレースケール)に変換するPythonのプログラムを作成しました。
今回は、画像に対してマウスでドラッグした部分にモザイクを入れるプログラムを作成します。
マウスドラッグの処理については、前回までのプログラムで用意したキャンバスの機能を利用します。
白黒画像(グレースケール)への変換は、画像処理モジュールのPillowに関数が用意されていましたが、僕がイメージしている、いわゆるモザイク処理については、適当な関数が用意されておらず、自作することにしました。その部分については詳しく後述します。
では、プログラム全体を見てみましょう。
# -*- coding: utf-8 -*- | |
import tkinter | |
from PIL import Image, ImageTk | |
# パラメータ | |
mouse_c = 0 | |
x0 = 0 | |
y0 = 0 | |
x1 = 0 | |
y1 = 0 | |
# モザイクサイズ | |
mosaic_size = 15 | |
read_image = None | |
img = None | |
cvs = None | |
# モザイク処理(mos_size=1 -> 1px, 2 -> 4px, ...) | |
def make_mosaic(img, x1, y1, x2, y2, mos_size): | |
ret_img = img.copy() | |
for x in range(x1, x2, mos_size): | |
for y in range(y1, y2, mos_size): | |
r=g=b=0 | |
endx = x + mos_size if x + mos_size < x2 else x2 | |
endy = y + mos_size if y + mos_size < y2 else y2 | |
mos_size2 = (endx - x) * (endy - y) | |
for i in range(x, endx): | |
for j in range(y, endy): | |
rij,gij,bij = img.getpixel((i, j)) | |
r += rij | |
g += gij | |
b += bij | |
r = (int)(r / mos_size2) | |
g = (int)(g / mos_size2) | |
b = (int)(b / mos_size2) | |
for i in range(x, endx): | |
for j in range(y, endy): | |
ret_img.putpixel((i, j), (r, g, b, 0)) | |
return ret_img | |
# マウスボタンを押したときの関数 | |
def mouse_press(e): | |
global mouse_c, x0, y0 | |
# 左クリックされたフラグを立てる | |
mouse_c = 1 | |
# ドラッグ範囲を描画 | |
cvs.create_rectangle(e.x, e.y, e.x+1, e.y+1, outline="red", tag="rect1") | |
# マウスボタンを押したときの座標をx, yにセット | |
x0 = e.x | |
y0 = e.y | |
# マウスを動かしているときの関数 | |
def mouse_move(e): | |
global mouse_c, x0, y0 | |
# ドラッグされた場合、範囲を更新 | |
if mouse_c == 1: | |
cvs.coords("rect1", x0, y0, e.x, e.y) | |
# マウスボタンを離したときの関数 | |
def mouse_release(e): | |
global cvs, img, read_image, mouse_c, x1, y1, mosaic_size | |
# 左クリックされたフラグを下ろす | |
mouse_c = 0 | |
# マウスボタンを離したときの座標をx1, y1にセット | |
x1 = e.x | |
y1 = e.y | |
# 描写していたドラッグ範囲を消す | |
cvs.delete("rect1") | |
# 選択範囲に対してモザイク処理 | |
read_image = make_mosaic(read_image, x0, y0, x1, y1, mosaic_size) | |
# 編集した画像を画面に反映 | |
img = ImageTk.PhotoImage(image=read_image) | |
cvs.create_image(0, 0, image=img, anchor='nw') | |
if __name__ == "__main__": | |
# ファイル名指定 | |
fname = "neko.jpg" | |
# ファイルを開く | |
read_image = Image.open(fname) | |
# 画像サイズを取得 | |
w, h = read_image.size | |
# GUI作成 | |
root = tkinter.Tk() | |
root.title("Image Test 3") | |
root.resizable(False, False) | |
# tkinterで表示できるように画像変換 | |
img = ImageTk.PhotoImage(image=read_image) | |
# Canvasの準備 | |
cvs = tkinter.Canvas(root, bg="black", width=w, height=h) | |
# Canvasに画像を描画 | |
cvs.create_image(0, 0, image=img, anchor='nw') | |
# Canvasを配置しマウスのイベントを設定 | |
cvs.pack() | |
cvs.bind("<Motion>", mouse_move) | |
cvs.bind("<ButtonPress>", mouse_press) | |
cvs.bind("<ButtonRelease>", mouse_release) | |
root.mainloop() |
ベースは前回のPythonで画像処理(2)で使用したプログラムですが、今回は、マウス操作の処理とモザイク処理を追加しています。追加部分について見ていきます。
6行目から10行目では、プログラム全体で使用するパラメータを初期化しています。
mouse_c = 0は、マウスクリックフラグです。クリック状態を1、未クリック状態を0とします。初期化時点では、0にします。
x0、y0、x1、y1は、それぞれ、クリック開始時のX座標、Y座標、クリック終了時のX座標、Y座標です。初期化時点では、0を代入しておきます。
13行目のmosaic_sizeはモザイクサイズを定義します。今回作成するモザイクは、ある程度の大きさの正方形をいくつか連ねるモザイクです。mosaic_sizeで指定するサイズは、正方形の一辺の長さ(px)となります。今回は一辺の長さが15pxの正方形を連ねたモザイクを作成します。
20行目から41行目のmake_mosaic関数が、モザイクを作成する関数です。
細かい処理を行っているので、詳細は割愛しますが、処理概要について記載しておきます。
1.関数実行元から引数で、元画像、ドラッグ開始座標、ドラッグ終了画像、モザイクサイズを受け取る(20行目)
2.処理前の画像をコピーする(21行目)
3.モザイクサイズ×モザイクサイズの正方形内のR、G、Bの値をそれぞれ足し合わせ、平均値を計算し、正方形内のR、G、B値をすべて平均値で置き換える(23行目〜40行目)
4.処理後の画像を戻り値で返す(41行目)
44行目から55行目は、マウスを左クリックした際に呼び出される関数「mouse_press」です。
45行目で、関数内でグローバル変数を利用するため、global定義しています。
48行目で、左クリックされたフラグを立てます。
51行目で、ドラッグ範囲を赤四角で描写します。描写した四角は「rect1」という名前をつけています。
引数のeには、マウスクリックに関する変数が代入されており、e.x、e.yで座標が取り出せます。ドラッグ開始位置の座標として、x0、y0に代入します。
58行目から63行目は、マウスを動かしているときに呼び出される関数「mouse_move」です。
62行目で、処理をマウスクリックされている場合のみに限定しています。
63行目で、ドラッグ範囲の赤四角「rect1」の大きさをドラッグ位置に合わせて更新しています。
67行目から85行目は、マウスの左クリックを終えた際に呼び出される関数「mouse_release」です。
71行目で、左クリックされたフラグを下ろします。
74行目、75行目でマウスクリックを終えた際の座標をx1、y1に代入しています。
78行目で、ドラッグ範囲の赤四角「rect1」を消去します。
81行目で、モザイク処理関数「make_mosaic」を呼び出し、モザイク後の画像をread_imageに上書きで受け取ります。
84行目、85行目で、処理後の画像でGUIの表示画像を更新しています。
87行目から117行目は、本プログラムのメイン処理です。
89行目で、画像のファイル名を指定しています。今回は、「neko.jpg」という画像をプログラムと同一フォルダに置いています。下記画像です。

実家の猫です。きゃわいいですね。
続いて、92行目で画像を開き、95行目で画像サイズを取得しています。
98行目から100行目でGUIを作成し、103行目でGUI表示用に画像を変換しています。
106行目では、マウスクリック処理用でキャンバスを用意します。
109行目で変換後の画像を貼り付けます。
112行目で、作成したキャンバスをGUIに反映します。
113行目から115行目では、キャンバスに対するマウス操作イベントをそれぞれ前述した関数に紐づけています。
・マウス移動ー>「mouse_move」関数
・マウスクリックー>「mouse_press」関数
・マウスクリック終了ー>「mouse_release」関数
今回作成したプログラムの説明は以上です。
それでは動かしてみましょう。
プログラムを実行すると先程のneko.jpgがGUI画面で表示されます。

そして、モザイクを入れたい部分をドラッグすると、下記のようにモザイクが表示されます。

笑。まあ、コンプライアンスですね。
ちなみに、モザイクサイズを小さくすればモザイクの四角が小さくなります。
mosaic_sizeを「15」から「5」に変更して、実行してみます。

ギ◯モザですね笑。
画像のドラッグ部分にモザイクを入れることができました。
次回は、写真を油絵のように加工する処理をやってみたいと思います。
コメント