PythonのPillowでモノクロ画像ファイルを開くと、Imageクラスの mode は “L” となりますが、これはカラーパレットを持たない画像データとなります。
C言語やC#ではモノクロ画像データを表示するときは、カラーパレットを参照して表示してモニタ上に画像を表示するインデックスカラーという仕組みがありました。
このインデックスカラーですが、モノクロ画像データの場合は 0~255 の輝度値を持ちますが、実際にモニタ上に表示する場合は、 0~255 のインデックスを持つカラーパレットのR,G,Bの値を参照して、モニタに表示されています。
index | R | G | B |
0 | 0 | 0 | 0 |
1 | 1 | 1 | 1 |
2 | 2 | 2 | 2 |
3 | 3 | 3 | 3 |
: | : | : | : |
254 | 254 | 254 | 254 |
255 | 255 | 255 | 255 |
モノクロ画像の場合、通常、indexの値とR,G,Bの値を同じ値にして表示するのですが、このR,G,Bの値を変更する事で、モノクロ画像に擬似的に色を付けて表示する事が可能になります。
Pillowでモノクロ画像にカラーパレットを使うには putpalette関数を用います。
構文
Image.putpalette(data, rawmode='RGB')
(参考)
https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.putpalette
dataの部分にカラーパレットを指定しますが、これはリストで、
[R0, G0, B0, R1, G1, B1, R2, G2, B2, R3, G3, B3, … R255, G255, B255]
の順で、256×3個(768個)の一次元の配列で指定します。
サンプルプログラム
from PIL import Image
# PIL.Imageで画像を開く
img = Image.open("./Mandrill.bmp")
palette = []
for i in range(0, 64):
palette.append(i) # R
palette.append(i) # G
palette.append(i) # B
for i in range(64, 128):
palette.append(0) # R
palette.append(0) # G
palette.append(255) # B
for i in range(128, 192):
palette.append(0) # R
palette.append(255) # G
palette.append(0) # B
for i in range(192, 256):
palette.append(255) # R
palette.append(0) # G
palette.append(0) # B
img.putpalette(palette)
# 画像の表示
img.show()
(実行結果)
実際にはカラーパレットは、モノクロカメラに擬似的に色を付けて輝度分布を見やすくする疑似カラー表示を行う場合だったり、二値化を行う際のプレビュー用として私は使っています。
二値化のプレビュー用のプログラムを以下に示します。
from PIL import Image, ImageTk, ImageOps
import tkinter as tk
class Application(tk.Frame):
def __init__(self, master = None):
super().__init__(master)
self.master.title("カラーパレット") # ウィンドウタイトル
self.src_img = Image.open("./Mandrill.bmp")
#---------------------------------------------------------------
# Canvasの作成
self.canvas = tk.Canvas(self.master, bg = "#008B8B")
# Canvasを配置
self.canvas.pack(expand = True, fill = tk.BOTH)
#---------------------------------------------------------------
# Scaleの作成
self.scale_var = tk.IntVar()
scaleH = tk.Scale( self.master,
variable = self.scale_var,
command = self.slider_scroll,
orient=tk.HORIZONTAL, # 配置の向き、水平(HORIZONTAL)、垂直(VERTICAL)
from_ = 0, # 最小値(開始の値)
to = 256, # 最大値(終了の値)
tickinterval=64 # 目盛りの分解能(初期値0で表示なし)
)
scaleH.pack(fill = tk.X)
#---------------------------------------------------------------
def slider_scroll(self, event=None):
'''スライダーを移動したとき'''
self.disp_image(self.src_img, self.scale_var.get())
def disp_image(self, image, threshold):
'''画像をCanvasに表示する'''
canvas_width = self.canvas.winfo_width()
canvas_height = self.canvas.winfo_height()
#---------------------------------------------------------------
# カラーパレットの設定
palette = []
for i in range(0, threshold):
palette.append(i) # R
palette.append(i) # G
palette.append(i) # B
for i in range(threshold, 256):
palette.append(255) # R
palette.append(0) # G
palette.append(0) # B
# カラーパレットの設定
image.putpalette(palette)
#---------------------------------------------------------------
#PIL.ImageからPhotoImageへ変換する
self.photo_image = ImageTk.PhotoImage(image=image)
# 画像の描画
self.canvas.create_image(
canvas_width / 2, # 画像表示位置(Canvasの中心)
canvas_height / 2,
image=self.photo_image # 表示画像データ
)
if __name__ == "__main__":
root = tk.Tk()
app = Application(master = root)
app.mainloop()
(実行結果)
まとめ
PythonのPillowを使っていると、モノクロ画像のカラーパレットを意識する事は少ない気がしますが、カラーパレットを使わずにモノクロ画像に擬似的に色を付けようとすると、モノクロ8bitのデータをカラーの24bitに変換して、輝度値に合わせてR,G,Bの値を画像の全画素に対して変換を行う必要がありますが、カラーパレットを使うと、たかだか768個のデータを指定するだけなので、簡単に色を付ける事が可能になります。
ただし、このカラーパレットを指定できるのは、8bitのモノクロ画像(インデックスカラーも含む)のみとなります。
参考記事
https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.putpalette