【Python/Pillow(PIL)】JPEG画像の品質を指定して保存する

JPEG画像の保存では、品質を指定することで、画像のファイルサイズが変わります。

品質を良くすると、画像はキレイになりますが、ファイルサイズが大きくなります。

逆に品質を下げると、画像にノイズが乗りますが、ファイルサイズが小さくなります。

JPEG画像の品質を下げたときに乗る代表的なノイズとして、ブロックノイズモスキートノイズというものがあります。

品質を指定してJPEGファイルを保存するときの構文は以下の通りです。

Image.save(jpegfilename, quality = 75)

となります。

品質の値(quality)は初期値が75で指定可能なのは、0(低品質)~95(高品質)です。

サンプル

from PIL import Image

# 画像を開く
img = Image.open("Mandrill.bmp")

# JPEGで品質を指定して画像を保存する
img.save("Mandrill_q10.jpg", quality = 10)
img.save("Mandrill_q50.jpg", quality = 50)
img.save("Mandrill_q75.jpg", quality = 75)
img.save("Mandrill_q95.jpg", quality = 95)

# 画像を開く
img = Image.open("ImagingSolution.bmp")

# JPEGで品質を指定して画像を保存する
img.save("ImagingSolution_q10.jpg", quality = 10)
img.save("ImagingSolution_q50.jpg", quality = 50)
img.save("ImagingSolution_q75.jpg", quality = 75)
img.save("ImagingSolution_q95.jpg", quality = 95)

入力画像

評価用に読み込む画像は非圧縮のビットマップファイル(*.bmp)にしました。

一つは一般的な画像としてマンドリルの画像と、もう一つはモノクロでエッジの強い文字の画像にしました。

Mandrill.bmp
256×256画素24bitカラー
196,662バイト(非圧縮)
ImagingSolution.bmp
256×256画素8bitグレースケール
66,614バイト(非圧縮)

ファイルサイズの比較

quality 画像 サイズ(バイト)
10 4,465
50 13,189
75 19,908
95 47,073
10 3,594
50 6,629
75 8,487
95 15,452

等倍の画像で見ると qualityが50以上であれば、見た目に差はさほど感じられないでしょうか?

qualityが75と95とで比較すると、特に文字の画像を見ると、ほんの少しだけエッジがくっきり見えますが、ファイルサイズが倍近く異なります。

ノイズの比較

画像の中心付近を拡大して、ノイズを確認してみます。

quality = 10

quality = 50

quality = 75

quality = 95

quality = 10

quality = 50

quality = 75

quality = 95

マンドリルの画像ではquality = 50ぐらいまでが、四角いマス状のノイズ(ブロックノイズ)が確認できます。

文字の画像は、明暗の差が大きく、エッジが強い画像の代表例として用いたのですが、黒い周りにゴミのように見えるモスキートノイズが確認できます。

 

ノイズの出方やファイルサイズの両方を考えると、qualityの初期値である 75 ぐらいがバランスが良さそうです。

参考

https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.save

https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html

【Python/Pillow(PIL)】画像の上下左右反転、90°ごとの回転

Pillowで画像(画像データ)を画像の上下左右反転、90°ごとの回転をさせるには、Imageクラスのtransposeメソッドを用います。

これとは別にImageOpsモジュールmirror関数(左右反転)flip関数(上下反転)があるので、これは後半で説明します。

構文

Image.transpose(method)

パラメータ

引数名 説明
method 上下左右反転、90°ごとの回転の種類を以下の中から指定します。
Image.FLIP_LEFT_RIGHT
Image.FLIP_FLIP_TOP_BOTTOM
Image.FLIP_ROTATE_90
Image.FLIP_ROTATE_180
Image.FLIP_ROTATE_270
Image.FLIP_TRANSPOSE
Image.FLIP_TRANSVERSE
戻り値 上下左右反転、回転した画像(PIL.Image)

サンプル

from PIL import Image

# 画像を開く
img = Image.open("image.jpg")

# 左右上下反転、90度ごとの回転
img_transpose = img.transpose(Image.FLIP_LEFT_RIGHT)

# 画像の保存
img_transpose.save("image_transpose_FLIP_LEFT_RIGHT.jpg")

(実行結果)

元画像 左右反転(Image.FLIP_LEFT_RIGHT)

上下左右、回転の種類

methodに指定する種類は以下の通りです。

Image.FLIP_LEFT_RIGHT Image.FLIP_TOP_BOTTOM
Image.ROTATE_180 Image.ROTATE_90
Image.ROTATE_270 元画像
Image.TRANSPOSE Image.TRANSVERSE

ImageOpsモジュールのmirror(左右反転)とflip(上下反転)

ImageOpsモジュールにも、左右反転(mirror)と上下反転(flip)の関数があります。

こちらの方が関数名的には覚えやすいでしょうか?

 

(サンプル)

from PIL import Image
from PIL import ImageOps

# 画像を開く
img = Image.open("image.jpg")

# 左右反転
img_mirror = ImageOps.mirror(img)
# 画像の保存
img_mirror.save("image_mirror.jpg")

# 上下反転
img_flip = ImageOps.flip(img)
# 画像の保存
img_flip.save("image_flip.jpg")

(実行結果)

ImageOps.mirror() ImageOps.flip()

参考

https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.transpose

https://pillow.readthedocs.io/en/stable/reference/ImageOps.html#PIL.ImageOps.flip

【Python/Pillow(PIL)】画像の回転

【Python/Pillow(PIL)】画像の回転

Pillowで画像(画像データ)を拡大/縮小するには、Imageクラスのrotateメソッドを用います。

構文

Image.rotate(angle, resample=0, expand=0, center=None, translate=None, fillcolor=None)

パラメータ

引数名 説明
angle 回転角度を度数(°)で指定します。
resample 回転時の補間方法を指定します。
Image.NEAREST
Image.BILINEAR
Image.BICUBIC
(参考)画像のリサイズ、補間指定
expand Trueのとき、画像が回転しても画像全体が表示されるよう画像サイズを拡張します。
False(初期値)のとき、画像が回転するしたときにはみ出した部分が切り取られます。
ただし、画像回転の中心は画像の中心で、平行移動が無い事を想定しています。
center 回転の中心座標を(x, y)のタプルで指定します。
初期値:画像の中心
translate 回転後の移動量を(Tx, Ty)のタプルで指定します。
fillcolor 画像の外側の色を指定します。
初期値:黒
戻り値 回転した画像(PIL.Image)

サンプル

from PIL import Image

# 画像を開く
img = Image.open("image.bmp")

# 画像を回転する
img_rotate = img.rotate(30)

# 回転した画像の保存
img_rotate.save("image_rotate.bmp")

(実行結果)

元画像 回転画像

画像がはみ出さないようにする

expand = True に設定することで、画像がはみ出さないよう、画像サイズを自動で拡張し調整します。

(例)

img_rotate = img.rotate(30, expand = True)

(結果)

画像の外側の色の指定

画像の外側の色をfillcolorに指定します。

(例)

img_rotate = img.rotate(30, fillcolor = "Blue")

(結果)

参考

https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.rotate

https://pillow.readthedocs.io/en/stable/handbook/concepts.html#concept-filters

 

【Python/Pillow(PIL)】画像のリサイズ、補間指定

Pillowで画像(画像データ)を拡大/縮小するには、Imageクラスのresizeメソッドを用います。

構文

Image.resize(size, resample=None, box=None, reducing_gap=None)

パラメータ

引数名 説明
size リサイズ後の大きさを (幅, 高さ)のタプルで指定します。
resample リサイズ時の補間方法を指定します。
Image.NEAREST
Image.BOX
Image.BILINEAR
Image.HAMMING
Image.BICUBIC
Image.LANCZOS
画像の mode が 1, P や I;16などのビット指定モードのときは、Image.NEAREST が初期値
その他の場合、Image.BICUBICが初期値
box 画像を拡大/縮小する領域を(左, 上, 右, 下)の座標のタプルで指定します。
初期値は(0, 0, 画像の幅, 画像の高さ)の画像全体
cropメソッドの処理と同じ
reducing_gap リサイズ時の最適化(詳細わからず。。)
初期値: None (最適化なし)
戻り値 リサイズされた画像(PIL.Image)

サンプル

from PIL import Image

# 画像を開く
img = Image.open("Parrots.bmp")

# 画像を拡大/縮小する
img_resize = img.resize((64, 64))

# 切り抜いた画像の保存
img_resize.save("Parrots_resize.bmp")

(実行結果)

元画像 縮小画像

resample(補間方法)について

resampleの設定は、主に画像を拡大するときに画像の画素と画素の間の輝度値を求める事になるのですが、この時の求め方の方法を指定します。

この画素間の輝度値を求める方法は一般に補間と言いますが、補間については、下記のページを参照ください。

画素の補間(Nearest neighbor,Bilinear,Bicubic)の計算方法

Pillowでは、補間方法にImage.NEAREST, Image.BOX, Image.BILINEAR, Image.HAMMING,  Image.BICUBIC, Image.LANCZOSの方法を指定する事ができますが、

NEAREST→BOX→BILINEAR→HAMMING→BICUBIC→LANCZOS

の順で、キレイにリサイズすることができますが、処理時間は逆に遅くなるため、用途に応じてresampleの設定を行ってください。

個人的には、画像処理後の1画素1画素の輝度値を見せたいときはNEAREST、とりあえずのBILINEARという使い分けが多いです。

下図のように部分的に画像を拡大したときに、resampleの設定を変えると、どのようになるか?見てみたいと思います。

 

NEAREST BOX BILINEAR
HAMMING BICUBIC LANCZOS

拡大の時のキレイさは、NEAREST, BOX → HAMMING → BILEAR → BICUBIC → LANCZOS の順ですかね?

参考

https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.resize

https://pillow.readthedocs.io/en/stable/handbook/concepts.html#concept-filters

【Python/Pillow(PIL)】画像の一部を切り抜く

Pillowで画像の一部を切り抜くには、Imageクラスのcropメソッドを用います。

書式は

Image.crop(box=None)
引数 説明
box 切り抜く領域を(左, 上, 右, 下)の座標のタプルで指定します。

 

(サンプルプログラム)

from PIL import Image

# 画像を開く
img = Image.open("Parrots.bmp")

# 画像を切り抜く
img_roi = img.crop((146, 81, 253, 183)) # (left, upper, right, lower)

# 切り抜いた画像の保存
img_roi.save("Parrots_roi.bmp")

(実行結果)

元画像 切り抜き画像

(補足)指定座標について

画像を切り抜く座標の定義ですが、画像の幅がWidth、画像の高さがHeightとすると、
画像の左上が(0, 0)、画像の右下が(Width-1, Height-1)となります。

実際に切り抜かれる画像の領域は、左上の座標(left, upper) を含み、右下の座標(right, lower) の1画素内側の領域が切り抜かれます。

参考

https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.crop

【OpenCV/Python】imreadで画像ファイルが読み込めないときの対応

opencv-pythonのimreadで画像を開き、以下のようなフィルタ処理を行うプログラムを実行すると、エラーが表示される場合があります。

import cv2

# OpenCVで画像ファイルを開く
img = cv2.imread("Mandrill.bmp", cv2.IMREAD_UNCHANGED)

# ガウシアンフィルタ
dst = cv2.GaussianBlur(img, (3, 3), 0)

cv2.imshow("Image", dst)
cv2.waitKey(0)

エラー内容

OpenCV(4.5.5) D:\a\opencv-python\opencv-python\opencv\modules\imgproc\src\smooth.dispatch.cpp:617: error: (-215:Assertion failed) !_src.empty() in function ‘cv::GaussianBlur’

 

OpenCVの場合、エラー内容の最後の方を見ると、だいたいエラーの原因がわかる場合が多いのですが、今回のエラーの内容の場合

  !_src.empty() in function ‘cv::GaussianBlur’

の部分が大事です。

直訳的に言うと、

  ’cv::GaussianBlur’関数の入力画像(src)は空(empty)ではいけません。

と言われています。

入力画像が空になる原因は imread()関数で画像ファイルが読み込めない場合がほとんどなのですが、読み込めない原因は、主に以下の2つです。

 

●画像ファイルがみつからない。(カレントディレクトリに画像ファイルが無い)

●ファイル名、ディレクトリ名に日本語が含まれている

 

画像ファイルがみつからない場合

imread()関数で、ファイル名の部分に相対パスでファイル名を指定すると、カレントディレクトリにあるファイルを探しに行くため、画像ファイルがカレントディレクトリに無いと、画像ファイルを開く事ができません。

【対策】

①ファイル名を相対パスではなく、絶対パス(フルパス)で指定する。

(例)

img = cv2.imread(r"C:\temp\Mandrill.bmp", cv2.IMREAD_UNCHANGED)

 

②カレントディレクトリを確認し、カレントディレクトリにファイルを置く。

カレントディレクトリは以下のようにすると、確認ができます。

import os

# カレントディレクトリの表示
print(os.getcwd())

ファイル名、ディレクトリ名に日本語が含まれている場合

OpenCVでは日本語を扱う事ができません。

そのため、画像ファイル名においても日本語が含まれるとimread関数で画像ファイルの読み込みに失敗します。

日本語ファイル名の画像を開くには、OpenCVではなく、PillowやNumPyを使って画像ファイルを開き、OpenCVの画像形式である ndarray へ変換する事ができます。

Pillowで画像ファイルを開いた方が、さまざまな画像ファイルフォーマットに対応しているので、汎用性が高いと思います。

(参考)

【OpenCV/Python】日本語の画像ファイル読込・保存

【Python/tkinter】図形の編集(削除、移動、変形など)

線や円などの図形の描画のページでは、tkinterでCanvas上に図形を描画しましたが、描画後に図形の移動や移動や変形、削除などを行う事ができ、その方法を紹介します。

ちょうどWordやExcelのように図形を選択し、移動や変形、削除、表示する順番の変更(最前面へ移動、最背面へ移動)などを行う操作と似ているので、これをイメージしてください。

実際にtkinterで、どのように図形を編集するのか?を紹介します。

tagとid

図形を編集するために、それぞれの図形を識別する必要があり、識別のためにid もしくは tag を用います。

id は create_line のように create_ から始まるメソッドを実行するたびに、1から始まるシリアル番号が返されます。

tag は図形を識別するために、create_ から始まるメソッドの tag オプションに任意文字列を指定します。
この文字列は重複していてもよく、複数の図形に同じtagの名前を指定すると、WordやExcelでいうところのグループ化のような事ができます。

(サンプル)

import tkinter as tk

root = tk.Tk()
root.geometry("300x200")

# Canvasの作成
canvas = tk.Canvas(root, bg = "white")
# Canvasを配置
canvas.pack(fill = tk.BOTH, expand = True)

# 線の描画
id1 = canvas.create_line(20, 10, 280, 190, fill = "Red", width = 5, tag="line")
# 矩形の描画
id2 = canvas.create_rectangle(100,50,200,150, fill="Yellow",width = 3, tag="rect")

root.mainloop()

(実行結果)

このプログラムを実行すると、id1=1, id2=2 となります。

 

以降は、このサンプルをベースに説明します。
また、tag もしくは idを tagOrId と表現します。

図形の削除(delete)

図形を削除します。

.delete(tagOrId)

.delete(“all”) とすると、Canvas上のすべての図形を削除します。
.delete(id1, “rect”)のように複数のtagOrIdを指定することもできます。

(例)

# 線の描画
id1 = canvas.create_line(20, 10, 280, 190, fill = "Red", width = 5, tag="line")
# 矩形の描画
id2 = canvas.create_rectangle(100,50,200,150, fill="Yellow",width = 3, tag="rect")

# 線の削除(id指定)
canvas.delete(id1)
# 線の削除(tag指定)
#canvas.delete("line")

 

 

 

 

 

図形の相対移動(move)

現在の位置からのX方向、Y方向の移動量を指定して、図形を移動します。(相対移動)

.move(tagOrId, x, y)
tagOrId 移動する図形のid もしくは tag
x X方向(右方向が正)に移動する量
y Y方向(下方向が正)に移動する量

(例)

# 線の描画
id1 = canvas.create_line(20, 10, 280, 190, fill = "Red", width = 5, tag="line")
# 矩形の描画
id2 = canvas.create_rectangle(100,50,200,150, fill="Yellow",width = 3, tag="rect")

# 矩形の移動(相対座標指定指定)
canvas.move(id2, 50, -30)

 

 

 

 

 

図形の拡大縮小(sacle)

点(xOffset, yOffset)を基点とした拡大縮小

.scale(tagOrId, xOffset, yOffset, xScale, yScale)
tagOrId 拡大縮小する図形のid もしくは tag
xOffset 基点となる点のx座標
yOffset 基点となる点のy座標
xScale
X方向(幅方向)の倍率
yScale Y方向(高さ方向)の倍率

(例1)矩形の左上の座標を基点として拡大縮小

# 矩形の描画
id = canvas.create_rectangle(100,50,200,150, fill="Yellow",width = 3, tag="rect")
canvas.create_rectangle(100,50,200,150) # 移動前の位置

# (xOffset, yOffset)を基点とした拡大縮小
canvas.scale(id, 100, 50, 1.5, 0.5) # 矩形の左上の座標を基点に拡大縮小

 

 

 

 

 

(例2)矩形の中心を基点として拡大縮小

# 矩形の描画
id = canvas.create_rectangle(100,50,200,150, fill="Yellow",width = 3, tag="rect")
canvas.create_rectangle(100,50,200,150) # 移動前の位置

# (xOffset, yOffset)を基点とした拡大縮小
canvas.scale(id, 150, 100, 1.5, 0.5) # 矩形の中心を基点に拡大縮小

 

 

 

 

 

図形を構成している座標を指定して変形(coords)

図形を作成した時の座標を書き換えます。

引数にtagOrIdのみを指定すると、図形を構成している座標を[x0, y0, x1, y1, …, xn, yn]のリストで取得します。

.coords(tagOrId, x0, y0, x1, y1, ..., xn, yn)

(例1)図形を構成している座標を指定して変形

# 線の描画
id = canvas.create_line(20, 10, 150, 190, 280, 50, fill = "Red", width = 5, tag="line")
canvas.create_line(20, 10, 150, 190, 280, 50) # 移動前の位置
# 図形を構成している座標を指定して変形
canvas.coords(id, 20, 50, 150, 120, 280, 100)

 

 

 

 

 

(例2)図形を構成している座標を取得し、部分的に座標を変更する

# 線の描画
id = canvas.create_line(20, 10, 150, 190, 280, 50, fill = "Red", width = 5, tag="line")
canvas.create_line(20, 10, 150, 190, 280, 50) # 移動前の位置
# 図形を構成している座標の取得
points = canvas.coords(id)
print(points) # [20.0, 10.0, 150.0, 190.0, 280.0, 50.0]
# 部分的に座標を修正
points[2:4]=[200, 80]
# 図形の変形
points = canvas.coords(id, points)

 

 

 

 

 

前面へ移動(lift)

図形の重なり具合を調整します。

tagOrIdで指定した図形をaboveThisの上へ移動します。

.lift(tagOrId, aboveThis)
tagOrId 重なりを調整する図形のid もしくは tag
aboveThis 移動先の図形のid もしくは tag
指定しない場合、最前面へ移動します。

(例)

# 矩形の描画
id1 = canvas.create_rectangle(20,20,120,120, fill="Red",width = 3)
# 線の描画
id2 = canvas.create_line(5,95,220,95, fill="Green",width = 10)
# 矩形の描画
id3 = canvas.create_rectangle(70,70,170,170, fill="Blue",width = 3)

# 矩形(id3)を矩形(id1)の上へ移動
canvas.lift(id3, id1)

 

 

 

 

 

背面へ移動(lower)

図形の重なり具合を調整します。

tagOrIdで指定した図形をbelowThisの下へ移動します。

.lower(tagOrId, belowThis)
tagOrId 重なりを調整する図形のid もしくは tag
belowThis 移動先の図形のid もしくは tag
指定しない場合、最背面へ移動します。

(例)

# 矩形の描画
id1 = canvas.create_rectangle(20,20,120,120, fill="Red",width = 3)
# 線の描画
id2 = canvas.create_line(5,95,220,95, fill="Green",width = 10)
# 矩形の描画
id3 = canvas.create_rectangle(70,70,170,170, fill="Blue",width = 3)

# 矩形(id3)を最背面移動
canvas.lower(id3)

 

 

 

 

 

参考

https://tkdocs.com/shipman/canvas-methods.html

【Python/tkinter】Canvas(キャンバス)の作成

【Python/tkinter】線や円などの図形の描画

【Python/tkinter】線や円などの図形の描画

前回は、Pillowで線や円などの図形の描画について説明しましたが、今回はtkinterでCanvasウィジェットの上に図形を描画する方法についてです。

前半では、線や矩形、楕円などのメソッドについて説明し、後半にこれらメソッドを用いて下図のような図形を描画するサンプルを示しています。

Pillowとtkinterで大きく異なるのは、Pillowでは画像の上に線などの図形を描画すると、図形データが画像データに反映されましたが、tkinterではCanvasウィジェットの上に図形を配置するイメージとなります。

ちょうどWordやExcelで図形を描画する事に似ています。

Wordの画面

 

Word/Excelでは図形の編集で、図形の移動や選択、削除、配置の順番の最前面へ移動/最背面へ移動などができますが、tkinterでも同様のことが出来ます。

tkinterでの図形の編集方法については図形の編集で説明しています。

ここでは、図形や画像を動画のように繰り返し描画すると、図形のオブジェクトが増え、描画速度が遅くなるため、図形を削除する必要がある事を覚えておいてください。
図形の削除方法は最後の部分に書きました。

簡単なサンプル

import tkinter as tk

root = tk.Tk()
root.geometry("300x200")

# Canvasの作成
canvas = tk.Canvas(root, bg = "white")
# Canvasを配置
canvas.pack(fill = tk.BOTH, expand = True)

# 線の描画
canvas.create_line(20, 10, 280, 190, fill = "Blue", width = 5)

root.mainloop()

図形の種類

図形はCanvasクラスにcreate_で始まる名前のメソッドが用意されています。

メソッド名 図形の種類
create_arc 円弧
create_bitmap ビットマップ
create_image イメージ
create_line 直線、折れ線
create_oval 楕円、円
create_polygon ポリゴン(閉じた多角形)
create_rectangle 矩形
create_text 文字
create_window ウィンドウ(ウィジェットの配置)

● 直線、折れ線

id = Canvas.create_line(x0, y0, x1, y1, ..., xn, yn, option, ...)
x0,y0… 線の始点と終点、もしくは折れ線を構成する交点の座標を指定します。
activedash マウスポインタが線上にあるときの破線のスタイルを設定します。
(例)activedash=(5,3) 
activefill マウスポインタが線上にあるときの線色を設定します。
(例)activefill=”Red”
activestipple マウスポインタが線上にあるときの線のパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
(例)activestipple= ‘gray50’  のとき(参考)https://tkdocs.com/shipman/bitmaps.html
activewidth マウスポインタが線上にあるときの線幅を設定します。
arrow 線の矢印の設定をします。
arrow=tk.FIRST のとき、始点に矢印を追加
arrow=tk.LAST のとき、終点に矢印を追加
arrow=tk.BOTH のとき、両方に矢印を追加
arrowshape 矢印の形状を(d1, d2, d3)のタプルで指定します。
初期値:arrowshape=(8, 10, 3)
capstyle 線の端の形状を設定します。
初期値:capstyle=tk.BUTT
(参考)https://tkdocs.com/shipman/cap-join-styles.html
dash 輪郭線の破線スタイルを(線の長さ, 間隔)のタプルで設定します。
(例)dash=(5,3)
dashoffset 破線の開始位置を設定します。
disableddash 線が無効状態(state=tk.DISABLED)のときの破線のスタイルを設定します。
disabledfill 線が無効状態(state=tk.DISABLED)のときの線色を設定します。
disabledstipple 線が無効状態(state=tk.DISABLED)のときの線のパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
disabledwidth 線が無効状態(state=tk.DISABLED)のときの線幅を設定します。
fill 線色を指定します。
joinstyle 折れ線のつなぎ目のスタイルを設定します。
(参考)https://tkdocs.com/shipman/cap-join-styles.html
offset 線のパターン(stipple)のパターンの開始位置を指定します。
smooth smooth=True のとき、折れ線はスプライン曲線となります。
splinesteps スプライン曲線のセグメント数を設定します。
state 線の状態を設定します。
(初期値)state=tk.NORMAL
state=tk.HIDDEN のとき、線の非表示
state=tk.DISABLEDのとき、マウスオーバーの処理の無効
stipple 線のパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
(参考)https://tkdocs.com/shipman/bitmaps.html
tags 線の識別用任意文字列もしくは、文字列のタプルを指定します。
width 線幅を指定します。
戻り値 id, 線の識別用番号

● 矩形(四角形)

id = Canvas.create_rectangle(x0, y0, x1, y1, option, ...)
x0,y0… 左上の座標(x0, y0)と右下の座標(x1, y1)を指定します。
描画される矩形は、(x0, y0)の座標を含み、(x1, y1)より1画素内側に描画されます。
activedash マウスポインタが図形上にあるときの輪郭線の破線のスタイルを設定します。
(例)activedash=(5,3) 
activefill マウスポインタが図形上にあるときの塗りつぶしの色を設定します。
(例)activefill=”Red”
activeoutline
マウスポインタが図形上にあるときの輪郭線の色を設定します。
activeoutlinestipple マウスポインタが図形上にあるときの輪郭線のパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
(例)activestipple= ‘gray50’  のとき(参考)https://tkdocs.com/shipman/bitmaps.html
activestipple マウスポインタが図形上にあるときの塗りつぶしのパターンを設定します。
activewidth マウスポインタが図形上にあるときの線幅を設定します。
dash 輪郭線の破線スタイルを(線の長さ, 間隔)のタプルで設定します。
(例)dash=(5,3)
dashoffset 破線の開始位置を設定します。
disableddash 線が無効状態(state=tk.DISABLED)のときの破線のスタイルを設定します。
disabledfill 線が無効状態(state=tk.DISABLED)のときの線色を設定します。
disabledoutline
線が無効状態(state=tk.DISABLED)のときの輪郭線の色を設定します。
disabledoutlinestipple 線が無効状態(state=tk.DISABLED)のときの 輪郭線のパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
disabledstipple 線が無効状態(state=tk.DISABLED)のときの塗りつぶしのパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
disabledwidth 線が無効状態(state=tk.DISABLED)のときの線幅を設定します。
fill 塗りつぶしの色を指定します。
offset 塗りつぶしのパターン(stipple)のパターンの開始位置を指定します。
outline
輪郭線の色を指定します。
outlineoffset 輪郭線のパターン(outlinestipple)のパターンの開始位置を指定します。
outlinestipple 輪郭線のパターンを指定します。
state 線の状態を設定します。
(初期値)state=tk.NORMAL
state=tk.HIDDEN のとき、線の非表示
state=tk.DISABLEDのとき、マウスオーバーの処理の無効
stipple 線のパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
(参考)https://tkdocs.com/shipman/bitmaps.html
tags 矩形の識別用任意文字列もしくは、文字列のタプルを指定します。
width 線幅を指定します。
戻り値 id, 矩形の識別用番号

● 文字

id = Canvas.create_text(x, y, option, ...)
x, y 文字を表示する位置の座標(x, y)を指定します。
座標の位置(文字の左上や中央など)はanchorオプションに依存します。
activestipple マウスポインタが文字上にあるときの塗りつぶしのパターンを設定します。
anchor 表示位置の座標の基準となる位置を指定します。
(初期値)anchor=tk.CENTER
disabledfill 文字が無効状態(state=tk.DISABLED)のときの文字色を設定します。
disabledstipple 線文字無効状態(state=tk.DISABLED)のときの文字のパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
fill 文字色を指定します。
font 文字のフォントを指定します。
justify
文字列を複数行のまたぐときの、左寄せ(tk.LEFT)、中央寄せ(tk.CENTER)、右寄せ(tk.RIGHT)を設定します。
offset 線のパターン(stipple)のパターンの開始位置を指定します。
smooth smooth=True のとき、折れ線はスプライン曲線となります。
splinesteps スプライン曲線のセグメント数を設定します。
state 線の状態を設定します。
(初期値)state=tk.NORMAL
state=tk.HIDDEN のとき、線の非表示
state=tk.DISABLEDのとき、マウスオーバーの処理の無効
stipple 線のパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
(参考)https://tkdocs.com/shipman/bitmaps.html
tags 文字の識別用任意文字列もしくは、文字列のタプルを指定します。
text 描画する文字列を指定します。(日本語可)
width 文字列を描画する領域の幅を指定します。
描画する文字(text)が幅(width)より大きい場合、文字列は押し返し表示されます。
戻り値 id, 線の識別用番号

●楕円、円

id = C.create_oval(x0, y0, x1, y1, option, ...)
x0,y0,x1,y1 左上の座標(x0, y0)と右下の座標(x1, y1)を指定します。
描画される矩形は、(x0, y0)の座標を含み、(x1, y1)より1画素内側に描画されます。
activedash マウスポインタが図形上にあるときの輪郭線の破線のスタイルを設定します。
(例)activedash=(5,3) 
activefill マウスポインタが図形上にあるときの塗りつぶしの色を設定します。
(例)activefill=”Red”
activeoutline
マウスポインタが図形上にあるときの輪郭線の色を設定します。
activeoutlinestipple マウスポインタが図形上にあるときの輪郭線のパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
(例)activestipple= ‘gray50’  のとき(参考)https://tkdocs.com/shipman/bitmaps.html
activestipple マウスポインタが図形上にあるときの塗りつぶしのパターンを設定します。
activewidth マウスポインタが図形上にあるときの線幅を設定します。
dash 輪郭線の破線スタイルを(線の長さ, 間隔)のタプルで設定します。
(例)dash=(5,3)
dashoffset 破線の開始位置を設定します。
disableddash 線が無効状態(state=tk.DISABLED)のときの破線のスタイルを設定します。
disabledfill 線が無効状態(state=tk.DISABLED)のときの線色を設定します。
disabledoutline
線が無効状態(state=tk.DISABLED)のときの輪郭線の色を設定します。
disabledoutlinestipple 線が無効状態(state=tk.DISABLED)のときの 輪郭線のパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
disabledstipple 線が無効状態(state=tk.DISABLED)のときの塗りつぶしのパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
disabledwidth 線が無効状態(state=tk.DISABLED)のときの線幅を設定します。
fill 領域を塗りつぶす色を指定します。
offset 塗りつぶしのパターン(stipple)のパターンの開始位置を指定します。
outline 輪郭線の色を指定します。
outlineoffset 輪郭線のパターン(outlinestipple)のパターンの開始位置を指定します。
stipple 線のパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
(参考)https://tkdocs.com/shipman/bitmaps.html
outlinestipple 輪郭線のパターンを指定します。
state 線の状態を設定します。
(初期値)state=tk.NORMAL
state=tk.HIDDEN のとき、線の非表示
state=tk.DISABLEDのとき、マウスオーバーの処理の無効
tags 楕円の識別用任意文字列もしくは、文字列のタプルを指定します。
width 線幅を指定します。
戻り値 id, 楕円の識別用番号

●ビットマップ

ビットマップの描画を行います。
ここでいうビットマップは二値化された画像で、一般的な画像の描画はcreate_imageを用います。
(参考)https://tkdocs.com/shipman/bitmaps.html

id = C.create_bitmap(x, y, *options ...)
x, y ビットマップを表示する位置の座標(x, y)を指定します。
座標の位置(ビットマップの左上や中央など)はanchorオプションに依存します。。
activebackground マウスポインタがビットマップ上にあるときの背景色を設定します。
activebitmap マウスポインタがビットマップ上にあるときのビットマップを設定します。
activeforeground マウスポインタがビットマップ上にあるときのビットマップの色を設定します。
anchor 表示位置の座標の基準となる位置を指定します。
(初期値)anchor=tk.CENTER 
background ビットマップの背景色を指定します。
bitmap 表示するビットマップを指定します。
disabledbackground 無効状態(state=tk.DISABLED)のときの背景色を設定します。
disabledbitmap 無効状態(state=tk.DISABLED)のときのビットマップを設定します。
disabledforeground 線が無効状態(state=tk.DISABLED)のときのビットマップの色を設定します。
foreground ビットマップの色を指定します。
state ビットマップの状態を設定します。
(初期値)state=tk.NORMAL
state=tk.HIDDEN のとき、線の非表示
state=tk.DISABLEDのとき、マウスオーバーの処理の無効
tags ビットマップの識別用任意文字列もしくは、文字列のタプルを指定します。
戻り値 id, ビットマップの識別用番号

●イメージ

イメージ(Pillow(PIL)のPhotoImage もしくは tkinterのBitmapImage,PhotoImage)の描画を行います。

id = C.create_image(x, y, *options ...)
x, y ビットマップを表示する位置の座標(x, y)を指定します。
座標の位置(ビットマップの左上や中央など)はanchorオプションに依存します。
activeimage マウスポインタが画像上にあるときのイメージを設定します。
anchor 表示位置の座標の基準となる位置を指定します。
(初期値)anchor=tk.CENTER
disabledimage 無効状態(state=tk.DISABLED)のときのイメージを設定します。
image イメージを指定します。
通常はPillow(PIL)のPhotoImageを指定します。
state ビットマップの状態を設定します。
(初期値)state=tk.NORMAL
state=tk.HIDDEN のとき、線の非表示
state=tk.DISABLEDのとき、マウスオーバーの処理の無効
tags イメージの識別用任意文字列もしくは、文字列のタプルを指定します。
戻り値 id, イメージの識別用番号

● ポリゴン

id = Canvas.create_polygon(x0, y0, x1, y1, ..., xn, yn, option, ...)
x0,y0… 線の始点と終点、もしくは折れ線を構成する交点の座標を指定します。
activedash マウスポインタが線上にあるときの破線のスタイルを設定します。
(例)activedash=(5,3) 
activefill マウスポインタが線上にあるときの線色を設定します。
(例)activefill=”Red”
activeoutline
マウスポインタが線上にあるときの輪郭線の色を設定します。
activelinestipple マウスポインタが線上にあるときの線のパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
activestipple マウスポインタが図形上にあるときの塗りつぶしのパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
(例)activestipple= ‘gray50’  のとき(参考)https://tkdocs.com/shipman/bitmaps.html
activewidth マウスポインタが線上にあるときの線幅を設定します。
dash 輪郭線の破線スタイルを(線の長さ, 間隔)のタプルで設定します。
(例)dash=(5,3)
dashoffset 破線の開始位置を設定します。
disableddash 線が無効状態(state=tk.DISABLED)のときの破線のスタイルを設定します。
disabledfill 線が無効状態(state=tk.DISABLED)のときの線色を設定します。
disabledstipple 線が無効状態(state=tk.DISABLED)のときの線のパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
disabledwidth 線が無効状態(state=tk.DISABLED)のときの線幅を設定します。
fill 塗りつぶす色を指定します。
joinstyle 折れ線のつなぎ目のスタイルを設定します。
tk.ROUND, tk.BEVEL, tk.MITER
(参考)https://tkdocs.com/shipman/cap-join-styles.html
offset 線のパターン(stipple)のパターンの開始位置を指定します。
outline 輪郭線の色を指定します。
outlineoffset 輪郭線のパターン(outlinestipple)のパターンの開始位置を指定します。
outlinestipple 輪郭線のパターンを指定します。
smooth smooth=True のとき、折れ線はスプライン曲線となります。
splinesteps スプライン曲線のセグメント数を設定します。
state 線の状態を設定します。
(初期値)state=tk.NORMAL
state=tk.HIDDEN のとき、線の非表示
state=tk.DISABLEDのとき、マウスオーバーの処理の無効
stipple 線のパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
(参考)https://tkdocs.com/shipman/bitmaps.html
tags ポリゴンの識別用任意文字列もしくは、文字列のタプルを指定します。
width 線幅を指定します。
戻り値 id, ポリゴンの識別用番号

● 円弧

id = Canvas.create_arc(x0, y0, x1, y1, option, ...)
x0,y0… 線の始点と終点、もしくは折れ線を構成する交点の座標を指定します。
activedash マウスポインタが線上にあるときの破線のスタイルを設定します。
(例)activedash=(5,3) 
activefill マウスポインタが線上にあるときの線色を設定します。
(例)activefill=”Red”
activeoutline
マウスポインタが線上にあるときの輪郭線の色を設定します。
activelinestipple マウスポインタが線上にあるときの線のパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
activestipple マウスポインタが図形上にあるときの塗りつぶしのパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
(例)activestipple= ‘gray50’  のとき(参考)https://tkdocs.com/shipman/bitmaps.html
activewidth マウスポインタが線上にあるときの線幅を設定します。
dash 輪郭線の破線スタイルを(線の長さ, 間隔)のタプルで設定します。
(例)dash=(5,3)
dashoffset 破線の開始位置を設定します。
disableddash 線が無効状態(state=tk.DISABLED)のときの破線のスタイルを設定します。
disabledfill 線が無効状態(state=tk.DISABLED)のときの線色を設定します。
disabledstipple 線が無効状態(state=tk.DISABLED)のときの線のパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
disabledwidth 線が無効状態(state=tk.DISABLED)のときの線幅を設定します。
extent 開始角度(start)から終了位置までの角度(反時計周りが正)
fill 塗りつぶす色を指定します。
offset 線のパターン(stipple)のパターンの開始位置を指定します。
outline 輪郭線の色を指定します。
outlineoffset 輪郭線のパターン(outlinestipple)のパターンの開始位置を指定します。
outlinestipple 輪郭線のパターンを指定します。
start
円弧の開始位置の角度(度)を指定します。3時方向が0度
state 線の状態を設定します。
(初期値)state=tk.NORMAL
state=tk.HIDDEN のとき、線の非表示
state=tk.DISABLEDのとき、マウスオーバーの処理の無効
stipple 線のパターンを設定します。
‘gray75’, ‘gray50’, ‘gray25’, ‘gray12’, ‘hourglass’, ‘info’, ‘questhead’, ‘question’, ‘warning’ のいづれか
(参考)https://tkdocs.com/shipman/bitmaps.html
style
円弧のスタイルを指定します。
tk.PIESLICE (初期値) tk.CHORD tk.ARC
tags 円弧の識別用任意文字列もしくは、文字列のタプルを指定します。
width 線幅を指定します。
戻り値 id, 円弧の識別用番号

● ウィンドウ

Canvas上にウィジェットを配置します。

id = Canvas.create_window(x, y, option, ...)
x, y 文字を表示する位置の座標(x, y)を指定します。
座標の位置(文字の左上や中央など)はanchorオプションに依存します。
anchor 表示位置の座標の基準となる位置を指定します。
(初期値)anchor=tk.CENTER
height ウィジェットを配置する幅を設定します。
state ウィンドウの状態を設定します。
(初期値)state=tk.NORMAL
state=tk.HIDDEN のとき、線の非表示
state=tk.DISABLEDのとき、マウスオーバーの処理の無効
tags ウィンドウの識別用任意文字列もしくは、文字列のタプルを指定します。
width
ウィジェットを配置する高さを設定します。
window 配置するウィジェットオブジェクトを設定します。
戻り値 id, ウィンドウの識別用番号

備考

色の指定は”Red”, ”Green”などの色の名前の文字列を使うか、R,G,Bの順で16進数で指定(#FF0000だと赤)するか、(R,G,B)のタプルで指定します。

(参考)

HTML Color Names
W3Schools offers free online tutorials, references and exercises in all the major languages of the web. Covering popular subjects like HTML, CSS, JavaScript, Py…

楕円や円弧の座標指定位置は、下図のようになります。

サンプル

各種図形のオプション設定を行ったサンプルです。

import tkinter as tk
import os
from PIL import ImageTk

root = tk.Tk()
root.geometry("600x750")

# Canvasの作成
canvas = tk.Canvas(root, bg = "white")
# Canvasを配置
canvas.pack(fill = tk.BOTH, expand = True)

#######################################
# 線
canvas.create_line(10,   20, 290,  20)                  # オプションなし
canvas.create_line(310,  20, 590,  20, width = 5)       # 線幅5

canvas.create_line(10,   40, 290,  40, arrow=tk.FIRST)  # 始点の矢印
canvas.create_line(310,  40, 590,  40, arrow=tk.LAST)   # 終点の矢印
canvas.create_line(10,   60, 290,  60, arrow=tk.BOTH)   # 両方の矢印
canvas.create_line(310,  60, 590,  60, arrow=tk.BOTH, arrowshape=(24, 25, 8))   # 矢印のサイズ指定

canvas.create_line(10,   80, 290,  80, dash=(5, 3))         # 破線
canvas.create_line(310,  80, 590,  80, dash=(20, 3, 5, 3))  # 破線(1点鎖線)

canvas.create_line(10,  100, 290, 100, width = 15, capstyle=tk.BUTT)        # 終端形状
canvas.create_line(310, 100, 590, 100, width = 15, capstyle=tk.PROJECTING)  
canvas.create_line(10,  120, 290, 120, width = 15, capstyle=tk.ROUND)

canvas.create_line(10,  140, 290, 140, width = 15, stipple='gray12') # 線のパターン
canvas.create_line(310, 140, 590, 140, width = 15, stipple='gray50')

canvas.create_line(10, 160, 150, 180, 300, 160, 450, 180, 590, 160 ) # 折れ線


#######################################
# 矩形
canvas.create_rectangle(10, 200, 290, 220)              # オプションなし
canvas.create_rectangle(310,200, 590, 220, width = 5)   # 線幅5
canvas.create_rectangle(10, 240, 290, 260, fill="Blue") # 塗りつぶしの色指定
canvas.create_rectangle(310,240, 590, 260, fill="Green", outline="Red",width = 2) # 塗りつぶし、輪郭線の色指定

#######################################
# 文字
canvas.create_text(10, 300, text="あいうえお", anchor=tk.NW, font=("",20))   # 基準位置左上
canvas.create_oval(5, 295, 15, 305, fill="Red")

canvas.create_text(300, 300, text="あいうえお", font=("",20))                # 基準位置指定なし(CENTER)
canvas.create_oval(295, 295, 305, 305, fill="Red")

canvas.create_text(590, 300, text="あいうえお", anchor=tk.SE, font=("",20))  # 基準位置右下
canvas.create_oval(585, 295, 595, 305, fill="Red")

canvas.create_text(10, 340, text="あいうえお\nかきくけこ\nさしすせそ", anchor=tk.NW,font=("",20))          # 改行文字
canvas.create_text(310, 340, text="あいうえおかきくけこさしすせそ", anchor=tk.NW, width=120, font=("",20)) # 折り返し幅指定

#######################################
# 楕円
canvas.create_oval(10,450,290, 500)

#######################################
# ビットマップ
canvas.create_bitmap(310,450,bitmap='hourglass')

#######################################
# イメージ
if os.path.exists("Mandrill.png"):
    image = ImageTk.PhotoImage(file = "Mandrill.png")
    canvas.create_image(10,520,image=image, anchor=tk.NW)

#######################################
# ポリゴン
canvas.create_polygon(10,600,150,650,300,600,450,650,550,650)

#######################################
# 円弧
canvas.create_arc(10,670,60, 720)
canvas.create_arc(80,670,130, 720, extent=120)
canvas.create_arc(150,670,200, 720, extent=120, start=30)
canvas.create_arc(220,670,270, 720, extent=120, style=tk.CHORD)
canvas.create_arc(290,670,320, 720, extent=120, style=tk.ARC)

#######################################
# ウィンドウ(Canvas内にウィジェットの配置)
label = tk.Label(root, text = "ラベル", fg = "Red", font = ("", 14, "bold"))
canvas.create_window(300, 740, window=label, width = 300)

root.mainloop()

実行結果

注意点

tkinterのcreate_で始まるメソッドで線や画像などをCanvas上に描画すると、図形が重なっていて見えていなくても、各図形はオブジェクトとして認識しています。
(関数のスコープが外れても、図形のオブジェクトが自動で解放される事はありません。)

そのため、動画のように図形を何回も描画し続けると、オブジェクトの数(描画した図形の数)に比例して描画速度が低下します。

例えば、以下のようなプログラムを実行してみます。

import tkinter as tk
import time

root = tk.Tk()
root.geometry("500x200")

# Canvasの作成
canvas = tk.Canvas(root, bg = "white")
# Canvasを配置
canvas.pack(fill = tk.BOTH, expand = True)

colors = ("Red", "Green", "Blue")

for k in range(100):
    start = time.time()
    for i in range(100):
        canvas.create_line(0, i * 2, 500, i * 2, fill = colors[k%3])
    canvas.update()
    # 線100本分の描画時間    
    print((k+1)*100, time.time() - start)

root.mainloop()

実行結果

処理時間

このようにならないように、どこかのタイミングで描画した図形を削除する必要があるのですが、Canvasクラスにdelete()メソッドがあるので、これを用います。

delete()メソッドは、引き数にcreate_XXXXXメソッドで指定したtagの文字列もしくは戻り値のid番号を指定すると、その図形を削除することができます。

また、delete(“all”)と指定することで、すべての図形を削除することも可能です。

そこで、先ほどのプログラムを少し修正して、

import tkinter as tk
import time

root = tk.Tk()
root.geometry("500x200")

# Canvasの作成
canvas = tk.Canvas(root, bg = "white")
# Canvasを配置
canvas.pack(fill = tk.BOTH, expand = True)

colors = ("Red", "Green", "Blue")

for k in range(100):
    start = time.time()

    # 図形の削除
    canvas.delete("lines")
    # canvas.delete("all") # ←今回はこれでも同じ

    for i in range(100):
        canvas.create_line(0, i * 2, 500, i * 2, fill = colors[k%3], tag="lines")
    canvas.update()
    # 線100本分の描画時間  
    print((k+1)*100, time.time() - start)

root.mainloop()

処理時間

参考記事

https://tkdocs.com/shipman/canvas.html

【Python/tkinter】Canvas(キャンバス)の作成

【Python/tkinter】図形の編集(削除、移動、変形など)

【Python/tkinter】Canvasに画像を表示する

【Python/Pillow】線や円などの図形の描画

【Python/Pillow】線や円などの図形の描画

Pythonで線や円などの図形を書く方法としては、OpenCVやtkinterなどを用いても出来ますが、今回はPillowを用いて描画する方法です。tkinterの場合はこちらを参照ください。

Pillowで図形を描画するには、描画先のImageオブジェクトからImageDrawオブジェクトを作成し、このImageDrawオブジェクトに対して線などを描画を行います。

ImageDrawオブジェクトに線などを描画すると、書いたデータはImageオブジェクトに反映されます。

簡単なサンプル

from PIL import Image, ImageDraw

# カラーの画像データ(Imageオブジェクト)の作成
img = Image.new("RGB", (300, 100), "White")
# ImageDrawオブジェクトの作成
draw = ImageDraw.Draw(img)

# 直線の描画
draw.line([(10, 90), (290, 10)], fill = "Blue", width = 10)

# 画像の表示
img.show()

図形の種類

● 直線、折れ線

ImageDraw.line(xy, fill=None, width=0, joint=None)
xy 始点と終点、もしくは折れ線を構成する交点の座標をタプルのリスト[(x, y), (x, y), (x, y),・・・]
もしくは x,y座標のリスト[x, y, x, y, x, y,・・・]で指定します。
fill 線色を指定します。
width 線幅を指定します。
joint 折れ線の場合の、つなぎ目の状態を指定します。

何も指定しない(None)と線の継ぎ目に切れ目ができます。

“curve”を指定すると、丸く繋いでくれます。

● 矩形(四角形)

ImageDraw.rectangle(xy, fill=None, outline=None, width=1)
xy 左上と右下の座標をタプルのリスト[(x0, y0), (x1, y1)]
もしくは x,y座標のリスト[x0, y0, x1, y1]で指定します。
fill 領域を塗りつぶす色を指定します。
outline 輪郭線の色を指定します。
width 線幅を指定します。

● 角の丸い矩形

ImageDraw.rounded_rectangle(xy, radius=0, fill=None, outline=None, width=1)
xy 左上と右下の座標をタプルのリスト[(x0, y0), (x1, y1)]
もしくは x,y座標のリスト[x0, y0, x1, y1]で指定します。
radius 角の半径を指定します。
fill 領域を塗りつぶす色を指定します。
outline 輪郭線の色を指定します。
width 線幅を指定します。

● 楕円、円

ImageDraw.ellipse(xyfill=Noneoutline=Nonewidth=1)
xy 左上と右下の座標をタプルのリスト[(x0, y0), (x1, y1)]
もしくは x,y座標のリスト[x0, y0, x1, y1]で指定します。
fill 領域を塗りつぶす色を指定します。
outline 輪郭線の色を指定します。
width 線幅を指定します。

● ポリゴン(折れ線の始点と終点を結んだ図形)

ImageDraw.polygon(xy, fill=None, outline=None)
xy 折れ線を構成する交点の座標をタプルのリスト[(x, y), (x, y), (x, y),・・・]
もしくは x,y座標のリスト[x, y, x, y, x, y,・・・]で指定します。
fill 閉じた領域を塗りつぶす色を指定します。
outline 輪郭線の色を指定します。

● 正n角形

ImageDraw.regular_polygon(bounding_circle, n_sides, rotation=0, fill=None, outline=None)
bounding_circle 多角形に外接する円の中心(x, y)および半径r を指定します。
指定方法
(x, y, r) および ((x, y), r)
n_sides  何角形かを指定します。 3以上
rotation  回転角度を度数で指定します。
fill 閉じた領域を塗りつぶす色を指定します。
outline 輪郭線の色を指定します。

● 円弧

ImageDraw.arc(xy, start, end, fill=None, width=0)
xy 円弧を構成する楕円を囲む矩形の左上と右下の座標をタプルのリスト[(x0, y0), (x1, y1)]
もしくは x,y座標のリスト[x0, y0, x1, y1]で指定します。
start 円弧の始点の角度(3時方向が0度、時計周りが正)を指定します。
end 円弧の終点の角度(3時方向が0度、時計周りが正)を指定します。
fill 線色を指定します。
width 線幅を指定します。

● 円弧(始点と終点が直線で結ばれた状態)

ImageDraw.chord(xy, start, end, fill=None, outline=None, width=1)
xy 円弧を構成する楕円を囲む矩形の左上と右下の座標をタプルのリスト[(x0, y0), (x1, y1)]
もしくは x,y座標のリスト[x0, y0, x1, y1]で指定します。
start 円弧の始点の角度(3時方向が0度、時計周りが正)を指定します。
end 円弧の終点の角度(3時方向が0度、時計周りが正)を指定します。
fill 閉じた領域を塗りつぶす色を指定します。
outline 輪郭線の色を指定します。
width 線幅を指定します。

● 円弧(始点と終点がそれぞれ原点と直線で結ばれた状態)

ImageDraw.pieslice(xy, start, end, fill=None, outline=None, width=1)
xy 円弧を構成する楕円を囲む矩形の左上と右下の座標をタプルのリスト[(x0, y0), (x1, y1)]
もしくは x,y座標のリスト[x0, y0, x1, y1]で指定します。
start 円弧の始点の角度(3時方向が0度、時計周りが正)を指定します。
end 円弧の終点の角度(3時方向が0度、時計周りが正)を指定します。
fill 閉じた領域を塗りつぶす色を指定します。
outline 輪郭線の色を指定します。
width 線幅を指定します。

● 点

ImageDraw.point(xy, fill=None)
xy 点の座標をタプルのリスト[(x, y), (x, y), (x, y),・・・]
もしくは x,y座標のリスト[x, y, x, y, x, y,・・・]で指定します。
fill 点の色を指定します。

 

備考

色の指定は”Red”, ”Green”などの色の名前の文字列を使うか、R,G,Bの順で16進数で指定(#FF0000だと赤)するか、(R,G,B)のタプルで指定します。

(参考)

https://www.w3schools.com/colors/colors_names.asp

楕円や円弧の座標指定位置は、下図のようになります。

サンプルプログラム

from PIL import Image, ImageDraw
import random # 点のランダム表示用

# カラーの画像データ(Imageオブジェクト)の作成
img = Image.new("RGB", (600, 700), "White")
# ImageDrawオブジェクトの作成
draw = ImageDraw.Draw(img)

# 直線
draw.line([(10, 60), (290, 10)], fill = "Blue", width = 3)

# 折れ線
draw.line([(10, 100), (150, 180), (290, 100)], fill = "Green", width = 20)
draw.line([(10, 200), (150, 280), (290, 200)], fill = "Red", width = 20, joint="curve")

# 四角形
draw.rectangle([(10, 300), (290, 380)], fill = "#FF8C00", outline="#DC143C", width = 5)

# 角の丸い四角形
draw.rounded_rectangle([(10, 400), (290, 480)], radius = 15, fill = (0, 0, 255), outline="Red", width = 5)

# 楕円
draw.ellipse([(10, 500), (290, 580)], fill = "Magenta", outline="Cyan", width = 5)
# 円
draw.ellipse([(110, 600), (190, 680)], fill = "Green", outline="Yellow", width = 5)

# ポリゴン(始点と終点を結んだ多角形)
draw.polygon([(310, 0), (450, 30), (590, 0), (590, 80), (450, 50), (310, 80)], fill = "Red", outline="FireBrick")
draw.polygon([(310, 100), (410, 180), (490, 100), (590, 180)], fill = "blue", outline="red")

# 正n角形
draw.regular_polygon((450, 240, 40), 5, 0, fill = "blue", outline="red")

# 円弧
draw.arc([(310, 300), (590, 380)], 0, 240, fill = "Blue", width = 5)

# 円弧(始点と終点を結ぶ)
draw.chord([(310, 400), (590, 480)], 0, 240, fill = "Blue", outline="Red", width = 5)

# 円弧(始点・終点と原点を結ぶ)
draw.pieslice([(310, 500), (590, 580)], 0, 240, fill = "Blue", outline="Red", width = 5)

# 点
for i in range(1000):
    x = random.randrange(310, 590)
    y = random.randrange(600, 680)
    # 点の描画
    draw.point((x,y), fill = "Blue")

# 画像の保存
img.save("image.png")

# 画像の表示
img.show()

(実行結果)

参照ページ

https://pillow.readthedocs.io/en/stable/reference/ImageDraw.html

https://www.w3schools.com/colors/colors_names.asp

関連記事

【Python/tkinter】線や円などの図形の描画

【Python】ファイル、ディレクトリの存在確認

ファイル、ディレクトリの存在を確認するには、osモジュールの isfile, isdir, existsを用います。

isfile ファイルの存在の確認
isdir ディレクトリの存在の確認
exists ファイル もしくは ディレクトリの存在の確認
(ファイル、ディレクトリの区別なく存在確認します)

 

(サンプル)

カレントディレクトリ直下に \images\Porrots.bmp というファイルがある場合の例です。

import os

# ファイルが存在するか?
exist = os.path.isfile("images/Parrots.bmp")
print("ファイルの存在: ", exist)
exist = os.path.isfile("images")
print("ファイルの存在: ", exist)

# ディレクトリが存在するか?
exist = os.path.isdir("images/Parrots.bmp")
print("ディレクトリの存在: ", exist)
exist = os.path.isdir("images")
print("ディレクトリの存在: ", exist)

# ファイルもしくはディレクトリが存在するか?
exist = os.path.exists("images/Parrots.bmp")
print("ファイルもしくはディレクトリの存在: " , exist)
exist = os.path.exists("images")
print("ファイルもしくはディレクトリの存在: " , exist)

(実行結果)

 

注意点

ファイル名を相対パスで指定した場合、カレントディレクトリが移動しているとファイル存在の結果が意図した結果にならない場合があります。

カレントディレクトリを取得するには、osモジュールのgetcwd()メソッドを用います。

(参考)

【Python/os】カレントディレクトリの取得/設定

【Python】tkinterのGUIにmatplotlibのグラフを表示する

matplotlibを使ってグラフを表示すると、通常は、matplotlib独自のウィンドウで表示されますが、これをtkinterのGUIに組み込んで表示する方法を紹介します。

基本的な処理の流れとしては、matplotlibのFigureクラスでグラフの描画領域を確保し、グラフ描画用の座標軸を作成します。

FigureCanvasTkAggクラスで作成したFigureとFigureの配置先のウィジェットを指定し、matplotlib用のキャンバスを作成します。

グラフを描画する時は、作成した軸に対してグラフを描画し、最後にFigureCanvasTkAggクラスで作成したオブジェクトのdraw()メソッドを呼び出して、グラフを表示します。

 

以下にできるだけシンプルにしたサンプルを示します。

import tkinter as tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import numpy as np

class Application(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master)
        self.master = master
        self.master.title('matplotlib graph')

        #-----------------------------------------------

        # matplotlib配置用フレーム
        frame = tk.Frame(self.master)
        
        # matplotlibの描画領域の作成
        fig = Figure()
        # 座標軸の作成
        self.ax = fig.add_subplot(1, 1, 1)
        # matplotlibの描画領域とウィジェット(Frame)の関連付け
        self.fig_canvas = FigureCanvasTkAgg(fig, frame)
        # matplotlibのツールバーを作成
        self.toolbar = NavigationToolbar2Tk(self.fig_canvas, frame)
        # matplotlibのグラフをフレームに配置
        self.fig_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

        # フレームをウィンドウに配置
        frame.pack()

        # ボタンの作成
        button = tk.Button(self.master, text = "Draw Graph", command = self.button_click)
        # 配置
        button.pack(side = tk.BOTTOM)

        #-----------------------------------------------

    def button_click(self):
        # 表示するデータの作成
        x = np.arange(-np.pi, np.pi, 0.1)
        y = np.sin(x)
        # グラフの描画
        self.ax.plot(x, y)
        # 表示
        self.fig_canvas.draw()

root = tk.Tk()
app = Application(master=root)
app.mainloop()

(実行結果)

 

ポイント

Figureは、今回のやり方とは別に、通常のmatplotlibのウィンドウで表示する場合は、

import matplotlib.pyplot as plt

fig = plt.figure()

のようにする場合もありますが、このようにすると、tkinterで作成したウィンドウの×ボタンでプログラムを終了させても、プロセスが終了しない状態になってしまうので、注意が必要です。

参考

https://matplotlib.org/stable/gallery/user_interfaces/embedding_in_tk_sgskip.html

【Python/tkinter】CanvasにStretchDIBitsで画像を表示する

tkinterでOpenCVなどの画像データ(numpyのndarray)をCanvasに表示する場合、画像がカラーだと、BGRからRGBに変換し、numpy→Pillow→PhotoImageと変換して、ようやくCanvasに画像を表示します。

さらに内部的にはおそらくRGBからBGRに変換している?と思います。

そのためnumpyの画像データをCanvasに表示するのは、かなり遅い。

VB6.0をメインで使っていた時代は遅い処理はWin32APIを使って高速に処理をするのが定番でしたが、Pythonで出来ないのか?調べてみました。

tkinterのCanvasにWin32APIのStretchDIBitsで画像を表示するとき、よく分からなかったポイントは2つ。

●Canvasのウィンドウハンドルをどのように取得するのか?

●numpyの配列(ndarray)のポインタの取得方法は?

これさえクリアできれば、なんとかなります。

Canvasのウィンドウハンドルの取得方法

Canvasに限らす、ウィジェットのウィンドウハンドルを取得するにはwinfo_id()関数を使います。

(例)

# Canvasの作成
canvas = tk.Canvas()
# キャンバスのウィンドウハンドルを取得
hWnd = canvas.winfo_id()

numpy配列のポインタの取得方法

numpy配列(ndarray)のデータのポイントを取得するには、ctypes.data_as()関数でポインタを取得します。

(例)

# 画像データの読み込み
img = cv2.imread("image.bmp", cv2.IMREAD_UNCHANGED)
# 画像データ(numpayのndarrayの配列)からポインタの取得
ptr_img = img.ctypes.data_as(ctypes.POINTER(ctypes.c_byte))

StretchDIBitsでCanvasに画像を表示するサンプル

できるだけシンプルなサンプルを作成しました。

imreadの部分のコメントを切り替えると、カラー/グレースケールの表示が可能になります。

import ctypes
import tkinter as tk

import cv2

# ----------------------------------------------------------------------
# 	構造体などの定義
# ----------------------------------------------------------------------
SRCCOPY		    = 0x00CC0020
DIB_RGB_COLORS  = 0

# StretchBlt() Modes
BLACKONWHITE = 1
WHITEONBLACK = 2
COLORONCOLOR = 3
HALFTONE     = 4

class RECT(ctypes.Structure):
    _fields_ = [
        ('left', ctypes.wintypes.LONG),
        ('top', ctypes.wintypes.LONG),
        ('right', ctypes.wintypes.LONG),
        ('bottom', ctypes.wintypes.LONG)
        ]

class BITMAPINFOHEADER(ctypes.Structure):
    _fields_ = [
        ('biSize', ctypes.wintypes.DWORD),
        ('biWidth', ctypes.wintypes.LONG),
        ('biHeight', ctypes.wintypes.LONG),
        ('biPlanes', ctypes.wintypes.WORD),
        ('biBitCount', ctypes.wintypes.WORD),
        ('biCompression', ctypes.wintypes.DWORD),
        ('biSizeImage', ctypes.wintypes.DWORD),
        ('biXPelsPerMeter', ctypes.wintypes.LONG),
        ('biYPelsPerMeter', ctypes.wintypes.LONG),
        ('biClrUsed', ctypes.wintypes.DWORD),
        ('biClrImportant', ctypes.wintypes.DWORD)
        ]

class RGBQUAD(ctypes.Structure):
    _fields_ = [
        ('rgbBlue', ctypes.wintypes.BYTE),
        ('rgbGreen', ctypes.wintypes.BYTE),
        ('rgbRed', ctypes.wintypes.BYTE),
        ('rgbReserved', ctypes.wintypes.BYTE)
    ]

class BITMAPINFO(ctypes.Structure):
    _fields_ = [
        ('bmiHeader', BITMAPINFOHEADER),
        ('bmiColors', RGBQUAD * 256)
        ]

# ----------------------------------------------------------------------
root = tk.Tk()
root.title("StretchDIBitsで画像の表示")       # ウィンドウタイトル
root.geometry("500x300")     # ウィンドウサイズ(幅x高さ)

# ----------------------------------------------------------------------
# Canvasの作成
canvas = tk.Canvas()
# Canvasを配置
canvas.pack(expand = True, fill = tk.BOTH)

# 画像の読込
#img = cv2.imread("Parrots.bmp", cv2.IMREAD_GRAYSCALE)  # グレースケールとして読み込む
img = cv2.imread("Parrots.bmp", cv2.IMREAD_COLOR)       # カラーとして読み込む
if len(img.shape) >= 3:
    # カラーの場合
    src_height, src_width, src_ch = img.shape
else:
    # グレースケールの場合
    src_height, src_width = img.shape
    src_ch = 1

# 画像データ(numpayのndarrayの配列)のポインタ
ptr_img = img.ctypes.data_as(ctypes.POINTER(ctypes.c_byte))

# ----------------------------------------------------------------------
# ヘッダの作成
bi = BITMAPINFO()
bi.bmiHeader.biSize = ctypes.sizeof(BITMAPINFOHEADER)
bi.bmiHeader.biWidth = src_width
bi.bmiHeader.biHeight = -src_height
bi.bmiHeader.biPlanes = 1
bi.bmiHeader.biBitCount = 8 * src_ch

# カラーパレット
for i in range(256):
    bi.bmiColors[i].rgbBlue = i
    bi.bmiColors[i].rgbGreen = i
    bi.bmiColors[i].rgbRed = i
    bi.bmiColors[i].rgbReserved = 255

# ----------------------------------------------------------------------
# キャンバスのウィンドウハンドルを取得
hWnd = canvas.winfo_id()

# キャンバスの領域取得
canvas.update() # 領域を取得するために一旦更新しておく
win_rect = RECT()
ctypes.windll.user32.GetClientRect(hWnd, ctypes.byref(win_rect))

# デバイスコンテキストハンドルの取得
hDC = ctypes.windll.user32.GetDC(hWnd)

# ストレッチモードの設定(BLACKONWHITE, WHITEONBLACK, COLORONCOLOR, HALFTONE)
ctypes.windll.gdi32.SetStretchBltMode(hDC , COLORONCOLOR)

# 描画
ctypes.windll.gdi32.StretchDIBits(
    hDC, 
    win_rect.left, win_rect.top, win_rect.right, win_rect.bottom, # 描画先の領域
    0, 0, src_width, src_height,  # 描画元(画像)の領域
    ptr_img, ctypes.byref(bi), DIB_RGB_COLORS, SRCCOPY)

# 解放
ctypes.windll.user32.ReleaseDC(hWnd, hDC)

# ----------------------------------------------------------------------
root.mainloop()

(実行結果)
カラー画像の場合

グレースケールの場合

まとめ

カメラの画像データはOpenCVに限らず、numpyのndarrayで取得される事が多く、カラー画像の場合はデータの並びがBGRなので、StretchDIBitsを使うと、BGR→RGBの変換など無く、ダイレクトにCanvasに画像を表示することができます。

これで、高速に画像を表示できるようになるはず?!

ただ、最近はベタにWin32APIを使って画像を表示する事も少なくなってきたので、情報が少ないのが難点でしょうか?

【matplotlib】複数グラフの表示

matplotlibで普通に1つのグラフを表示する場合は以下のようにします。

import numpy as np
import matplotlib.pyplot as plt

# 表示するデータの作成
x = np.arange(-np.pi, np.pi, 0.1)
y = np.sin(x)

# グラフの描画
fig = plt.figure() # 省略可
plt.plot(x, y)
plt.show()

(実行結果)

plt.figure()の部分は省略可能ですが、グラフのサイズなどのオプション設定が可能です。

(参考)

https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.figure.html#matplotlib.pyplot.figure

 

このグラフに複数のグラフを同時に表示する場合は add_subplot()関数を使って以下のようにします。

import numpy as np
import matplotlib.pyplot as plt

# 表示するデータの作成
x = np.arange(-np.pi, np.pi, 0.1)

y0 = np.cos(x * 0)
y1 = np.cos(x * 1)
y2 = np.cos(x * 2)
y3 = np.cos(x * 3)
y4 = np.cos(x * 4)
y5 = np.cos(x * 5)

# グラフ領域の作成
fig = plt.figure(figsize = [5.8, 4])

# 座標軸の作成
ax1 = fig.add_subplot(2, 3, 1)
ax2 = fig.add_subplot(2, 3, 2)
ax3 = fig.add_subplot(2, 3, 3)
ax4 = fig.add_subplot(2, 3, 4)
ax5 = fig.add_subplot(2, 3, 5)
ax6 = fig.add_subplot(2, 3, 6)

# データのプロット
ax1.plot(x, y0)
ax2.plot(x, y1)
ax3.plot(x, y2)
ax4.plot(x, y3)
ax5.plot(x, y4)
ax6.plot(x, y5)

# グラフの表示
plt.show()

(実行結果)

 

add_subplotの書式は以下の通りです。

add_subplot(何行, 何列, 何番目)

add_subplotではグラフ領域を何行何列で分割するか?を指定し、グラフの表示位置を何番目の位置に表示するか?を指定します。

何番目か?は、下図のように左上からの順番を1始まりの番号で指定します。

(参考)

https://matplotlib.org/stable/api/figure_api.html?highlight=add_subplot#matplotlib.figure.Figure.add_subplot

複数グラフを表示する時の行数、列数は、すべて同じにする必要は無く、混在させる事も可能です。

import numpy as np
import matplotlib.pyplot as plt

# 表示するデータの作成
x = np.arange(-np.pi, np.pi, 0.1)

y1 = np.cos(x * 1)
y2 = np.cos(x * 2)
y3 = np.cos(x * 3)

# グラフ領域の作成
fig = plt.figure(figsize = [5.8, 4])

# 座標軸の作成
ax1 = fig.add_subplot(2, 3, 1) # 2行3列の1番目
ax2 = fig.add_subplot(2, 3, 2) # 2行3列の2番目
ax3 = fig.add_subplot(2, 3, 3) # 2行3列の3番目
ax4 = fig.add_subplot(2, 1, 2) # 2行1列の2番目

# データのプロット
ax1.plot(x, y1)
ax2.plot(x, y2)
ax3.plot(x, y3)
ax4.plot(x, y1+y2+y3)

# グラフの表示
plt.show()

(実行結果)

【Python/tkinter】ツールバーの作成

tkinterにはToolBarのような、ツールバー用のウィジェットは無いのですが、ツールバーをFrameButtonを使って作ります。

フレームにアイコン付きのボタンを左側に配置するだけですが。。

(作成したツールバーのイメージ)

  • ファイルを開く
  • フォルダを開く
  • 保存

の3つのボタンを配置しています。

 

(ソースコード)

import tkinter as tk
from tkinter import filedialog
from PIL import ImageTk

class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)

        self.master.title("ToolBar")    # ウィンドウタイトル
        self.master.geometry("300x200") # ウィンドウサイズ(幅x高さ)

        #---------------------------------------
        #  ツールバー
        #---------------------------------------     
        
        # ツールバー用Frame
        frame_toolbar = tk.Frame(self.master, bg = "gray90")
        
        #-------------------------
        # ボタン
        # ファイルを開く
        self.ico_open_file = ImageTk.PhotoImage(file = "OpenFile_16x.png")
        btn_open_file = tk.Button(frame_toolbar, command = self.open_file_click, image = self.ico_open_file)
        # フォルダを開く
        self.ico_open_folder = ImageTk.PhotoImage(file = "OpenFolder_16x.png")
        btn_open_folder = tk.Button(frame_toolbar, command = self.open_folder_click, image = self.ico_open_folder)
        # 保存
        self.ico_save = ImageTk.PhotoImage(file = "Save_16x.png")
        btn_save = tk.Button(frame_toolbar, command = self.save_click, image = self.ico_save)

        #-------------------------
        # ボタンをフレームに配置
        btn_open_file.pack(side = tk.LEFT, padx = (5, 0)) # 左側だけ隙間を空ける
        btn_open_folder.pack(side = tk.LEFT)
        btn_save.pack(side = tk.LEFT)

        #-------------------------
        # ツールバーをウィンドウに配置
        frame_toolbar.pack(fill=tk.X)
        #---------------------------------------     

    def open_file_click(self):
        '''[ファイルを開く]がクリックされたとき'''
        filename = filedialog.askopenfilename()
        print(filename)

    def open_folder_click(self):
        '''[フォルダを開く]がクリックされたとき'''
        dir_name = filedialog.askdirectory()
        print(dir_name)

    def save_click(self):
        '''[保存]がクリックされたとき'''
        filename = filedialog.asksaveasfilename()
        print(filename)

if __name__ == "__main__":
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

アイコンファイルはマイクロソフトのVisual Studio Image Libraryより入手しました。

https://www.microsoft.com/en-us/download/details.aspx?id=35825

【Python/Pillow(PIL)】画像データの新規作成

画像データ(PIL.Image)を画像ファイルなどからではなく、新規に作成するには、Imageモジュールのnew関数を使います。

new関数の書式は以下の通り

PIL.Image.new(mode, size, color=0)
mode 画像のモードを設定します。
主なものとして、
“L”      8bitグレースケール
“RGB”  3x8bit カラー画像
詳細はこちらを参照ください。
size 画像のサイズを(幅, 高さ)のタプルで指定します。
color 画像全体のデータの色の値を指定します。
初期値は黒となります。
カラーの場合は、(R, G, B)のように各チャンネルごとの値のタプルで指定します。

 

グレースケール画像を作成するには、以下のようにします。

from PIL import Image

# グレースケールの画像データを作成
img = Image.new("L", (320, 240))
# 画像の表示
img.show()

(実行結果)

初期値を指定すると

# 輝度値を指定して画像データを作成
img = Image.new("L", (320, 240), 128)
# 画像の表示
img.show()

(実行結果)

カラー画像の場合は以下のようにします。

# カラー画像データを作成
img = Image.new("RGB", (320, 240), (0, 128, 255))
# 画像の表示
img.show()

(実行結果)

【Python/Pillow(PIL)】画像ファイルを開く,保存する

jpegやbmpなどの画像ファイルをPillowで開くには、Imageモジュールのopen関数を使います。

同様に画像をファイルに保存するにはsave関数を用います。

以下に、bmp形式の画像ファイルを開き、画像をカラーからモノクロのグレースケールに変換し、pngファイルに保存する例を示します。

from PIL import Image

# PIL.Imageで画像を開く
img = Image.open("Parrots.bmp")

# OS標準の画像ビューアで表示
img.show()

# グレースケールへ変換
img_gray = img.convert("L")
img_gray.show()

# 画像のファイル保存
img_gray.save("image_gray.png")

(実行結果)

 

ファイル名に日本語も指定できる(OpenCVのimread関数は日本語が使えない)ので、使い勝手がいいと思います。

読込、保存のできる画像ファイルのフォーマット(bmp,jegなど)は別途こちら↓にまとめました。

【Python/Pillow(PIL)】対応画像ファイルフォーマット

 

画像ファイルの保存では、jpegファイルでは品質(quality)など、ファイルフォーマットごとに指定できるオプションがあるので、詳細はこちらのページ↓を参照ください。

【Python/Pillow(PIL)】JPEG画像の品質を指定して保存する