xxwyyds commited on
Commit
2fcf971
·
verified ·
1 Parent(s): 25a7324

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +490 -242
app.py CHANGED
@@ -7,9 +7,25 @@ from src.predict import process_single_image
7
  import sys
8
  sys.path.insert(0, "./src")
9
 
10
- def get_example_images(folder_path="/home/xxw/Loupe/ffhq"):
11
- return [os.path.join(folder_path, f) for f in os.listdir(folder_path)
12
- if f.lower().endswith(('.png', '.jpg', '.jpeg'))]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  def safe_extract_prob(cls_probs):
15
  """安全地从cls_probs中提取概率值"""
@@ -26,74 +42,420 @@ def safe_extract_prob(cls_probs):
26
  print(f"Error extracting probability: {e}")
27
  return 0.0
28
 
29
- with gr.Blocks(title="Loupe图像伪造检测系统", theme=gr.themes.Soft()) as demo:
30
- gr.Markdown("""
31
- # Loupe🕵️‍♂️ 图像伪造检测系统
32
- ### 上传图像或从示例中选择,系统将检测图像中的伪造区域
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  """)
34
 
35
- with gr.Row():
36
- with gr.Column(scale=1):
37
- with gr.Tab("上传图像"):
38
- image_input = gr.Image(type="pil", label="原始图像")
39
- upload_button = gr.Button("检测伪造", variant="primary")
40
-
41
- with gr.Tab("选择示例"):
42
- example_images = get_example_images()
43
- example_dropdown = gr.Dropdown(
44
- choices=example_images,
45
- label="选择示例图像",
46
- value=example_images[0] if example_images else None
47
- )
48
- example_button = gr.Button("检测示例", variant="secondary")
49
-
50
- with gr.Accordion("高级选项", open=False):
51
- threshold = gr.Slider(0, 1, value=0.5, label="检测阈值")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
- with gr.Column(scale=1):
54
- gr.Markdown("### 检测结果")
55
- with gr.Tabs():
56
- with gr.Tab("处理后的图像"):
57
- output_image = gr.Image(label="伪造检测结果", interactive=False)
 
 
 
 
 
 
 
58
 
59
- with gr.Tab("对比视图"):
60
  with gr.Row():
61
- original_display = gr.Image(label="原始图像", interactive=False)
62
- processed_display = gr.Image(label="处理后图像", interactive=False)
63
-
64
- with gr.Group():
65
  with gr.Row():
66
- fake_prob = gr.Number(label="伪造概率", precision=4)
67
- # Simplified to just show the probability as text
68
- result_text = gr.Textbox(label="检测结果", interactive=False)
69
-
70
- save_button = gr.Button("保存结果", variant="secondary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
- gr.Markdown("""
73
- ---
74
- ### 关于
75
- - **技术**: Forgery Image Detection and Localization.
76
- - **版本**: 1.0.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  """)
78
 
79
  def process_image(image, threshold_value):
 
 
 
 
 
 
 
 
80
  with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_file:
81
  image_path = tmp_file.name
82
  image.save(image_path)
83
 
84
  try:
85
  processed_img, cls_probs = process_single_image(image_path)
86
-
87
- # 安全提取概率值
88
  prob = safe_extract_prob(cls_probs)
89
- print(f"Classification probability: {prob:.4f}" if prob is not None else "No cls output")
 
 
 
 
 
 
 
 
 
 
90
 
91
  return {
92
  output_image: processed_img,
93
  original_display: image,
94
  processed_display: processed_img,
95
  fake_prob: prob,
96
- result_text: f"伪造概率: {prob:.4f}"
97
  }
98
  except Exception as e:
99
  print(f"Error in processing: {e}")
@@ -102,29 +464,62 @@ with gr.Blocks(title="Loupe图像伪造检测系统", theme=gr.themes.Soft()) as
102
  original_display: image,
103
  processed_display: None,
104
  fake_prob: 0.0,
105
- result_text: "处理错误"
106
  }
107
  finally:
108
  if os.path.exists(image_path):
109
  os.unlink(image_path)
110
-
111
- def process_example(image_path, threshold_value):
 
 
 
 
 
 
 
 
 
 
 
 
112
  try:
113
- processed_img, cls_probs = process_single_image(image_path)
 
114
 
115
- # 安全提取概率值
116
- prob = safe_extract_prob(cls_probs)
117
- print(f"Classification probability: {prob:.4f}" if prob is not None else "No cls output")
 
 
 
 
 
 
118
 
 
 
 
119
  original_img = Image.open(image_path)
120
 
 
 
 
 
 
 
 
 
 
 
 
121
  return {
122
  image_input: original_img,
123
  output_image: processed_img,
124
  original_display: original_img,
125
  processed_display: processed_img,
126
  fake_prob: prob,
127
- result_text: f"伪造概率: {prob:.4f}",
128
  threshold: threshold_value
129
  }
130
  except Exception as e:
@@ -135,207 +530,60 @@ with gr.Blocks(title="Loupe图像伪造检测系统", theme=gr.themes.Soft()) as
135
  original_display: None,
136
  processed_display: None,
137
  fake_prob: 0.0,
138
- result_text: "处理错误",
139
  threshold: threshold_value
140
  }
141
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  upload_button.click(
143
  process_image,
144
  [image_input, threshold],
145
  [output_image, original_display, processed_display, fake_prob, result_text]
146
  )
147
 
 
 
 
 
 
 
 
 
148
  example_button.click(
149
  process_example,
150
- [example_dropdown, threshold],
151
  [image_input, output_image, original_display, processed_display, fake_prob, result_text, threshold]
152
  )
153
 
154
  save_button.click(
155
- lambda img: img.save("result.jpg") if img else None,
156
  [output_image],
157
- None
 
 
 
 
 
 
 
158
  )
159
 
160
  if __name__ == "__main__":
161
- demo.launch(server_name="0.0.0.0", server_port=7860)
162
-
163
- # import gradio as gr
164
- # import os
165
- # import tempfile
166
- # import numpy as np
167
- # from PIL import Image
168
- # from src.predict import process_single_image
169
- # import sys
170
- # sys.path.insert(0, "./src") # 确保src目录在路径中
171
- # # 可以处理无mask的图像 也可以处理有mask的两张图像
172
-
173
-
174
- # # 获取图像文件夹中的图片列表
175
- # def get_example_images(folder_path="/home/xxw/Loupe/ffhq"):
176
- # return [os.path.join(folder_path, f) for f in os.listdir(folder_path)
177
- # if f.lower().endswith(('.png', '.jpg', '.jpeg'))]
178
-
179
-
180
- # # 创建Gradio界面
181
- # with gr.Blocks(title="Loupe图像伪造检测系统", theme=gr.themes.Soft()) as demo:
182
- # # 标题和描述
183
- # gr.Markdown("""
184
- # # Loupe🕵️‍♂️ 图像伪造检测系统
185
- # ### 上传图像或从示例中选择,系统将检测图像中的伪造区域
186
- # """)
187
-
188
- # with gr.Row():
189
- # # 左侧面板 - 原始图像
190
- # with gr.Column(scale=1):
191
- # with gr.Tab("上传图像"):
192
- # image_input = gr.Image(type="pil", label="原始图像")
193
- # upload_button = gr.Button("检测伪造", variant="primary")
194
-
195
- # with gr.Tab("选择示例"):
196
- # example_images = get_example_images()
197
- # example_dropdown = gr.Dropdown(
198
- # choices=example_images,
199
- # label="选择示例图像",
200
- # value=example_images[0] if example_images else None
201
- # )
202
- # example_button = gr.Button("检测示例", variant="secondary")
203
-
204
- # with gr.Accordion("高级选项", open=False):
205
- # threshold = gr.Slider(0, 1, value=0.5,
206
- # label="检测阈值",
207
- # info="调整伪造检测的敏感度")
208
- # processing_mode = gr.Radio(
209
- # ["快速模式", "精确模式"],
210
- # value="快速模式",
211
- # label="处理模式"
212
- # )
213
-
214
- # # 右侧输出面板
215
- # with gr.Column(scale=1):
216
- # gr.Markdown("### 检测结果")
217
- # with gr.Tabs():
218
- # with gr.Tab("处理后的图像"):
219
- # output_image = gr.Image(label="伪造检测结果", interactive=False)
220
-
221
- # with gr.Tab("对比视图"):
222
- # with gr.Row():
223
- # original_display = gr.Image(label="原始图像", interactive=False)
224
- # processed_display = gr.Image(label="处理后图像", interactive=False)
225
-
226
- # with gr.Group():
227
- # with gr.Row():
228
- # fake_prob = gr.Number(label="伪造概率", precision=2)
229
- # result_label = gr.Label(label="检测结论")
230
-
231
- # save_button = gr.Button("保存结果", variant="secondary")
232
-
233
- # # 底部信息
234
- # gr.Markdown("""
235
- # ---
236
- # ### 关于
237
- # - **技术**: Forgery Image Detection and Localization.
238
- # - **版本**: 1.0.0
239
- # - **开发者**: xxw/teleai EVOL lab
240
- # """)
241
-
242
- # # 定义处理函数
243
- # def process_image(image, threshold_value):
244
- # # 创建一个临时文件保存上传的图像
245
- # with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_file:
246
- # image_path = tmp_file.name
247
- # image.save(image_path)
248
-
249
- # try:
250
- # # 调用你的处理函数
251
- # processed_img, cls_probs = process_single_image(image_path)
252
-
253
- # # 获取伪造概率(假设cls_probs是一个数组,取第一个值)
254
- # prob = float(cls_probs[0]) if cls_probs else 0.0
255
-
256
- # # 确定结果 - 修改为返回字典格式
257
- # result = {
258
- # "label": "伪造图像" if prob > threshold_value else "真实图像",
259
- # "confidences": [
260
- # {"label": "伪造", "confidence": prob},
261
- # {"label": "真实", "confidence": 1 - prob}
262
- # ]
263
- # }
264
-
265
- # return {
266
- # output_image: processed_img,
267
- # original_display: image,
268
- # processed_display: processed_img,
269
- # fake_prob: prob,
270
- # result_label: result # 使用正确的字典格式
271
- # }
272
- # finally:
273
- # # 清理临时文件
274
- # if os.path.exists(image_path):
275
- # os.unlink(image_path)
276
-
277
- # def process_example(image_path, threshold_value):
278
- # # 直接调用你的处理函数
279
- # processed_img, cls_probs = process_single_image(image_path)
280
-
281
- # # 获取伪造概率
282
- # prob = float(cls_probs[0]) if cls_probs else 0.0
283
-
284
- # # 确定结果 - 修改为返回字典格式
285
- # result = {
286
- # "label": "伪造图像" if prob > threshold_value else "真实图像",
287
- # "confidences": [
288
- # {"label": "伪造", "confidence": prob},
289
- # {"label": "真实", "confidence": 1 - prob}
290
- # ]
291
- # }
292
-
293
- # # 打开原始图像用于显示
294
- # original_img = Image.open(image_path)
295
-
296
- # return {
297
- # image_input: original_img,
298
- # output_image: processed_img,
299
- # original_display: original_img,
300
- # processed_display: processed_img,
301
- # fake_prob: prob,
302
- # result_label: result, # 使用正确的字典格式
303
- # threshold: threshold_value
304
- # }
305
-
306
- # # 修改绑定事件
307
- # upload_button.click(
308
- # fn=process_image,
309
- # inputs=[image_input, threshold],
310
- # outputs=[output_image, original_display, processed_display, fake_prob, result_label]
311
- # )
312
-
313
- # def load_example_image(example_path):
314
- # try:
315
- # return Image.open(example_path)
316
- # except:
317
- # return None
318
-
319
- # example_button.click(
320
- # fn=process_example,
321
- # inputs=[example_dropdown, threshold],
322
- # outputs=[image_input, output_image, original_display, processed_display, fake_prob, result_label, threshold]
323
- # )
324
-
325
- # save_button.click(
326
- # fn=lambda img: (img.save("result.jpg") if img else None) or "结果已保存!",
327
- # inputs=[output_image],
328
- # outputs=gr.Textbox(visible=True, label="保存状态"),
329
- # api_name="save_result"
330
- # )
331
-
332
-
333
- # # def greet(name):
334
- # # return "Hello " + name + "!!"
335
-
336
- # # demo = gr.Interface(fn=greet, inputs="text", outputs="text")
337
- # # demo.launch()
338
-
339
- # # 启动应用
340
- # if __name__ == "__main__":
341
- # demo.launch(server_name="0.0.0.0", server_port=7860)
 
7
  import sys
8
  sys.path.insert(0, "./src")
9
 
10
+ # 自定义主题 - 炫彩现代化
11
+ custom_theme = gr.themes.Default(
12
+ primary_hue="purple",
13
+ secondary_hue="pink",
14
+ neutral_hue="slate",
15
+ font=[gr.themes.GoogleFont("Poppins"), gr.themes.GoogleFont("Inter"), "Arial", "sans-serif"]
16
+ ).set(
17
+ button_primary_background_fill="linear-gradient(45deg, #667eea 0%, #764ba2 100%)",
18
+ button_primary_background_fill_hover="linear-gradient(45deg, #764ba2 0%, #667eea 100%)",
19
+ button_primary_text_color="white",
20
+ button_secondary_background_fill="linear-gradient(45deg, #f093fb 0%, #f5576c 100%)",
21
+ button_secondary_background_fill_hover="linear-gradient(45deg, #f5576c 0%, #f093fb 100%)",
22
+ button_secondary_text_color="white"
23
+ )
24
+
25
+ def get_example_images(folder_path="ffhq"):
26
+ """获取示例图片列表"""
27
+ return sorted([os.path.join(folder_path, f) for f in os.listdir(folder_path)
28
+ if f.lower().endswith(('.png', '.jpg', '.jpeg'))])
29
 
30
  def safe_extract_prob(cls_probs):
31
  """安全地从cls_probs中提取概率值"""
 
42
  print(f"Error extracting probability: {e}")
43
  return 0.0
44
 
45
+ # 创建主界面
46
+ with gr.Blocks(
47
+ title="Loupe - AI图像伪造检测系统",
48
+ theme=custom_theme,
49
+ css="""
50
+ /* 全局样式 */
51
+ body {
52
+ background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
53
+ background-size: 400% 400%;
54
+ animation: gradientBG 15s ease infinite;
55
+ min-height: 100vh;
56
+ }
57
+
58
+ @keyframes gradientBG {
59
+ 0% { background-position: 0% 50%; }
60
+ 50% { background-position: 100% 50%; }
61
+ 100% { background-position: 0% 50%; }
62
+ }
63
+
64
+ /* 主容器样式 */
65
+ .gradio-container {
66
+ background: rgba(255, 255, 255, 0.95);
67
+ backdrop-filter: blur(10px);
68
+ border-radius: 20px;
69
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
70
+ margin: 20px;
71
+ padding: 20px;
72
+ }
73
+
74
+ /* 标题样式 */
75
+ .title-box {
76
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
77
+ padding: 30px;
78
+ border-radius: 15px;
79
+ margin-bottom: 30px;
80
+ box-shadow: 0 15px 35px rgba(102, 126, 234, 0.3);
81
+ position: relative;
82
+ overflow: hidden;
83
+ }
84
+
85
+ .title-box::before {
86
+ content: '';
87
+ position: absolute;
88
+ top: -50%;
89
+ left: -50%;
90
+ width: 200%;
91
+ height: 200%;
92
+ background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.1), transparent);
93
+ animation: shine 3s infinite;
94
+ }
95
+
96
+ @keyframes shine {
97
+ 0% { transform: translateX(-100%) translateY(-100%) rotate(45deg); }
98
+ 100% { transform: translateX(100%) translateY(100%) rotate(45deg); }
99
+ }
100
+
101
+ .title-text {
102
+ font-weight: 700;
103
+ font-size: 32px;
104
+ color: white;
105
+ margin-bottom: 8px;
106
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
107
+ background: linear-gradient(45deg, #fff, #f0f8ff);
108
+ -webkit-background-clip: text;
109
+ -webkit-text-fill-color: transparent;
110
+ background-clip: text;
111
+ }
112
+
113
+ .subtitle-text {
114
+ color: rgba(255, 255, 255, 0.9);
115
+ font-size: 18px;
116
+ font-weight: 300;
117
+ text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2);
118
+ }
119
+
120
+ /* 输入和结果框样式 */
121
+ .input-box, .result-box {
122
+ background: linear-gradient(145deg, rgba(255, 255, 255, 0.9), rgba(248, 250, 252, 0.9));
123
+ padding: 25px;
124
+ border-radius: 15px;
125
+ margin-bottom: 20px;
126
+ border: 1px solid rgba(255, 255, 255, 0.3);
127
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
128
+ backdrop-filter: blur(10px);
129
+ transition: all 0.3s ease;
130
+ }
131
+
132
+ .input-box:hover, .result-box:hover {
133
+ transform: translateY(-5px);
134
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
135
+ }
136
+
137
+ .input-title, .result-title {
138
+ font-weight: 700;
139
+ background: linear-gradient(45deg, #667eea, #764ba2);
140
+ -webkit-background-clip: text;
141
+ -webkit-text-fill-color: transparent;
142
+ background-clip: text;
143
+ margin-bottom: 15px;
144
+ font-size: 20px;
145
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
146
+ }
147
+
148
+ /* 按钮样式 */
149
+ .btn-primary {
150
+ background: linear-gradient(45deg, #667eea 0%, #764ba2 100%);
151
+ border: none;
152
+ border-radius: 25px;
153
+ padding: 12px 30px;
154
+ font-weight: 600;
155
+ text-transform: uppercase;
156
+ letter-spacing: 1px;
157
+ box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
158
+ transition: all 0.3s ease;
159
+ }
160
+
161
+ .btn-primary:hover {
162
+ transform: translateY(-3px);
163
+ box-shadow: 0 15px 30px rgba(102, 126, 234, 0.4);
164
+ background: linear-gradient(45deg, #764ba2 0%, #667eea 100%);
165
+ }
166
+
167
+ .btn-secondary {
168
+ background: linear-gradient(45deg, #f093fb 0%, #f5576c 100%);
169
+ border: none;
170
+ border-radius: 25px;
171
+ padding: 10px 25px;
172
+ font-weight: 600;
173
+ box-shadow: 0 8px 16px rgba(240, 147, 251, 0.3);
174
+ transition: all 0.3s ease;
175
+ }
176
+
177
+ .btn-secondary:hover {
178
+ transform: translateY(-2px);
179
+ box-shadow: 0 12px 24px rgba(240, 147, 251, 0.4);
180
+ }
181
+
182
+ /* 图片上传区域 */
183
+ #upload_image {
184
+ min-height: 350px;
185
+ border: 3px dashed rgba(102, 126, 234, 0.3);
186
+ border-radius: 15px;
187
+ background: linear-gradient(45deg, rgba(102, 126, 234, 0.05), rgba(118, 75, 162, 0.05));
188
+ transition: all 0.3s ease;
189
+ }
190
+
191
+ #upload_image:hover {
192
+ border-color: rgba(102, 126, 234, 0.6);
193
+ background: linear-gradient(45deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1));
194
+ transform: scale(1.02);
195
+ }
196
+
197
+ /* 概率显示 */
198
+ #probability input {
199
+ font-weight: bold;
200
+ background: linear-gradient(45deg, #667eea, #764ba2);
201
+ -webkit-background-clip: text;
202
+ -webkit-text-fill-color: transparent;
203
+ background-clip: text;
204
+ font-size: 1.2em;
205
+ }
206
+
207
+ #result_text input {
208
+ font-size: 1.1em;
209
+ font-weight: 600;
210
+ background: linear-gradient(45deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1));
211
+ border-radius: 10px;
212
+ border: 2px solid rgba(102, 126, 234, 0.2);
213
+ }
214
+
215
+ /* 画廊样式 */
216
+ .gallery-item {
217
+ border-radius: 12px !important;
218
+ transition: all 0.3s ease;
219
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
220
+ }
221
+
222
+ .gallery-item:hover {
223
+ transform: scale(1.05);
224
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
225
+ }
226
+
227
+ /* 示例按钮 */
228
+ .example-btn {
229
+ margin-top: 15px;
230
+ width: 100%;
231
+ background: linear-gradient(45deg, #23a6d5 0%, #23d5ab 100%);
232
+ border-radius: 20px;
233
+ font-weight: 600;
234
+ box-shadow: 0 8px 16px rgba(35, 166, 213, 0.3);
235
+ transition: all 0.3s ease;
236
+ }
237
+
238
+ .example-btn:hover {
239
+ transform: translateY(-2px);
240
+ box-shadow: 0 12px 24px rgba(35, 166, 213, 0.4);
241
+ }
242
+
243
+ /* Tab 样式 */
244
+ .tab-nav button {
245
+ border-radius: 15px 15px 0 0;
246
+ background: linear-gradient(45deg, rgba(102, 126, 234, 0.8), rgba(118, 75, 162, 0.8));
247
+ color: white;
248
+ font-weight: 600;
249
+ transition: all 0.3s ease;
250
+ }
251
+
252
+ .tab-nav button:hover {
253
+ background: linear-gradient(45deg, rgba(118, 75, 162, 0.9), rgba(102, 126, 234, 0.9));
254
+ transform: translateY(-2px);
255
+ }
256
+
257
+ /* 滑块样式 */
258
+ .gr-slider input[type="range"] {
259
+ background: linear-gradient(45deg, #667eea, #764ba2);
260
+ border-radius: 10px;
261
+ }
262
+
263
+ /* 手风琴样式 */
264
+ .gr-accordion {
265
+ background: linear-gradient(145deg, rgba(255, 255, 255, 0.8), rgba(248, 250, 252, 0.8));
266
+ border-radius: 15px;
267
+ border: 1px solid rgba(102, 126, 234, 0.2);
268
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
269
+ }
270
+
271
+ /* 炫彩加载动画 */
272
+ @keyframes rainbow {
273
+ 0% { background-position: 0% 50%; }
274
+ 50% { background-position: 100% 50%; }
275
+ 100% { background-position: 0% 50%; }
276
+ }
277
+
278
+ .processing {
279
+ background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
280
+ background-size: 400% 400%;
281
+ animation: rainbow 2s ease infinite;
282
+ }
283
+
284
+ /* 响应式设计 */
285
+ @media (max-width: 768px) {
286
+ .title-text { font-size: 24px; }
287
+ .subtitle-text { font-size: 16px; }
288
+ .input-box, .result-box { padding: 20px; }
289
+ }
290
+ """
291
+ ) as demo:
292
+
293
+ # 标题部分 - 炫彩渐变设计
294
+ with gr.Column(elem_classes="title-box"):
295
+ gr.Markdown("""
296
+ <div class="title-text">🔍 Loupe 图像伪造检测系统</div>
297
+ <div class="subtitle-text">✨ 基于深度学习的图像伪造检测与定位技术</div>
298
+ """)
299
+
300
+ # 添加装饰性分割线
301
+ gr.HTML("""
302
+ <div style="height: 4px; background: linear-gradient(90deg, #667eea, #764ba2, #f093fb, #f5576c, #23a6d5, #23d5ab);
303
+ border-radius: 2px; margin: 20px 0; box-shadow: 0 2px 10px rgba(0,0,0,0.2);"></div>
304
  """)
305
 
306
+ # 主界面组件
307
+ with gr.Row(equal_height=True):
308
+ with gr.Column(scale=1, min_width=300):
309
+ # 输入图像区域 - 炫彩设计
310
+ with gr.Column(elem_classes="input-box"):
311
+ gr.Markdown("""<div class="input-title">🎨 输入图像</div>""")
312
+ with gr.Tabs():
313
+ with gr.Tab("📤 上传图片", id="upload_tab"):
314
+ image_input = gr.Image(type="pil", label="", elem_id="upload_image")
315
+ upload_button = gr.Button("🚀 开始检测", variant="primary", size="lg", elem_classes="btn-primary")
316
+
317
+ with gr.Tab("🖼️ 示例图片", id="example_tab"):
318
+ example_images = get_example_images()
319
+ example_gallery = gr.Gallery(
320
+ value=example_images,
321
+ label="",
322
+ columns=4,
323
+ rows=None,
324
+ height="auto",
325
+ object_fit="contain",
326
+ allow_preview=True,
327
+ selected_index=None
328
+ )
329
+ # 添加炫彩检测按钮
330
+ example_button = gr.Button(
331
+ "✨ 检测选中的示例图片",
332
+ variant="primary",
333
+ elem_classes="example-btn"
334
+ )
335
+ # 隐藏组件用于存储选中索引
336
+ selected_index = gr.Number(visible=False)
337
+
338
+ with gr.Accordion("⚙️ 高级设置", open=False):
339
+ threshold = gr.Slider(0, 1, value=0.5, step=0.01, label="🎯 检测敏感度")
340
+ gr.HTML("""
341
+ <div style="background: linear-gradient(45deg, rgba(102,126,234,0.1), rgba(118,75,162,0.1));
342
+ padding: 10px; border-radius: 8px; margin-top: 10px;">
343
+ <small style="color: #667eea; font-weight: 500;">💡 调整数值可改变检测的严格程度</small>
344
+ </div>
345
+ """)
346
 
347
+ with gr.Column(scale=1.5, min_width=500):
348
+ # 检测结果区域 - 炫彩设计
349
+ with gr.Column(elem_classes="result-box"):
350
+ gr.Markdown("""<div class="result-title">🎯 检测结果</div>""")
351
+ with gr.Tabs():
352
+ with gr.Tab("🔍 检测效果", id="result_tab"):
353
+ output_image = gr.Image(label="伪造区域标记", interactive=False)
354
+
355
+ with gr.Tab("⚖️ 对比视图", id="compare_tab"):
356
+ with gr.Row():
357
+ original_display = gr.Image(label="原始图像", interactive=False)
358
+ processed_display = gr.Image(label="检测结果", interactive=False)
359
 
360
+ with gr.Group():
361
  with gr.Row():
362
+ fake_prob = gr.Number(label="🎲 伪造概率", precision=2, elem_id="probability")
363
+ result_text = gr.Textbox(label="📝 检测结论", interactive=False, elem_id="result_text")
364
+
 
365
  with gr.Row():
366
+ save_button = gr.Button("💾 保存结果", variant="secondary", elem_classes="btn-secondary")
367
+ clear_button = gr.Button("🧹 清除", variant="secondary", elem_classes="btn-secondary")
368
+
369
+ # 关于部分 - 炫彩设计
370
+ with gr.Accordion("🌟 关于系统", open=False):
371
+ gr.HTML("""
372
+ <div style="background: linear-gradient(135deg, rgba(102,126,234,0.1), rgba(118,75,162,0.1), rgba(240,147,251,0.1));
373
+ padding: 20px; border-radius: 15px; border: 1px solid rgba(102,126,234,0.2);">
374
+ <h3 style="background: linear-gradient(45deg, #667eea, #764ba2); -webkit-background-clip: text;
375
+ -webkit-text-fill-color: transparent; margin-bottom: 15px;">
376
+ ✨ Loupe 伪造图像检测系统
377
+ </h3>
378
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
379
+ <div style="background: rgba(102,126,234,0.1); padding: 15px; border-radius: 10px;">
380
+ <strong style="color: #667eea;">🚀 技术</strong><br>
381
+ 基于深度学习的图像伪造检测与定位
382
+ </div>
383
+ <div style="background: rgba(118,75,162,0.1); padding: 15px; border-radius: 10px;">
384
+ <strong style="color: #764ba2;">⭐ 特点</strong><br>
385
+ 高精度、实时处理、可解释性强
386
+ </div>
387
+ <div style="background: rgba(240,147,251,0.1); padding: 15px; border-radius: 10px;">
388
+ <strong style="color: #f093fb;">📱 版本</strong><br>
389
+ v2.0.0 炫彩版
390
+ </div>
391
+ <div style="background: rgba(245,87,108,0.1); padding: 15px; border-radius: 10px;">
392
+ <strong style="color: #f5576c;">👥 开发者</strong><br>
393
+ EVOL Lab (jyc, xxw)
394
+ </div>
395
+ </div>
396
+ <div style="margin-top: 20px; padding: 15px; background: linear-gradient(45deg, rgba(35,166,213,0.1), rgba(35,213,171,0.1));
397
+ border-radius: 10px; border-left: 4px solid #23a6d5;">
398
+ <strong style="color: #23a6d5;">💡 系统介绍</strong><br>
399
+ 本系统可检测多种图像篡改痕迹,包括复制-移动、拼接、擦除等操作。采用最新的深度学习算法,提供高精度的检测结果和直观的可视化分析。
400
+ </div>
401
+ </div>
402
+ """)
403
 
404
+ # 页脚 - 炫彩设计
405
+ gr.HTML("""
406
+ <div style="margin-top: 40px; padding: 20px; text-align: center;
407
+ background: linear-gradient(135deg, rgba(102,126,234,0.1), rgba(118,75,162,0.1));
408
+ border-radius: 15px; border-top: 2px solid rgba(102,126,234,0.3);">
409
+ <div style="background: linear-gradient(45deg, #667eea, #764ba2); -webkit-background-clip: text;
410
+ -webkit-text-fill-color: transparent; font-weight: 600; margin-bottom: 10px;">
411
+ ✨ 感谢使用 Loupe 图像伪造检测系统 ✨
412
+ </div>
413
+ <div style="color: #64748b; font-size: 14px;">
414
+ © 2025 EVOL Lab | 让AI守护图像真实性 🛡️
415
+ </div>
416
+ <div style="margin-top: 10px;">
417
+ <span style="background: linear-gradient(45deg, #f093fb, #f5576c); -webkit-background-clip: text;
418
+ -webkit-text-fill-color: transparent; font-weight: 500;">
419
+ 🌟 科技点亮未来,智能守护真实 🌟
420
+ </span>
421
+ </div>
422
+ </div>
423
  """)
424
 
425
  def process_image(image, threshold_value):
426
+ """处理上传的图像"""
427
+ if image is None:
428
+ return {
429
+ output_image: None,
430
+ fake_prob: 0.0,
431
+ result_text: "❌ 请上传有效图像"
432
+ }
433
+
434
  with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_file:
435
  image_path = tmp_file.name
436
  image.save(image_path)
437
 
438
  try:
439
  processed_img, cls_probs = process_single_image(image_path)
 
 
440
  prob = safe_extract_prob(cls_probs)
441
+
442
+ # 根据概率生成炫彩结论
443
+ if prob > threshold_value + 0.2:
444
+ conclusion = "🚨 高度疑似伪造"
445
+ emoji = "🔴"
446
+ elif prob > threshold_value:
447
+ conclusion = "⚠️ 可能伪造"
448
+ emoji = "🟡"
449
+ else:
450
+ conclusion = "✅ 未检测到伪造"
451
+ emoji = "🟢"
452
 
453
  return {
454
  output_image: processed_img,
455
  original_display: image,
456
  processed_display: processed_img,
457
  fake_prob: prob,
458
+ result_text: f"{emoji} {conclusion} (概率: {prob:.2f})"
459
  }
460
  except Exception as e:
461
  print(f"Error in processing: {e}")
 
464
  original_display: image,
465
  processed_display: None,
466
  fake_prob: 0.0,
467
+ result_text: f"❌ 处理错误: {str(e)}"
468
  }
469
  finally:
470
  if os.path.exists(image_path):
471
  os.unlink(image_path)
472
+
473
+ def process_example(example_data, selected_idx, threshold_value):
474
+ """处理示例图像"""
475
+ if not example_data or selected_idx is None:
476
+ return {
477
+ image_input: None,
478
+ output_image: None,
479
+ original_display: None,
480
+ processed_display: None,
481
+ fake_prob: 0.0,
482
+ result_text: "⚠️ 请先选择示例图片",
483
+ threshold: threshold_value
484
+ }
485
+
486
  try:
487
+ selected_idx = int(selected_idx)
488
+ image_info = example_data[selected_idx]
489
 
490
+ # 处理不同的数据格式
491
+ if isinstance(image_info, (tuple, list)):
492
+ image_path = image_info[0] # (path, caption)格式
493
+ elif isinstance(image_info, dict):
494
+ image_path = image_info.get("name", image_info.get("path"))
495
+ else:
496
+ image_path = image_info
497
+
498
+ print(f"Processing selected image (index {selected_idx}): {image_path}") # 调试日志
499
 
500
+ # 处理图像
501
+ processed_img, cls_probs = process_single_image(image_path)
502
+ prob = safe_extract_prob(cls_probs)
503
  original_img = Image.open(image_path)
504
 
505
+ # 根据概率生成炫彩结论
506
+ if prob > threshold_value + 0.2:
507
+ conclusion = "🚨 高度疑似伪造"
508
+ emoji = "🔴"
509
+ elif prob > threshold_value:
510
+ conclusion = "⚠️ 可能伪造"
511
+ emoji = "🟡"
512
+ else:
513
+ conclusion = "✅ 未检测到伪造"
514
+ emoji = "🟢"
515
+
516
  return {
517
  image_input: original_img,
518
  output_image: processed_img,
519
  original_display: original_img,
520
  processed_display: processed_img,
521
  fake_prob: prob,
522
+ result_text: f"{emoji} {conclusion} (概率: {prob:.2f})",
523
  threshold: threshold_value
524
  }
525
  except Exception as e:
 
530
  original_display: None,
531
  processed_display: None,
532
  fake_prob: 0.0,
533
+ result_text: f"❌ 示例处理错误: {str(e)}",
534
  threshold: threshold_value
535
  }
536
+
537
+ def clear_all():
538
+ """清除所有输入输出"""
539
+ return {
540
+ image_input: None,
541
+ output_image: None,
542
+ original_display: None,
543
+ processed_display: None,
544
+ fake_prob: 0.0,
545
+ result_text: "🧹 已清除所有数据"
546
+ }
547
+
548
+ def update_selected_index(evt: gr.SelectData):
549
+ """更新选中的图片索引"""
550
+ return evt.index
551
+
552
+ # 交互逻辑
553
  upload_button.click(
554
  process_image,
555
  [image_input, threshold],
556
  [output_image, original_display, processed_display, fake_prob, result_text]
557
  )
558
 
559
+ # 示例图片选择事件
560
+ example_gallery.select(
561
+ update_selected_index,
562
+ None,
563
+ selected_index
564
+ )
565
+
566
+ # 示例图片检测按钮点击事件
567
  example_button.click(
568
  process_example,
569
+ [example_gallery, selected_index, threshold],
570
  [image_input, output_image, original_display, processed_display, fake_prob, result_text, threshold]
571
  )
572
 
573
  save_button.click(
574
+ lambda img: (img.save("result.jpg"), "💾 结果已保存为 result.jpg")[1] if img else "❌ 没有图像可保存",
575
  [output_image],
576
+ None,
577
+ api_name="save_result"
578
+ )
579
+
580
+ clear_button.click(
581
+ clear_all,
582
+ [],
583
+ [image_input, output_image, original_display, processed_display, fake_prob, result_text]
584
  )
585
 
586
  if __name__ == "__main__":
587
+ demo.launch(
588
+ favicon_path="./favicon.ico" if os.path.exists("./favicon.ico") else None
589
+ )