『第十七秘鍵描彩術』は、16色パレット画像の色順をランダムに変換・復号するGUIツールです。
>>GUI外観 >>使い方以下は『第十七秘鍵描彩術』のソースコード全文です:
import tkinter as tk
from tkinter import filedialog, messagebox
from PIL import Image, ImageTk
import os
import random
from datetime import datetime
# 定数:16色パレットとa〜pの対応
PALETTE = [
(0, 0, 0), (128, 0, 0), (0, 128, 0), (128, 128, 0),
(0, 0, 128), (128, 0, 128), (0, 128, 128), (192, 192, 192),
(128, 128, 128), (255, 0, 0), (0, 255, 0), (255, 255, 0),
(0, 0, 255), (255, 0, 255), (0, 255, 255), (255, 255, 255)
]
LETTERS = list("abcdefghijklmnop")
class ColorShuffleApp:
def __init__(self, root):
self.root = root
self.root.title("パレット色並び替えツール - 第十七秘鍵描彩術")
# __init__の冒頭
self.root.geometry("600x800") # ウインドウサイズ拡大
self.image_path = None
self.original_image = None
self.processed_image = None
# モード選択(エンコード or デコード)
self.mode = tk.StringVar(value="encode")
tk.Radiobutton(root, text="エンコード", variable=self.mode, value="encode", command=self.update_mode).pack(anchor="w")
tk.Radiobutton(root, text="デコード", variable=self.mode, value="decode", command=self.update_mode).pack(anchor="w")
# UIパーツ
tk.Button(root, text="ファイル選択", command=self.load_image).pack(pady=10)
self.file_label = tk.Label(root, text="ファイル未選択")
self.file_label.pack()
self.original_label = tk.Label(root, text="元画像")
self.original_label.pack(pady=10)
self.palette_button = tk.Button(root, text="パレット生成(エンコード用)", command=self.generate_palette)
self.palette_button.pack()
self.palette_entry = tk.Entry(root, width=30)
self.palette_entry.pack(pady=5)
tk.Button(root, text="色変換", command=self.run_process).pack(pady=10)
self.converted_label = tk.Label(root, text="処理後画像")
self.converted_label.pack(pady=10)
tk.Button(root, text="保存", command=self.save_image).pack(pady=10)
def update_mode(self):
if self.mode.get() == "encode":
self.palette_button.config(state="normal")
else:
self.palette_button.config(state="disabled")
def load_image(self):
path = filedialog.askopenfilename(filetypes=[
("画像ファイル", "*.bmp *.png *.gif"),
("すべてのファイル", "*.*")
])
if path:
self.image_path = path
self.original_image = Image.open(path).convert("RGB")
self.show_image(self.original_image, self.original_label)
self.file_label.config(text=os.path.basename(path))
def generate_palette(self):
if self.mode.get() != "encode":
messagebox.showinfo("情報", "パレット生成はエンコードモードでのみ使用可能です。")
return
shuffled = random.sample(LETTERS, len(LETTERS))
self.palette_entry.delete(0, tk.END)
self.palette_entry.insert(0, "".join(shuffled))
def validate_palette(self, s):
s = s.lower()
return len(s) == 16 and set(s) == set(LETTERS)
def run_process(self):
key = self.palette_entry.get().strip().lower()
if not self.validate_palette(key):
messagebox.showerror("エラー", "a〜p を1回ずつ含む16文字を入力してください。")
return
if not self.original_image:
messagebox.showerror("エラー", "画像を読み込んでください。")
return
if self.mode.get() == "encode":
self.processed_image = self.encode_image(key)
else:
self.processed_image = self.decode_image(key)
self.show_image(self.processed_image, self.converted_label)
def encode_image(self, key):
mapping = {i: LETTERS.index(key[i]) for i in range(16)}
palette_map = {PALETTE[i]: PALETTE[mapping[i]] for i in range(16)}
img = self.original_image.copy()
pixels = img.load()
for y in range(img.height):
for x in range(img.width):
if pixels[x, y] in palette_map:
pixels[x, y] = palette_map[pixels[x, y]]
return img
def decode_image(self, key):
palette_map = {PALETTE[LETTERS.index(key[i])]: PALETTE[i] for i in range(16)}
img = self.original_image.copy()
pixels = img.load()
for y in range(img.height):
for x in range(img.width):
if pixels[x, y] in palette_map:
pixels[x, y] = palette_map[pixels[x, y]]
return img
def save_image(self):
if not self.processed_image:
messagebox.showerror("エラー", "保存する画像がありません。")
return
ext = os.path.splitext(self.image_path)[1].lower()
base = os.path.splitext(os.path.basename(self.image_path))[0]
now = datetime.now().strftime("%Y%m%d_%H%M%S")
default_name = f"{base}-{now}{ext}"
# ファイル名と保存先を指定可能に
file_path = filedialog.asksaveasfilename(
defaultextension=ext,
filetypes=[("画像ファイル", "*.bmp *.png *.gif")],
initialfile=default_name,
title="保存先とファイル名を指定"
)
if not file_path:
return
img_to_save = self.processed_image
if ext == ".gif":
img_to_save = img_to_save.convert("P", palette=Image.ADAPTIVE)
img_to_save.save(file_path)
messagebox.showinfo("保存完了", f"{os.path.basename(file_path)} を保存しました。")
def show_image(self, img, label_widget):
img_thumb = img.copy()
max_size =300
width, height = img_thumb.size
if width > height:
new_width = min(width, max_size)
new_height = int(height * (new_width / width))
else:
new_height = min(height, max_size)
new_width = int(width * (new_height / height))
img_thumb = img_thumb.resize((new_width, new_height), Image.LANCZOS)
img_tk = ImageTk.PhotoImage(img_thumb)
label_widget.configure(image=img_tk)
label_widget.image = img_tk
# 実行
if __name__ == "__main__":
root = tk.Tk()
##タイトルバーアイコン表示用
data = '''R0lGODdhEAAQAIcAAP//5/X5wvTqwezpwObkvf/GyMXRybDCtKHFtf+4uOGtstqzvMC3sru1sZ21r52xv6CfqirixzfRwQDl1QDSyxnFxijEzCPDyyW6w2mltWyjs12fsVCsska5uDekuBu0jNGXqriQpqOOpJaeqI2dtaOIrZKLopWCqJl1lGSYrmWEkYOBn2t8qlh7qld7n1x3n1V6nE+akEqXjz6Gr0+DqUKBrDqBsDx/rz99rkB8rUR5q0J4q0l2qUh1qEd0qUV0qI5olX1rk3dlnHNekYZdin9bjHVbkm5llmtlnGdelW9cimdbhW5WhmhZkWdXk2tUkWdUkH5Tin5ThnxQh4NOiHZNinFMiG5LiWxQjmxNi21MimxMimxLh2tThWhRjmdTjWdNil5uoldsn1NqoVdooF1lkVhlnU5wpU5tpFJqoUVwpmNjnF9jm2BfmGJfklpknF1hm1djnGJblmRbj2Fbll9dmVtZi1hZkmRYlGNYjV5SkGZOimVNiGJNjXNIiHRHhXJIhnRGgHNGgnJEhHdDhnVCgnRDgHNCgnNCf3ZBfnVBf3RBf3c/gXQ/f3Q/fnNBgnJBgXJAgHI/f29KiXBIg25IhnBGhm9GhW1Jg2ZJiGBJgmxFhG5DgWxDgGpCfnE/f3A/fmw/e3c9fnU9fnU9fHM9fnQ7e3U6enQ6e3I+f3I+fnI8e288fW88e287e246fHY5enU5enQ5enQ4eXQ4eHQ2eHE5eXI4eHE4eHA5eW44dnM2d3Q1dnU0dXQ0dnM1dnM0dnIzdXAzdG80dG80c3QydXQxdHMxdHMxc3Ixc3UvcHQvcnMwcnIwcXIvcXUucHQucHIucHIub3Qtb3ItcHAwcXAub28ucHMsbnMrbXIrbnIrbXEsbnIqbXIqbHEqbHEpbHEpa3Ioa3Eoa3EoanInanEnaXEmaXElaHAlZ3AkZnAjZW8iZG8hY24fYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAEAAQAEAI/wCf8UIV6dIWKHTejDnDQ0eOGzbEaWsWrBYoLnzclHlhQcOABn2wLbMV6xCgLE/kIDmxgEQFD2bUmQNnbVguT5qi7BEB4MAHGYSg7ZLVaNAVLG3YxAnDosWMGj22OTPWStUoK1+G3DFBwIEEDnjOgbtGTJcrSkTsoHBBYUMCEJnayfwmDZkvWrBElXqVilEhQcp6oYJkSQsUOXDIoFHzw8cOHNOKyXL0aJIXJ2LWlCjwYAKGNNmYAZslqVKVJkaEQAiAIEKHOt6wCfu16tOmOUGO0LiQQQADPeW6UUuGixUmKUmUwKiQQkEIMOjGcat2LFSgLlTyrDAwIoYKP+vShSHbFi3YLVOdOE0BUmQJkz/u2KUjRx7ZL1qnSCVShMjQooAAOw=='''
root.tk.call('wm', 'iconphoto', root._w, tk.PhotoImage(data=data))
####
app = ColorShuffleApp(root)
root.mainloop()