RGBをリアルタイムで入れ替えて表示するツールをPythonで作ったよ

こんにちは。

私、色盲というのか色弱というのか色が苦手で、困ることはそんなに多くないのですが時々「こんなツールがあると助かるなあ」というときがあります。それも基本的にパソコン上での作業は、学生時代に見つけた「色々の色」を使うと概ね事足ります。

forest.watch.impress.co.jp

あともうちょいほしい機能としてはタイトルの通り、RGBを入れ替えて表示するというものがあります。ディスプレイの色は光の三原色であるRGB(=赤、緑、青)の強さを0~255の256段階で決定し、組み合わせることで表現されます。私の見え方ではRGBでいうとRに対する感度が弱く、Rの有無が識別のポイントになっている色同士の区別が難しいようです。例として…

f:id:mamiske:20210407005122p:plain

3つの数字は文字の色をRGBで示しています。①と②はR=255かR=0かの違いですので、瞬時で識別が難しいです。たとえば今の職場では図面のルールとして形状は①の色、寸法線は②の色となっています。多くの人はRに対する感度が高く、①と②は大きく違う色と認識されることが多いようなので認識に大きなギャップがあります。この線はどっちだ?というのを瞬時に見分けられると便利です。

Windows標準で拡大鏡というツールが入っていて色の反転ができますが、それは①と②を③と④に変えるものです。結局R=0かR=255かが識別のポイントなので、瞬時で判別が難しいです。多くの人にとっては全く違う色でしょうが、私にとって赤は目立たなく、黒と見間違うぐらい地味な暗い色なのです。一方でRとBを入れ替えて⑤と⑥にするとB=255かB=0かが識別のポイントになっているので簡単に識別できます。

人類の98%ぐらいの人には不要だと思いますが、この色を入れ替えるツールをPythonで作ってみました。ラジオボタンで選んだ変換をします。

import tkinter as tk
from tkinter import *
from PIL import ImageGrab, Image, ImageTk
import pyautogui as pgui
import numpy as np

root = tk.Tk()
root.geometry("300x200")
root.title("RGB Switcher")

cont_bln = tk.BooleanVar()
cont_bln.set(False)

img_size = 200
img_half = img_size / 2


# ラジオボタンのチェック確認用
var = tk.IntVar()
var.set(0)

def pil_to_tk(pil_image):
    global var
    im = np.array(pil_image)
    im_swap = im.copy()
    if var.get() == 0:
        im_swap[:, :, 0], im_swap[:, :, 2] = im[:, :, 2], im[:, :, 0]
    elif var.get() == 1:
        im_swap[:, :, 0], im_swap[:, :, 1] = im[:, :, 1], im[:, :, 0]
    elif var.get() == 2:
        im_swap[:, :, 1], im_swap[:, :, 2] = im[:, :, 2], im[:, :, 1]
    elif var.get() == 3:
        im_swap[:, :, 0], im_swap[:, :, 1] , im_swap[:, :, 2] \
                   = im[:, :, 2], im[:, :, 0] , im[:, :, 1]
    elif var.get() == 4:
        im_swap[:, :, 0], im_swap[:, :, 1] , im_swap[:, :, 2] \
                   = im[:, :, 1], im[:, :, 2] , im[:, :, 0]
        
    pil_img_swap = Image.fromarray(im_swap)

    # Tkinter画像オブジェクトをPIL画像オブジェクトから生成
    tk_image = ImageTk.PhotoImage(pil_img_swap)
    return tk_image


def cont_func():
    global cont_bln
    global tk_image
    global img_half
    if cont_bln.get() == True:
        x, y = pgui.position()
        pil_image = ImageGrab.grab ( bbox = ( x - img_half,
                                              y - img_half,
                                              x + img_half,
                                              y + img_half )
                                     )

        # PIL画像オブジェクトをTkinter画像オブジェクトに変換
        tk_image = pil_to_tk(pil_image)
        canvas.create_image(
        tk_image.width() // 2,
        tk_image.height() // 2,
        image=tk_image)
        root.after_idle( root.after, 0, cont_func )
        
def cont_pressed():
    global cont_bln
    if cont_bln.get() == False:
        cont_bln.set(True)
        root.after_idle( root.after, 0, cont_func )
    else:
        cont_bln.set(False)

# マウスポインタの位置を取得
x, y = pgui.position()
# マウスポインタ周辺の画像を取得
pil_image = ImageGrab.grab ( bbox = ( x - img_half,
                                      y - img_half,
                                      x + img_half,
                                      y + img_half )
                             )
# PIL画像オブジェクトをTkinter画像オブジェクトに変換
tk_image = pil_to_tk(pil_image)

frame = Frame(root, bd=1)
cont_btn = tk.Button(frame, text = "実行/停止", command=cont_pressed)
cont_chk = tk.Checkbutton(frame, variable = cont_bln, text = "実行中")
canvas = tk.Canvas(frame, width=img_size, height=img_size) 
rad_btn0 = tk.Radiobutton(frame, value=0, variable=var, text='RGB->BGR')
rad_btn1 = tk.Radiobutton(frame, value=1, variable=var, text='RGB->GRB')
rad_btn2 = tk.Radiobutton(frame, value=2, variable=var, text='RGB->RBG')
rad_btn3 = tk.Radiobutton(frame, value=3, variable=var, text='RGB->GBR')
rad_btn4 = tk.Radiobutton(frame, value=4, variable=var, text='RGB->BRG')

frame.grid(row=0,column=0)
canvas.pack(side=LEFT)
rad_btn0.pack(side=TOP)
rad_btn1.pack(side=TOP)
rad_btn2.pack(side=TOP)
rad_btn3.pack(side=TOP)
rad_btn4.pack(side=TOP)
cont_btn.pack(side=TOP)
cont_chk.pack(side=TOP)

root.attributes("-topmost", True)
root.mainloop()

参考にしたもの
NumPyでRGB画像の色チャンネルを分離して単色化、白黒化、色交換 | note.nkmk.me
[Python][Windows] Pythonでスクリーンキャプチャを行う - Qiita
【Python】ラジオボタン(Radiobutton)を作成する | 鎖プログラム
Python 3.x - Python3 Tkinter after()について|teratail
Tkinter 入門: 2. Widget を配置しよう
Python&tkinterで周期処理をしたいんだけどうまく行かなかった話と解決方法 - ウォーミングアップ

ちなみに何言っているかわからないかもしれませんが、いつも紛らわしいパワーポイントとエクセルのアイコンを見て
f:id:mamiske:20210407010206p:plain
「お前らホンマに違う色やったんやなあ」と思いました。