パレット色並び替えツール『第十七秘鍵描彩術』 ソースコード

『第十七秘鍵描彩術』は、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()
        

       
    

📦 ダウンロード 🏠 ホームへ