Upload Image_Artifact_Visualizer.py
Browse files- Image_Artifact_Visualizer.py +141 -0
Image_Artifact_Visualizer.py
ADDED
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import cv2
|
2 |
+
import numpy as np
|
3 |
+
import tkinter as tk
|
4 |
+
from tkinter import filedialog
|
5 |
+
from tkinterdnd2 import DND_FILES, TkinterDnD
|
6 |
+
from PIL import Image, ImageTk
|
7 |
+
import os
|
8 |
+
|
9 |
+
class ImageViewer(TkinterDnD.Tk):
|
10 |
+
def __init__(self):
|
11 |
+
super().__init__()
|
12 |
+
|
13 |
+
self.title("Image Artifact Visualizer")
|
14 |
+
self.geometry("800x600")
|
15 |
+
|
16 |
+
# キャンバスを作成
|
17 |
+
self.canvas = tk.Canvas(self, bg="black")
|
18 |
+
self.canvas.pack(fill=tk.BOTH, expand=True)
|
19 |
+
|
20 |
+
# 画像データ
|
21 |
+
self.image = None
|
22 |
+
self.tk_image = None
|
23 |
+
self.zoom_factor = 1.0
|
24 |
+
self.offset_x = 0
|
25 |
+
self.offset_y = 0
|
26 |
+
|
27 |
+
# マウスドラッグ用
|
28 |
+
self.start_x = 0
|
29 |
+
self.start_y = 0
|
30 |
+
self.is_dragging = False
|
31 |
+
|
32 |
+
# イベント設定
|
33 |
+
self.canvas.bind("<MouseWheel>", self.zoom_image)
|
34 |
+
self.canvas.bind("<ButtonPress-1>", self.start_drag)
|
35 |
+
self.canvas.bind("<B1-Motion>", self.do_drag)
|
36 |
+
self.canvas.bind("<ButtonRelease-1>", self.end_drag)
|
37 |
+
|
38 |
+
# ドラッグ&ドロップの設定
|
39 |
+
self.drop_target_register(DND_FILES)
|
40 |
+
self.dnd_bind("<<Drop>>", self.on_drop)
|
41 |
+
|
42 |
+
# ファイル選択ボタン
|
43 |
+
btn = tk.Button(self, text="Select Image", command=self.select_file, font=("Arial", 12))
|
44 |
+
btn.pack(side=tk.BOTTOM, pady=10)
|
45 |
+
|
46 |
+
def select_file(self):
|
47 |
+
file_path = filedialog.askopenfilename(filetypes=[("Image Files", "*.jpg;*.jpeg;*.png;*.webp")])
|
48 |
+
if file_path:
|
49 |
+
self.load_image(file_path)
|
50 |
+
|
51 |
+
def on_drop(self, event):
|
52 |
+
file_path = event.data.strip("{}") # パスの前後の{}を削除
|
53 |
+
if os.path.isfile(file_path):
|
54 |
+
self.load_image(file_path)
|
55 |
+
|
56 |
+
def load_image(self, file_path):
|
57 |
+
try:
|
58 |
+
file_path = os.path.normpath(file_path) # 日本語や特殊文字対応
|
59 |
+
|
60 |
+
# Pillowで画像を読み込む(OpenCVのimreadの代わり)
|
61 |
+
img_pil = Image.open(file_path).convert("L") # モノクロ化
|
62 |
+
img_cv = np.array(img_pil) # OpenCV用に変換
|
63 |
+
|
64 |
+
# **アーティファクト強調処理**
|
65 |
+
img_cv = self.enhance_artifacts(img_cv)
|
66 |
+
|
67 |
+
self.image = Image.fromarray(img_cv) # OpenCV → PIL
|
68 |
+
self.zoom_factor = 1.0
|
69 |
+
self.offset_x = 0
|
70 |
+
self.offset_y = 0
|
71 |
+
self.update_image()
|
72 |
+
|
73 |
+
except Exception as e:
|
74 |
+
print(f"Error loading image: {e}")
|
75 |
+
|
76 |
+
def enhance_artifacts(self, img_cv):
|
77 |
+
"""JPEGノイズやブロックアーティファクトを強調"""
|
78 |
+
img_blur = cv2.GaussianBlur(img_cv, (3, 3), 0) # ぼかし
|
79 |
+
img_sharp = cv2.addWeighted(img_cv, 2.5, img_blur, -1.5, 0) # シャープ化
|
80 |
+
|
81 |
+
# ヒストグラム平坦化でコントラスト強調
|
82 |
+
img_eq = cv2.equalizeHist(img_sharp)
|
83 |
+
|
84 |
+
return img_eq
|
85 |
+
|
86 |
+
def update_image(self):
|
87 |
+
if self.image:
|
88 |
+
# 画像のサイズ調整
|
89 |
+
img_resized = self.image.resize(
|
90 |
+
(int(self.image.width * self.zoom_factor), int(self.image.height * self.zoom_factor)),
|
91 |
+
Image.Resampling.LANCZOS
|
92 |
+
)
|
93 |
+
self.tk_image = ImageTk.PhotoImage(img_resized)
|
94 |
+
|
95 |
+
# キャンバス更新
|
96 |
+
self.canvas.delete("all")
|
97 |
+
self.canvas.create_image(
|
98 |
+
self.offset_x + self.canvas.winfo_width() // 2,
|
99 |
+
self.offset_y + self.canvas.winfo_height() // 2,
|
100 |
+
image=self.tk_image,
|
101 |
+
anchor=tk.CENTER
|
102 |
+
)
|
103 |
+
|
104 |
+
def zoom_image(self, event):
|
105 |
+
"""マウスホイールでズーム"""
|
106 |
+
old_zoom = self.zoom_factor
|
107 |
+
if event.delta > 0:
|
108 |
+
self.zoom_factor *= 1.1 # ズームイン
|
109 |
+
else:
|
110 |
+
self.zoom_factor /= 1.1 # ズームアウト
|
111 |
+
|
112 |
+
# ズーム位置の補正
|
113 |
+
scale_factor = self.zoom_factor / old_zoom
|
114 |
+
self.offset_x = int(self.offset_x * scale_factor)
|
115 |
+
self.offset_y = int(self.offset_y * scale_factor)
|
116 |
+
|
117 |
+
self.update_image()
|
118 |
+
|
119 |
+
def start_drag(self, event):
|
120 |
+
"""マウスドラッグ開始"""
|
121 |
+
self.start_x = event.x
|
122 |
+
self.start_y = event.y
|
123 |
+
self.is_dragging = True
|
124 |
+
|
125 |
+
def do_drag(self, event):
|
126 |
+
"""マウスドラッグ処理"""
|
127 |
+
if self.is_dragging:
|
128 |
+
self.offset_x += event.x - self.start_x
|
129 |
+
self.offset_y += event.y - self.start_y
|
130 |
+
self.start_x = event.x
|
131 |
+
self.start_y = event.y
|
132 |
+
self.update_image()
|
133 |
+
|
134 |
+
def end_drag(self, event):
|
135 |
+
"""マウスドラッグ終了"""
|
136 |
+
self.is_dragging = False
|
137 |
+
|
138 |
+
if __name__ == "__main__":
|
139 |
+
app = ImageViewer()
|
140 |
+
app.mainloop()
|
141 |
+
|