Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -5,13 +5,25 @@ import numpy as np
|
|
5 |
import cv2
|
6 |
from transformers import AutoImageProcessor, AutoModelForImageClassification
|
7 |
|
8 |
-
#
|
9 |
models = {
|
10 |
"model1": {
|
11 |
"name": "umm-maybe/AI-image-detector",
|
12 |
"processor": None,
|
13 |
"model": None,
|
14 |
-
"weight":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
}
|
16 |
}
|
17 |
|
@@ -26,7 +38,78 @@ for key in models:
|
|
26 |
models[key]["processor"] = None
|
27 |
models[key]["model"] = None
|
28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
def analyze_image_features(image):
|
|
|
30 |
# 转换为OpenCV格式
|
31 |
img_array = np.array(image)
|
32 |
if len(img_array.shape) == 3 and img_array.shape[2] == 3:
|
@@ -46,6 +129,13 @@ def analyze_image_features(image):
|
|
46 |
features["avg_red"] = float(np.mean(img_array[:,:,0]))
|
47 |
features["avg_green"] = float(np.mean(img_array[:,:,1]))
|
48 |
features["avg_blue"] = float(np.mean(img_array[:,:,2]))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
|
50 |
# 边缘一致性分析
|
51 |
edges = cv2.Canny(img_cv, 100, 200)
|
@@ -64,16 +154,168 @@ def analyze_image_features(image):
|
|
64 |
# 计算GLCM属性
|
65 |
features["texture_contrast"] = float(np.mean(graycoprops(glcm, 'contrast')[0]))
|
66 |
features["texture_homogeneity"] = float(np.mean(graycoprops(glcm, 'homogeneity')[0]))
|
|
|
|
|
67 |
|
68 |
# 噪声分析
|
69 |
if len(img_array.shape) == 3:
|
70 |
blurred = cv2.GaussianBlur(img_cv, (5, 5), 0)
|
71 |
noise = cv2.absdiff(img_cv, blurred)
|
72 |
features["noise_level"] = float(np.mean(noise))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
|
74 |
return features
|
75 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
76 |
def detect_ai_image(image):
|
|
|
77 |
if image is None:
|
78 |
return {"error": "未提供图像"}
|
79 |
|
@@ -90,36 +332,14 @@ def detect_ai_image(image):
|
|
90 |
with torch.no_grad():
|
91 |
outputs = model_info["model"](**inputs)
|
92 |
|
93 |
-
# 获取预测结果
|
94 |
-
logits = outputs.logits
|
95 |
-
predicted_class_idx = logits.argmax(-1).item()
|
96 |
-
|
97 |
# 获取概率
|
98 |
-
probabilities = torch.nn.functional.softmax(logits, dim=-1)
|
99 |
|
100 |
-
#
|
101 |
-
|
102 |
-
human_label_idx = None
|
103 |
-
|
104 |
-
for idx, label in model_info["model"].config.id2label.items():
|
105 |
-
label_lower = label.lower()
|
106 |
-
if "ai" in label_lower or "generated" in label_lower or "fake" in label_lower:
|
107 |
-
ai_label_idx = idx
|
108 |
-
if "human" in label_lower or "real" in label_lower:
|
109 |
-
human_label_idx = idx
|
110 |
-
|
111 |
-
# 修改后的标签解释逻辑
|
112 |
-
if human_label_idx is not None:
|
113 |
-
# 反转解释,将human标签视为AI生成的指标
|
114 |
-
ai_probability = float(probabilities[0][human_label_idx].item())
|
115 |
-
elif ai_label_idx is not None:
|
116 |
-
# 如果有AI标签
|
117 |
-
ai_probability = float(probabilities[0][ai_label_idx].item())
|
118 |
-
else:
|
119 |
-
# 默认使用索引1作为AI标签
|
120 |
-
ai_probability = float(probabilities[0][1].item())
|
121 |
|
122 |
# 添加到结果
|
|
|
123 |
results[key] = {
|
124 |
"model_name": model_info["name"],
|
125 |
"ai_probability": ai_probability,
|
@@ -150,32 +370,36 @@ def detect_ai_image(image):
|
|
150 |
|
151 |
# 低边缘密度通常表示AI生成
|
152 |
if image_features["edge_density"] < 0.01:
|
153 |
-
adjusted_probability += 0.
|
154 |
|
155 |
# 高纹理均匀性通常表示AI生成
|
156 |
-
if image_features["texture_homogeneity"] > 0.5:
|
157 |
adjusted_probability += 0.1
|
158 |
|
159 |
# 低噪声水平通常表示AI生成
|
160 |
if image_features["noise_level"] < 0.5:
|
161 |
adjusted_probability += 0.1
|
162 |
|
|
|
|
|
|
|
|
|
163 |
# 确保概率在0-1范围内
|
164 |
adjusted_probability = min(1.0, max(0.0, adjusted_probability))
|
165 |
|
166 |
-
#
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
else:
|
172 |
-
confidence_level = "无法确定"
|
173 |
|
174 |
# 构建最终结果
|
175 |
final_result = {
|
176 |
"ai_probability": adjusted_probability,
|
177 |
-
"
|
178 |
-
"
|
|
|
|
|
179 |
"individual_model_results": results,
|
180 |
"features": image_features
|
181 |
}
|
@@ -188,7 +412,7 @@ iface = gr.Interface(
|
|
188 |
inputs=gr.Image(type="pil"),
|
189 |
outputs=gr.JSON(),
|
190 |
title="增强型AI图像检测API",
|
191 |
-
description="多模型集成检测图像是否由AI
|
192 |
examples=None,
|
193 |
allow_flagging="never"
|
194 |
)
|
|
|
5 |
import cv2
|
6 |
from transformers import AutoImageProcessor, AutoModelForImageClassification
|
7 |
|
8 |
+
# 加载多个检测模型
|
9 |
models = {
|
10 |
"model1": {
|
11 |
"name": "umm-maybe/AI-image-detector",
|
12 |
"processor": None,
|
13 |
"model": None,
|
14 |
+
"weight": 0.5
|
15 |
+
},
|
16 |
+
"model2": {
|
17 |
+
"name": "microsoft/resnet-50", # 通用图像分类模型
|
18 |
+
"processor": None,
|
19 |
+
"model": None,
|
20 |
+
"weight": 0.25
|
21 |
+
},
|
22 |
+
"model3": {
|
23 |
+
"name": "google/vit-base-patch16-224", # Vision Transformer模型
|
24 |
+
"processor": None,
|
25 |
+
"model": None,
|
26 |
+
"weight": 0.25
|
27 |
}
|
28 |
}
|
29 |
|
|
|
38 |
models[key]["processor"] = None
|
39 |
models[key]["model"] = None
|
40 |
|
41 |
+
def process_model_output(model_info, outputs, probabilities):
|
42 |
+
"""处理不同模型的输出,统一返回AI生成概率"""
|
43 |
+
model_name = model_info["name"].lower()
|
44 |
+
|
45 |
+
# 针对不同模型的特殊处理
|
46 |
+
if "ai-image-detector" in model_name:
|
47 |
+
# umm-maybe/AI-image-detector模型特殊处理
|
48 |
+
# 检查标签
|
49 |
+
ai_label_idx = None
|
50 |
+
human_label_idx = None
|
51 |
+
|
52 |
+
for idx, label in model_info["model"].config.id2label.items():
|
53 |
+
label_lower = label.lower()
|
54 |
+
if "ai" in label_lower or "generated" in label_lower or "fake" in label_lower:
|
55 |
+
ai_label_idx = idx
|
56 |
+
if "human" in label_lower or "real" in label_lower:
|
57 |
+
human_label_idx = idx
|
58 |
+
|
59 |
+
# 修改后的标签解释逻辑 - 反转解释
|
60 |
+
if human_label_idx is not None:
|
61 |
+
# 将human标签视为AI生成的指标
|
62 |
+
ai_probability = float(probabilities[0][human_label_idx].item())
|
63 |
+
elif ai_label_idx is not None:
|
64 |
+
# 如果有AI标签
|
65 |
+
ai_probability = float(probabilities[0][ai_label_idx].item())
|
66 |
+
else:
|
67 |
+
# 默认使用索引1作为AI标签
|
68 |
+
ai_probability = float(probabilities[0][1].item())
|
69 |
+
|
70 |
+
elif "resnet" in model_name:
|
71 |
+
# 通用图像分类模型,使用简单启发式方法
|
72 |
+
predicted_class_idx = outputs.logits.argmax(-1).item()
|
73 |
+
# 检查是否有与AI相关的类别
|
74 |
+
predicted_class = model_info["model"].config.id2label[predicted_class_idx].lower()
|
75 |
+
|
76 |
+
# 简单启发式:检查类别名称是否包含与AI生成相关的关键词
|
77 |
+
ai_keywords = ["artificial", "generated", "synthetic", "fake", "computer"]
|
78 |
+
for keyword in ai_keywords:
|
79 |
+
if keyword in predicted_class:
|
80 |
+
return float(probabilities[0][predicted_class_idx].item())
|
81 |
+
|
82 |
+
# 如果没有明确的AI类别,返回中等概率
|
83 |
+
return 0.5
|
84 |
+
|
85 |
+
elif "vit" in model_name:
|
86 |
+
# Vision Transformer模型
|
87 |
+
predicted_class_idx = outputs.logits.argmax(-1).item()
|
88 |
+
# 同样检查类别名称
|
89 |
+
predicted_class = model_info["model"].config.id2label[predicted_class_idx].lower()
|
90 |
+
|
91 |
+
# 简单启发式:检查类别名称是否包含与AI生成相关的关键词
|
92 |
+
ai_keywords = ["artificial", "generated", "synthetic", "fake", "computer"]
|
93 |
+
for keyword in ai_keywords:
|
94 |
+
if keyword in predicted_class:
|
95 |
+
return float(probabilities[0][predicted_class_idx].item())
|
96 |
+
|
97 |
+
# 如果没有明确的AI类别,返回中等概率
|
98 |
+
return 0.5
|
99 |
+
|
100 |
+
# 默认处理
|
101 |
+
predicted_class_idx = outputs.logits.argmax(-1).item()
|
102 |
+
predicted_class = model_info["model"].config.id2label[predicted_class_idx].lower()
|
103 |
+
|
104 |
+
if "ai" in predicted_class or "generated" in predicted_class or "fake" in predicted_class:
|
105 |
+
return float(probabilities[0][predicted_class_idx].item())
|
106 |
+
else:
|
107 |
+
return 1 - float(probabilities[0][predicted_class_idx].item())
|
108 |
+
|
109 |
+
return ai_probability
|
110 |
+
|
111 |
def analyze_image_features(image):
|
112 |
+
"""分析图像特征"""
|
113 |
# 转换为OpenCV格式
|
114 |
img_array = np.array(image)
|
115 |
if len(img_array.shape) == 3 and img_array.shape[2] == 3:
|
|
|
129 |
features["avg_red"] = float(np.mean(img_array[:,:,0]))
|
130 |
features["avg_green"] = float(np.mean(img_array[:,:,1]))
|
131 |
features["avg_blue"] = float(np.mean(img_array[:,:,2]))
|
132 |
+
|
133 |
+
# 颜色标准差 - 用于检测颜色分布是否自然
|
134 |
+
features["color_std"] = float(np.std([
|
135 |
+
features["avg_red"],
|
136 |
+
features["avg_green"],
|
137 |
+
features["avg_blue"]
|
138 |
+
]))
|
139 |
|
140 |
# 边缘一致性分析
|
141 |
edges = cv2.Canny(img_cv, 100, 200)
|
|
|
154 |
# 计算GLCM属性
|
155 |
features["texture_contrast"] = float(np.mean(graycoprops(glcm, 'contrast')[0]))
|
156 |
features["texture_homogeneity"] = float(np.mean(graycoprops(glcm, 'homogeneity')[0]))
|
157 |
+
features["texture_correlation"] = float(np.mean(graycoprops(glcm, 'correlation')[0]))
|
158 |
+
features["texture_energy"] = float(np.mean(graycoprops(glcm, 'energy')[0]))
|
159 |
|
160 |
# 噪声分析
|
161 |
if len(img_array.shape) == 3:
|
162 |
blurred = cv2.GaussianBlur(img_cv, (5, 5), 0)
|
163 |
noise = cv2.absdiff(img_cv, blurred)
|
164 |
features["noise_level"] = float(np.mean(noise))
|
165 |
+
|
166 |
+
# 噪声分布 - 用于检测噪声是否自然
|
167 |
+
features["noise_std"] = float(np.std(noise))
|
168 |
+
|
169 |
+
# 对称性分析 - AI生成图像通常有更高的对称性
|
170 |
+
if img_cv.shape[1] % 2 == 0: # 确保宽度是偶数
|
171 |
+
left_half = img_cv[:, :img_cv.shape[1]//2]
|
172 |
+
right_half = cv2.flip(img_cv[:, img_cv.shape[1]//2:], 1)
|
173 |
+
if left_half.shape == right_half.shape:
|
174 |
+
h_symmetry = 1 - float(np.mean(cv2.absdiff(left_half, right_half)) / 255)
|
175 |
+
features["horizontal_symmetry"] = h_symmetry
|
176 |
+
|
177 |
+
if img_cv.shape[0] % 2 == 0: # 确保高度是偶数
|
178 |
+
top_half = img_cv[:img_cv.shape[0]//2, :]
|
179 |
+
bottom_half = cv2.flip(img_cv[img_cv.shape[0]//2:, :], 0)
|
180 |
+
if top_half.shape == bottom_half.shape:
|
181 |
+
v_symmetry = 1 - float(np.mean(cv2.absdiff(top_half, bottom_half)) / 255)
|
182 |
+
features["vertical_symmetry"] = v_symmetry
|
183 |
+
|
184 |
+
# 频率域分析 - 检测不自然的频率分布
|
185 |
+
if len(img_array.shape) == 3:
|
186 |
+
gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)
|
187 |
+
f_transform = np.fft.fft2(gray)
|
188 |
+
f_shift = np.fft.fftshift(f_transform)
|
189 |
+
magnitude = np.log(np.abs(f_shift) + 1)
|
190 |
+
|
191 |
+
# 计算高频和低频成分的比例
|
192 |
+
h, w = magnitude.shape
|
193 |
+
center_h, center_w = h // 2, w // 2
|
194 |
+
|
195 |
+
# 低频区域 (中心区域)
|
196 |
+
low_freq_region = magnitude[center_h-h//8:center_h+h//8, center_w-w//8:center_w+w//8]
|
197 |
+
low_freq_mean = np.mean(low_freq_region)
|
198 |
+
|
199 |
+
# 高频区域 (边缘区域)
|
200 |
+
high_freq_mean = np.mean(magnitude) - low_freq_mean
|
201 |
+
|
202 |
+
features["freq_ratio"] = float(high_freq_mean / max(low_freq_mean, 0.001))
|
203 |
|
204 |
return features
|
205 |
|
206 |
+
def detect_photoshop_signs(image_features):
|
207 |
+
"""检测图像中的PS痕迹"""
|
208 |
+
ps_score = 0
|
209 |
+
ps_signs = []
|
210 |
+
|
211 |
+
# 检查皮肤质感
|
212 |
+
if "texture_homogeneity" in image_features:
|
213 |
+
if image_features["texture_homogeneity"] > 0.4:
|
214 |
+
ps_score += 0.2
|
215 |
+
ps_signs.append("皮肤质感过于均匀")
|
216 |
+
elif image_features["texture_homogeneity"] > 0.3:
|
217 |
+
ps_score += 0.1
|
218 |
+
ps_signs.append("皮肤质感较为均匀")
|
219 |
+
|
220 |
+
# 检查边缘不自然
|
221 |
+
if "edge_density" in image_features:
|
222 |
+
if image_features["edge_density"] < 0.01:
|
223 |
+
ps_score += 0.2
|
224 |
+
ps_signs.append("边缘过于平滑")
|
225 |
+
elif image_features["edge_density"] < 0.03:
|
226 |
+
ps_score += 0.1
|
227 |
+
ps_signs.append("边缘较为平滑")
|
228 |
+
|
229 |
+
# 检查颜色不自然
|
230 |
+
if "color_std" in image_features:
|
231 |
+
if image_features["color_std"] > 50:
|
232 |
+
ps_score += 0.2
|
233 |
+
ps_signs.append("颜色分布极不自然")
|
234 |
+
elif image_features["color_std"] > 30:
|
235 |
+
ps_score += 0.1
|
236 |
+
ps_signs.append("颜色分布略不自然")
|
237 |
+
|
238 |
+
# 检查噪点不一致
|
239 |
+
if "noise_level" in image_features and "noise_std" in image_features:
|
240 |
+
noise_ratio = image_features["noise_std"] / max(image_features["noise_level"], 0.001)
|
241 |
+
if noise_ratio < 0.5:
|
242 |
+
ps_score += 0.2
|
243 |
+
ps_signs.append("噪点分布不自然")
|
244 |
+
elif noise_ratio < 0.7:
|
245 |
+
ps_score += 0.1
|
246 |
+
ps_signs.append("噪点分布略不自然")
|
247 |
+
|
248 |
+
# 检查频率分布不自然
|
249 |
+
if "freq_ratio" in image_features:
|
250 |
+
if image_features["freq_ratio"] < 0.2:
|
251 |
+
ps_score += 0.2
|
252 |
+
ps_signs.append("频率分布不自然,可能有过度模糊处理")
|
253 |
+
elif image_features["freq_ratio"] > 2.0:
|
254 |
+
ps_score += 0.2
|
255 |
+
ps_signs.append("频率分布不自然,可能有过度锐化处理")
|
256 |
+
|
257 |
+
# 检查对称性不自然
|
258 |
+
if "horizontal_symmetry" in image_features and "vertical_symmetry" in image_features:
|
259 |
+
avg_symmetry = (image_features["horizontal_symmetry"] + image_features["vertical_symmetry"]) / 2
|
260 |
+
if avg_symmetry > 0.8:
|
261 |
+
ps_score += 0.2
|
262 |
+
ps_signs.append("图像对称性过高,可能经过对称处理")
|
263 |
+
|
264 |
+
return min(ps_score, 1.0), ps_signs
|
265 |
+
|
266 |
+
def get_detailed_analysis(ai_probability, ps_score, ps_signs, valid_models_count):
|
267 |
+
"""提供更详细的分析结果"""
|
268 |
+
|
269 |
+
# 根据有效模型数量调整置信度描述
|
270 |
+
confidence_prefix = ""
|
271 |
+
if valid_models_count >= 3:
|
272 |
+
confidence_prefix = "极高置信度:"
|
273 |
+
elif valid_models_count == 2:
|
274 |
+
confidence_prefix = "高置信度:"
|
275 |
+
elif valid_models_count == 1:
|
276 |
+
confidence_prefix = "中等置信度:"
|
277 |
+
|
278 |
+
if ai_probability > 0.8:
|
279 |
+
category = confidence_prefix + "高概率AI生成"
|
280 |
+
description = "图像几乎可以确定是由AI完全生成,几乎没有真人照片的特征。"
|
281 |
+
elif ai_probability > 0.6:
|
282 |
+
if ps_score > 0.5:
|
283 |
+
category = confidence_prefix + "中等概率AI生成,高概率PS修图"
|
284 |
+
description = "图像可能是真人照片经过大量后期处理,或是AI生成后经过修饰的图像。"
|
285 |
+
else:
|
286 |
+
category = confidence_prefix + "中等概率AI生成"
|
287 |
+
description = "图像有较多AI生成的特征,但也保留了一些真实照片的特点。"
|
288 |
+
elif ai_probability > 0.4:
|
289 |
+
if ps_score > 0.5:
|
290 |
+
category = confidence_prefix + "低概率AI生成,高概率PS修图"
|
291 |
+
description = "图像更可能是真人照片经过大量后期处理,PS痕迹明显。"
|
292 |
+
else:
|
293 |
+
category = confidence_prefix + "低概率AI生成"
|
294 |
+
description = "图像更可能是真人照片,但有一些AI生成或修饰的特征。"
|
295 |
+
else:
|
296 |
+
if ps_score > 0.6:
|
297 |
+
category = confidence_prefix + "真人照片,重度PS修图"
|
298 |
+
description = "图像基本是真人照片,但经过了大量后期处理,修饰痕迹明显。"
|
299 |
+
elif ps_score > 0.3:
|
300 |
+
category = confidence_prefix + "真人照片,中度PS修图"
|
301 |
+
description = "图像是真人照片,有明显的后期处理痕迹。"
|
302 |
+
elif ps_score > 0.1:
|
303 |
+
category = confidence_prefix + "真人照片,轻度PS修图"
|
304 |
+
description = "图像是真人照片,有少量后期处理。"
|
305 |
+
else:
|
306 |
+
category = confidence_prefix + "高概率真人照片,几乎无修图"
|
307 |
+
description = "图像几乎可以确定是未经大量处理的真人照片。"
|
308 |
+
|
309 |
+
# 添加具体的PS痕迹描述
|
310 |
+
if ps_signs:
|
311 |
+
ps_details = "检测到的修图痕迹:" + "、".join(ps_signs)
|
312 |
+
else:
|
313 |
+
ps_details = "未检测到明显的修图痕迹。"
|
314 |
+
|
315 |
+
return category, description, ps_details
|
316 |
+
|
317 |
def detect_ai_image(image):
|
318 |
+
"""主检测函数"""
|
319 |
if image is None:
|
320 |
return {"error": "未提供图像"}
|
321 |
|
|
|
332 |
with torch.no_grad():
|
333 |
outputs = model_info["model"](**inputs)
|
334 |
|
|
|
|
|
|
|
|
|
335 |
# 获取概率
|
336 |
+
probabilities = torch.nn.functional.softmax(outputs.logits, dim=-1)
|
337 |
|
338 |
+
# 使用适配器处理不同模型的输出
|
339 |
+
ai_probability = process_model_output(model_info, outputs, probabilities)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
340 |
|
341 |
# 添加到结果
|
342 |
+
predicted_class_idx = outputs.logits.argmax(-1).item()
|
343 |
results[key] = {
|
344 |
"model_name": model_info["name"],
|
345 |
"ai_probability": ai_probability,
|
|
|
370 |
|
371 |
# 低边缘密度通常表示AI生成
|
372 |
if image_features["edge_density"] < 0.01:
|
373 |
+
adjusted_probability += 0.15
|
374 |
|
375 |
# 高纹理均匀性通常表示AI生成
|
376 |
+
if "texture_homogeneity" in image_features and image_features["texture_homogeneity"] > 0.5:
|
377 |
adjusted_probability += 0.1
|
378 |
|
379 |
# 低噪声水平通常表示AI生成
|
380 |
if image_features["noise_level"] < 0.5:
|
381 |
adjusted_probability += 0.1
|
382 |
|
383 |
+
# 高对称性通常表示AI生成
|
384 |
+
if "horizontal_symmetry" in image_features and image_features["horizontal_symmetry"] > 0.8:
|
385 |
+
adjusted_probability += 0.1
|
386 |
+
|
387 |
# 确保概率在0-1范围内
|
388 |
adjusted_probability = min(1.0, max(0.0, adjusted_probability))
|
389 |
|
390 |
+
# 分析PS痕迹
|
391 |
+
ps_score, ps_signs = detect_photoshop_signs(image_features)
|
392 |
+
|
393 |
+
# 获取详细分析
|
394 |
+
category, description, ps_details = get_detailed_analysis(adjusted_probability, ps_score, ps_signs, valid_models)
|
|
|
|
|
395 |
|
396 |
# 构建最终结果
|
397 |
final_result = {
|
398 |
"ai_probability": adjusted_probability,
|
399 |
+
"ps_score": ps_score,
|
400 |
+
"category": category,
|
401 |
+
"description": description,
|
402 |
+
"ps_details": ps_details,
|
403 |
"individual_model_results": results,
|
404 |
"features": image_features
|
405 |
}
|
|
|
412 |
inputs=gr.Image(type="pil"),
|
413 |
outputs=gr.JSON(),
|
414 |
title="增强型AI图像检测API",
|
415 |
+
description="多模型集成检测图像是否由AI生成,同时分析PS修图痕迹",
|
416 |
examples=None,
|
417 |
allow_flagging="never"
|
418 |
)
|