|
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("<MouseWheel>", self.zoom_image)
|
|
self.canvas.bind("<ButtonPress-1>", self.start_drag)
|
|
self.canvas.bind("<B1-Motion>", self.do_drag)
|
|
self.canvas.bind("<ButtonRelease-1>", self.end_drag)
|
|
|
|
|
|
self.drop_target_register(DND_FILES)
|
|
self.dnd_bind("<<Drop>>", 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)
|
|
|
|
|
|
img_pil = Image.open(file_path).convert("L")
|
|
img_cv = np.array(img_pil)
|
|
|
|
|
|
img_cv = self.enhance_artifacts(img_cv)
|
|
|
|
self.image = Image.fromarray(img_cv)
|
|
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()
|
|
|
|
|