import cv2 import numpy as np import tkinter as tk from tkinter import filedialog from tkinterdnd2 import DND_FILES, TkinterDnD from PIL import Image, ImageTk import os class ImageViewer(TkinterDnD.Tk): def __init__(self): super().__init__() self.title("Image Artifact Visualizer") self.geometry("800x600") # キャンバスを作成 self.canvas = tk.Canvas(self, bg="black") self.canvas.pack(fill=tk.BOTH, expand=True) # 画像データ self.image = None self.tk_image = None self.zoom_factor = 1.0 self.offset_x = 0 self.offset_y = 0 # マウスドラッグ用 self.start_x = 0 self.start_y = 0 self.is_dragging = False # イベント設定 self.canvas.bind("", self.zoom_image) self.canvas.bind("", self.start_drag) self.canvas.bind("", self.do_drag) self.canvas.bind("", self.end_drag) # ドラッグ&ドロップの設定 self.drop_target_register(DND_FILES) self.dnd_bind("<>", self.on_drop) # ファイル選択ボタン btn = tk.Button(self, text="Select Image", command=self.select_file, font=("Arial", 12)) btn.pack(side=tk.BOTTOM, pady=10) def select_file(self): file_path = filedialog.askopenfilename(filetypes=[("Image Files", "*.jpg;*.jpeg;*.png;*.webp")]) if file_path: self.load_image(file_path) def on_drop(self, event): file_path = event.data.strip("{}") # パスの前後の{}を削除 if os.path.isfile(file_path): self.load_image(file_path) def load_image(self, file_path): try: file_path = os.path.normpath(file_path) # 日本語や特殊文字対応 # Pillowで画像を読み込む(OpenCVのimreadの代わり) img_pil = Image.open(file_path).convert("L") # モノクロ化 img_cv = np.array(img_pil) # OpenCV用に変換 # **アーティファクト強調処理** img_cv = self.enhance_artifacts(img_cv) self.image = Image.fromarray(img_cv) # OpenCV → PIL self.zoom_factor = 1.0 self.offset_x = 0 self.offset_y = 0 self.update_image() except Exception as e: print(f"Error loading image: {e}") def enhance_artifacts(self, img_cv): """JPEGノイズやブロックアーティファクトを強調""" img_blur = cv2.GaussianBlur(img_cv, (3, 3), 0) # ぼかし img_sharp = cv2.addWeighted(img_cv, 2.5, img_blur, -1.5, 0) # シャープ化 # ヒストグラム平坦化でコントラスト強調 img_eq = cv2.equalizeHist(img_sharp) return img_eq def update_image(self): if self.image: # 画像のサイズ調整 img_resized = self.image.resize( (int(self.image.width * self.zoom_factor), int(self.image.height * self.zoom_factor)), Image.Resampling.LANCZOS ) self.tk_image = ImageTk.PhotoImage(img_resized) # キャンバス更新 self.canvas.delete("all") self.canvas.create_image( self.offset_x + self.canvas.winfo_width() // 2, self.offset_y + self.canvas.winfo_height() // 2, image=self.tk_image, anchor=tk.CENTER ) def zoom_image(self, event): """マウスホイールでズーム""" old_zoom = self.zoom_factor if event.delta > 0: self.zoom_factor *= 1.1 # ズームイン else: self.zoom_factor /= 1.1 # ズームアウト # ズーム位置の補正 scale_factor = self.zoom_factor / old_zoom self.offset_x = int(self.offset_x * scale_factor) self.offset_y = int(self.offset_y * scale_factor) self.update_image() def start_drag(self, event): """マウスドラッグ開始""" self.start_x = event.x self.start_y = event.y self.is_dragging = True def do_drag(self, event): """マウスドラッグ処理""" if self.is_dragging: self.offset_x += event.x - self.start_x self.offset_y += event.y - self.start_y self.start_x = event.x self.start_y = event.y self.update_image() def end_drag(self, event): """マウスドラッグ終了""" self.is_dragging = False if __name__ == "__main__": app = ImageViewer() app.mainloop()