フォトモザイクメーカー『欠片幻彩』 ソースコード

『欠片幻彩』は、指定したフォルダ内の画像を縮小・ランダムに配置して、モザイクアート風の1枚画像を作成するツールです。

>>GUI外観 >>使い方

以下は『欠片幻彩』のソースコード全文です:

 
        import os
        import random
        from datetime import datetime
        import tkinter as tk
        from tkinter import filedialog, colorchooser, messagebox
        from PIL import Image, ImageTk, ImageOps
        
        class ToolTip:
            def __init__(self, widget, text=""):
                self.widget = widget
                self.text = text
                self.tip_window = None
                widget.bind("<Enter>", self.show_tip)
                widget.bind("<Leave>", self.hide_tip)
        
            def show_tip(self, event=None):
                if self.tip_window or not self.text:
                    return
                x, y, cx, cy = self.widget.bbox("insert") if self.widget.bbox("insert") else (0,0,0,0)
                x = self.widget.winfo_rootx() + 20
                y = self.widget.winfo_rooty() + 20
                self.tip_window = tw = tk.Toplevel(self.widget)
                tw.wm_overrideredirect(True)
                tw.wm_geometry(f"+{x}+{y}")
                label = tk.Label(tw, text=self.text, justify="left",
                                 background="#ffffe0", relief="solid", borderwidth=1,
                                 font=("Noto Sans JP", 9))
                label.pack(ipadx=4, ipady=2)
        
            def hide_tip(self, event=None):
                if self.tip_window:
                    self.tip_window.destroy()
                    self.tip_window = None
        
        class MosaicApp:
            def __init__(self, root):
                self.root = root
                self.root.title("フォトモザイクメーカー - 欠片幻彩")
        
                # 各種設定
                self.width_var = tk.IntVar(value=1920)
                self.height_var = tk.IntVar(value=1080)
                self.scale_var = tk.DoubleVar(value=0.1)
                self.bg_color = "#ffffff"
                self.folder_path = ""
                self.generated_img = None
        
                # メインフレーム
                main_frame = tk.Frame(root, padx=10, pady=10)
                main_frame.pack(fill="both", expand=True)
        
                # 左側:設定パネル
                left_frame = tk.LabelFrame(main_frame, text="設定", padx=10, pady=10)
                left_frame.pack(side="left", fill="y")
        
                # 画像サイズ
                tk.Label(left_frame, text="画像サイズ:").grid(row=0, column=0, sticky="w")
                tk.Entry(left_frame, textvariable=self.width_var, width=6).grid(row=0, column=1)
                tk.Label(left_frame, text="x").grid(row=0, column=2)
                tk.Entry(left_frame, textvariable=self.height_var, width=6).grid(row=0, column=3)
        
                # 背景色
                tk.Label(left_frame, text="背景色:").grid(row=1, column=0, sticky="w")
                tk.Button(left_frame, text="選択", command=self.choose_color).grid(row=1, column=1, sticky="w")
                self.color_preview = tk.Label(left_frame, bg=self.bg_color, width=4, relief="sunken")
                self.color_preview.grid(row=1, column=2, columnspan=2, sticky="w")
        
                # フォルダ選択
                tk.Button(left_frame, text="フォルダ選択", command=self.choose_folder).grid(row=2, column=0, columnspan=4, pady=(5, 0), sticky="w")
                self.folder_label = tk.Label(left_frame, text="未選択", wraplength=220, fg="gray", anchor="w", justify="left")
                self.folder_label.grid(row=3, column=0, columnspan=4, sticky="w", pady=(2, 5))
                self.folder_tooltip = ToolTip(self.folder_label, "")
        
                # 縮小率
                tk.Label(left_frame, text="縮小率:").grid(row=4, column=0, sticky="w")
                tk.Entry(left_frame, textvariable=self.scale_var, width=6).grid(row=4, column=1, sticky="w")
        
                # ボタン
                tk.Button(left_frame, text="生成", command=self.generate, width=10).grid(row=5, column=0, columnspan=2, pady=5)
                tk.Button(left_frame, text="保存", command=self.save, width=10).grid(row=5, column=2, columnspan=2, pady=5)
        
                # 右側:プレビュー
                right_frame = tk.LabelFrame(main_frame, text="プレビュー", padx=10, pady=10)
                right_frame.pack(side="right", fill="both", expand=True)
        
                self.preview_label = tk.Label(right_frame)
                self.preview_label.pack(expand=True)
        
            def choose_color(self):
                color = colorchooser.askcolor(title="背景色を選択")
                if color[1]:
                    self.bg_color = color[1]
                    self.color_preview.config(bg=self.bg_color)
        
            def choose_folder(self):
                self.folder_path = filedialog.askdirectory()
                if self.folder_path:
                    self.folder_label.config(text=self.folder_path, fg="black")
                    self.folder_tooltip.text = self.folder_path
                else:
                    self.folder_label.config(text="未選択", fg="gray")
                    self.folder_tooltip.text = ""
        
            def generate(self):
                if not self.folder_path:
                    messagebox.showwarning("警告", "画像フォルダを選択してください。")
                    return
        
                W, H = self.width_var.get(), self.height_var.get()
                scale = self.scale_var.get()
        
                # 背景生成
                mosaic = Image.new("RGB", (W, H), self.bg_color)
        
                # 画像リスト取得
                files = [f for f in os.listdir(self.folder_path) if f.lower().endswith((".png",".jpg",".jpeg",".bmp"))]
                if not files:
                    messagebox.showwarning("警告", "フォルダに画像がありません。")
                    return
        
                # シャッフルして一度ずつ配置
                random.shuffle(files)
                for file in files:
                    img = Image.open(os.path.join(self.folder_path, file))
                    img = ImageOps.exif_transpose(img).convert("RGB")  # ★ 回転補正
                    img = img.resize((int(img.width * scale), int(img.height * scale)))
        
                    if img.width == 0 or img.height == 0:
                        continue  # 無効サイズはスキップ
        
                    # ランダム座標
                    x = random.randint(0, max(0, W - img.width))
                    y = random.randint(0, max(0, H - img.height))
        
                    mosaic.paste(img, (x, y))
        
                self.generated_img = mosaic
        
                # プレビュー表示
                preview = mosaic.copy()
                preview.thumbnail((500, 500))
                self.tk_img = ImageTk.PhotoImage(preview)
                self.preview_label.config(image=self.tk_img)
        
            def save(self):
                if self.generated_img is None:
                    messagebox.showwarning("警告", "まず画像を生成してください。")
                    return
                filename = datetime.now().strftime("%Y%m%d_%H%M%S.png")
                path = os.path.join(os.path.expanduser("~/Desktop"), filename)
                self.generated_img.save(path)
                messagebox.showinfo("保存完了", f"デスクトップに保存しました:\n{filename}")
        
        if __name__ == "__main__":
            root = tk.Tk()
            
            ##タイトルバーアイコン表示用
            data = '''R0lGODlhEAAQAIYAAAAAAEJYafpzWVJVVvNqTkW36j5UY/8AAP9/AKpVVVWq/+5pU///APtxWD2q2fFxUgB/f0Gq20hhc/ZuVENVZUBWZv9/f/9VVeRrUOdFRfxpTMFmWNpkTft1Wn//////f3ubsX9///9yTknB9vtyV0W26UKx40ey4P98YUCr2/WTSEeo01Wqqv+qVVVVqv+yM0JbbPi5O0FWZv/EOkFSYv/KPi2y6j+////KPjpYai5SZj1VZDxUZP//Mz9VVT9OXSRISD8/fz8/SwD//3VbXwZdhhxKYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAQAi0AAEIHEgQgAeCAwAYCMAwAAUOGlAIEEDggAUAQQBQkCDDB4AIBUZEAHCjYEIjGyY+wFHjBYIDBQEkBMBCoAMFBYE0DPDjRAEbAhMgBFBBAo8GAh4saEAAwMWCEAoyiClwpsAQVAXSMCDQBQAHJVIMwVmVKEMYQkwUKLACAdmEC3cWAUGABAYACMruYKhDxIQFEwUsAHBBYNQBOYhMmKgiBoAWMYUS6JChx4ysAxNcOMDgQ8GAADs=''' 
            root.tk.call('wm', 'iconphoto', root._w, tk.PhotoImage(data=data))
            ####
           
            app = MosaicApp(root)
            root.mainloop()     
    

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