Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -38,10 +38,6 @@ for key in models:
|
|
38 |
models[key]["processor"] = None
|
39 |
models[key]["model"] = None
|
40 |
|
41 |
-
|
42 |
-
## 第二部分:模型输出处理
|
43 |
-
|
44 |
-
|
45 |
def process_model_output(model_info, outputs, probabilities):
|
46 |
"""处理不同模型的输出,统一返回AI生成概率"""
|
47 |
model_name = model_info["name"].lower()
|
@@ -112,10 +108,6 @@ def process_model_output(model_info, outputs, probabilities):
|
|
112 |
|
113 |
return ai_probability
|
114 |
|
115 |
-
|
116 |
-
## 第三部分:图像特征分析
|
117 |
-
|
118 |
-
|
119 |
def analyze_image_features(image):
|
120 |
"""分析图像特征"""
|
121 |
# 转换为OpenCV格式
|
@@ -268,6 +260,14 @@ def analyze_image_features(image):
|
|
268 |
# 如果人脸检测失败,不添加人脸特征
|
269 |
pass
|
270 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
271 |
return features
|
272 |
|
273 |
def analyze_face_symmetry(face):
|
@@ -279,9 +279,122 @@ def analyze_face_symmetry(face):
|
|
279 |
return 1 - float(np.mean(cv2.absdiff(left_half, right_half)) / 255)
|
280 |
return 0.5 # 默认值
|
281 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
282 |
|
283 |
-
|
284 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
285 |
|
286 |
def check_ai_specific_features(image_features):
|
287 |
"""检查AI生成图像的典型特征"""
|
@@ -354,6 +467,25 @@ def check_ai_specific_features(image_features):
|
|
354 |
ai_score += 0.3
|
355 |
ai_signs.append("皮肤质感异常均匀")
|
356 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
357 |
return min(ai_score, 1.0), ai_signs
|
358 |
|
359 |
def detect_beauty_filter_signs(image_features):
|
@@ -418,8 +550,7 @@ def detect_photoshop_signs(image_features):
|
|
418 |
elif image_features["texture_homogeneity"] > 0.3:
|
419 |
ps_score += 0.1
|
420 |
ps_signs.append("皮肤质感较为均匀")
|
421 |
-
|
422 |
-
# 检查边缘不自然
|
423 |
if "edge_density" in image_features:
|
424 |
if image_features["edge_density"] < 0.01:
|
425 |
ps_score += 0.2
|
@@ -458,10 +589,6 @@ def detect_photoshop_signs(image_features):
|
|
458 |
|
459 |
return min(ps_score, 1.0), ps_signs
|
460 |
|
461 |
-
|
462 |
-
## 第五部分:结果分析与分类
|
463 |
-
|
464 |
-
|
465 |
def get_detailed_analysis(ai_probability, ps_score, beauty_score, ps_signs, ai_signs, beauty_signs, valid_models_count):
|
466 |
"""提供更详细的分析结果"""
|
467 |
|
@@ -474,7 +601,7 @@ def get_detailed_analysis(ai_probability, ps_score, beauty_score, ps_signs, ai_s
|
|
474 |
elif valid_models_count == 1:
|
475 |
confidence_prefix = "中等置信度:"
|
476 |
|
477 |
-
#
|
478 |
if ai_probability > 0.7: # 高AI概率
|
479 |
category = confidence_prefix + "高概率AI生成"
|
480 |
description = "图像很可能是由AI完全生成,几乎没有真人照片的特征。"
|
@@ -535,8 +662,6 @@ def get_detailed_analysis(ai_probability, ps_score, beauty_score, ps_signs, ai_s
|
|
535 |
|
536 |
return category, description, ps_details, ai_details, beauty_details
|
537 |
|
538 |
-
|
539 |
-
## 第六部分:主检测函数
|
540 |
def detect_ai_image(image):
|
541 |
"""主检测函数"""
|
542 |
if image is None:
|
@@ -608,6 +733,13 @@ def detect_ai_image(image):
|
|
608 |
elif ai_feature_score > 0.3:
|
609 |
adjusted_probability = max(adjusted_probability, 0.5)
|
610 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
611 |
# 如果美颜分数高但AI特征分数不高,降低AI概率
|
612 |
if beauty_score > 0.6 and ai_feature_score < 0.5:
|
613 |
adjusted_probability = min(adjusted_probability, 0.5)
|
@@ -663,21 +795,15 @@ def detect_ai_image(image):
|
|
663 |
|
664 |
return final_result
|
665 |
|
666 |
-
|
667 |
-
## 第七部分:Gradio界面
|
668 |
-
|
669 |
-
|
670 |
# 创建Gradio界面
|
671 |
iface = gr.Interface(
|
672 |
fn=detect_ai_image,
|
673 |
inputs=gr.Image(type="pil"),
|
674 |
outputs=gr.JSON(),
|
675 |
title="增强型AI图像检测API",
|
676 |
-
description="多模型集成检测图像是否由AI生成,同时分析PS
|
677 |
examples=None,
|
678 |
allow_flagging="never"
|
679 |
)
|
680 |
|
681 |
iface.launch()
|
682 |
-
|
683 |
-
|
|
|
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()
|
|
|
108 |
|
109 |
return ai_probability
|
110 |
|
|
|
|
|
|
|
|
|
111 |
def analyze_image_features(image):
|
112 |
"""分析图像特征"""
|
113 |
# 转换为OpenCV格式
|
|
|
260 |
# 如果人脸检测失败,不添加人脸特征
|
261 |
pass
|
262 |
|
263 |
+
# 分析衣物细节
|
264 |
+
clothing_features = analyze_clothing_details(img_cv)
|
265 |
+
features.update(clothing_features)
|
266 |
+
|
267 |
+
# 分析手部和关节
|
268 |
+
extremity_features = analyze_extremities(img_cv)
|
269 |
+
features.update(extremity_features)
|
270 |
+
|
271 |
return features
|
272 |
|
273 |
def analyze_face_symmetry(face):
|
|
|
279 |
return 1 - float(np.mean(cv2.absdiff(left_half, right_half)) / 255)
|
280 |
return 0.5 # 默认值
|
281 |
|
282 |
+
def analyze_clothing_details(image):
|
283 |
+
"""分析衣物细节的自然度"""
|
284 |
+
features = {}
|
285 |
+
|
286 |
+
try:
|
287 |
+
# 转换为灰度图
|
288 |
+
if len(image.shape) == 3:
|
289 |
+
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
290 |
+
else:
|
291 |
+
gray = image
|
292 |
+
|
293 |
+
# 使用Canny边缘检测
|
294 |
+
edges = cv2.Canny(gray, 50, 150)
|
295 |
+
|
296 |
+
# 使用霍夫变换检测直线
|
297 |
+
lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=50, minLineLength=50, maxLineGap=10)
|
298 |
+
|
299 |
+
if lines is not None:
|
300 |
+
# 计算直线的角度分布
|
301 |
+
angles = []
|
302 |
+
for line in lines:
|
303 |
+
x1, y1, x2, y2 = line[0]
|
304 |
+
if x2 - x1 != 0: # 避免除以零
|
305 |
+
angle = np.arctan((y2 - y1) / (x2 - x1)) * 180 / np.pi
|
306 |
+
angles.append(angle)
|
307 |
+
|
308 |
+
if angles:
|
309 |
+
# 计算角度的标准差 - AI生成的衣物褶皱通常角度分布不自然
|
310 |
+
features["clothing_angle_std"] = float(np.std(angles))
|
311 |
+
|
312 |
+
# 计算角度的直方图 - 检查是否有过多相似角度(AI生成特征)
|
313 |
+
hist, _ = np.histogram(angles, bins=18, range=(-90, 90))
|
314 |
+
max_count = np.max(hist)
|
315 |
+
total_count = np.sum(hist)
|
316 |
+
features["clothing_angle_uniformity"] = float(max_count / max(total_count, 1))
|
317 |
+
|
318 |
+
# 分析纹理的一致性
|
319 |
+
# 将图像分成小块,计算每个块的纹理特征
|
320 |
+
block_size = 32
|
321 |
+
h, w = gray.shape
|
322 |
+
texture_variations = []
|
323 |
+
|
324 |
+
for i in range(0, h-block_size, block_size):
|
325 |
+
for j in range(0, w-block_size, block_size):
|
326 |
+
block = gray[i:i+block_size, j:j+block_size]
|
327 |
+
# 计算局部LBP特征或简单的方差
|
328 |
+
texture_variations.append(np.var(block))
|
329 |
+
|
330 |
+
if texture_variations:
|
331 |
+
# 计算纹理变化的标准差 - 真实衣物纹理变化更自然
|
332 |
+
features["clothing_texture_std"] = float(np.std(texture_variations))
|
333 |
+
|
334 |
+
# 计算纹理变化的均值 - AI生成的衣物纹理通常变化较小
|
335 |
+
features["clothing_texture_mean"] = float(np.mean(texture_variations))
|
336 |
+
except:
|
337 |
+
# 如果分析失败,不添加衣物特征
|
338 |
+
pass
|
339 |
+
|
340 |
+
return features
|
341 |
|
342 |
+
def analyze_extremities(image):
|
343 |
+
"""分析手指、脚趾等末端细节"""
|
344 |
+
features = {}
|
345 |
+
|
346 |
+
try:
|
347 |
+
# 转换为灰度图
|
348 |
+
if len(image.shape) == 3:
|
349 |
+
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
350 |
+
else:
|
351 |
+
gray = image
|
352 |
+
|
353 |
+
# 使用形态学操作提取可能的手部区域
|
354 |
+
_, thresh = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY)
|
355 |
+
kernel = np.ones((5,5), np.uint8)
|
356 |
+
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
|
357 |
+
|
358 |
+
# 寻找轮廓
|
359 |
+
contours, _ = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
360 |
+
|
361 |
+
# 分析轮廓的复杂度
|
362 |
+
if contours:
|
363 |
+
# 计算轮廓的周长与面积比 - 手指等细节会增加这个比值
|
364 |
+
perimeter_area_ratios = []
|
365 |
+
for contour in contours:
|
366 |
+
area = cv2.contourArea(contour)
|
367 |
+
if area > 100: # 忽略太小的轮廓
|
368 |
+
perimeter = cv2.arcLength(contour, True)
|
369 |
+
ratio = perimeter / max(area, 1)
|
370 |
+
perimeter_area_ratios.append(ratio)
|
371 |
+
|
372 |
+
if perimeter_area_ratios:
|
373 |
+
features["extremity_perimeter_area_ratio"] = float(np.mean(perimeter_area_ratios))
|
374 |
+
|
375 |
+
# 计算凸包缺陷 - 手指之间的间隙���产生凸包缺陷
|
376 |
+
defect_depths = []
|
377 |
+
for contour in contours:
|
378 |
+
if len(contour) > 5: # 需要足够多的点来计算凸包
|
379 |
+
hull = cv2.convexHull(contour, returnPoints=False)
|
380 |
+
if len(hull) > 3: # 需要至少4个点来计算凸包缺陷
|
381 |
+
try:
|
382 |
+
defects = cv2.convexityDefects(contour, hull)
|
383 |
+
if defects is not None:
|
384 |
+
for i in range(defects.shape[0]):
|
385 |
+
_, _, _, depth = defects[i, 0]
|
386 |
+
defect_depths.append(depth)
|
387 |
+
except:
|
388 |
+
pass
|
389 |
+
|
390 |
+
if defect_depths:
|
391 |
+
features["extremity_defect_depth_mean"] = float(np.mean(defect_depths))
|
392 |
+
features["extremity_defect_depth_std"] = float(np.std(defect_depths))
|
393 |
+
except:
|
394 |
+
# 如果分析失败,不添加末端特征
|
395 |
+
pass
|
396 |
+
|
397 |
+
return features
|
398 |
|
399 |
def check_ai_specific_features(image_features):
|
400 |
"""检查AI生成图像的典型特征"""
|
|
|
467 |
ai_score += 0.3
|
468 |
ai_signs.append("皮肤质感异常均匀")
|
469 |
|
470 |
+
# 检查衣物特征 - AI生成的衣物通常有特定问题
|
471 |
+
if "clothing_angle_uniformity" in image_features and image_features["clothing_angle_uniformity"] > 0.3:
|
472 |
+
ai_score += 0.3
|
473 |
+
ai_signs.append("衣物褶皱角度分布不自然")
|
474 |
+
|
475 |
+
if "clothing_texture_std" in image_features and image_features["clothing_texture_std"] < 100:
|
476 |
+
ai_score += 0.2
|
477 |
+
ai_signs.append("衣物纹理变化异常均匀")
|
478 |
+
|
479 |
+
# 检查手部特征 - AI生成的手部通常有特定问题
|
480 |
+
if "extremity_perimeter_area_ratio" in image_features:
|
481 |
+
if image_features["extremity_perimeter_area_ratio"] < 0.05:
|
482 |
+
ai_score += 0.3
|
483 |
+
ai_signs.append("手部/末端轮廓异常平滑")
|
484 |
+
|
485 |
+
if "extremity_defect_depth_std" in image_features and image_features["extremity_defect_depth_std"] < 10:
|
486 |
+
ai_score += 0.2
|
487 |
+
ai_signs.append("手指间隙异常均匀")
|
488 |
+
|
489 |
return min(ai_score, 1.0), ai_signs
|
490 |
|
491 |
def detect_beauty_filter_signs(image_features):
|
|
|
550 |
elif image_features["texture_homogeneity"] > 0.3:
|
551 |
ps_score += 0.1
|
552 |
ps_signs.append("皮肤质感较为均匀")
|
553 |
+
# 检查边缘不自然
|
|
|
554 |
if "edge_density" in image_features:
|
555 |
if image_features["edge_density"] < 0.01:
|
556 |
ps_score += 0.2
|
|
|
589 |
|
590 |
return min(ps_score, 1.0), ps_signs
|
591 |
|
|
|
|
|
|
|
|
|
592 |
def get_detailed_analysis(ai_probability, ps_score, beauty_score, ps_signs, ai_signs, beauty_signs, valid_models_count):
|
593 |
"""提供更详细的分析结果"""
|
594 |
|
|
|
601 |
elif valid_models_count == 1:
|
602 |
confidence_prefix = "中等置信度:"
|
603 |
|
604 |
+
# 调整后的阈值判断,考虑美颜因素和衣物细节
|
605 |
if ai_probability > 0.7: # 高AI概率
|
606 |
category = confidence_prefix + "高概率AI生成"
|
607 |
description = "图像很可能是由AI完全生成,几乎没有真人照片的特征。"
|
|
|
662 |
|
663 |
return category, description, ps_details, ai_details, beauty_details
|
664 |
|
|
|
|
|
665 |
def detect_ai_image(image):
|
666 |
"""主检测函数"""
|
667 |
if image is None:
|
|
|
733 |
elif ai_feature_score > 0.3:
|
734 |
adjusted_probability = max(adjusted_probability, 0.5)
|
735 |
|
736 |
+
# 如果检测到衣物或手部异常,大幅提高AI概率
|
737 |
+
if "clothing_angle_uniformity" in image_features and image_features["clothing_angle_uniformity"] > 0.3:
|
738 |
+
adjusted_probability = max(adjusted_probability, 0.7)
|
739 |
+
|
740 |
+
if "extremity_perimeter_area_ratio" in image_features and image_features["extremity_perimeter_area_ratio"] < 0.05:
|
741 |
+
adjusted_probability = max(adjusted_probability, 0.7)
|
742 |
+
|
743 |
# 如果美颜分数高但AI特征分数不高,降低AI概率
|
744 |
if beauty_score > 0.6 and ai_feature_score < 0.5:
|
745 |
adjusted_probability = min(adjusted_probability, 0.5)
|
|
|
795 |
|
796 |
return final_result
|
797 |
|
|
|
|
|
|
|
|
|
798 |
# 创建Gradio界面
|
799 |
iface = gr.Interface(
|
800 |
fn=detect_ai_image,
|
801 |
inputs=gr.Image(type="pil"),
|
802 |
outputs=gr.JSON(),
|
803 |
title="增强型AI图像检测API",
|
804 |
+
description="多模型集成检测图像是否由AI生成,同时分析PS修图和美颜痕迹,特别关注衣物和手部等细节问题",
|
805 |
examples=None,
|
806 |
allow_flagging="never"
|
807 |
)
|
808 |
|
809 |
iface.launch()
|
|
|
|