Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -125,7 +125,8 @@ class EnhancedModelManager:
|
|
125 |
"model_name": model_info["name"],
|
126 |
"ai_probability": ai_probability,
|
127 |
"predicted_class": model_info["model"].config.id2label[predicted_class_idx],
|
128 |
-
"confidence": float(probabilities[0][predicted_class_idx].item())
|
|
|
129 |
}
|
130 |
|
131 |
except Exception as e:
|
@@ -272,7 +273,6 @@ class EnhancedFeatureExtractor:
|
|
272 |
self._extract_advanced_features(img_cv, features, image_id)
|
273 |
|
274 |
return features
|
275 |
-
|
276 |
def _extract_color_features(self, img_array, features):
|
277 |
"""Extract color-related features"""
|
278 |
if len(img_array.shape) == 3:
|
@@ -367,7 +367,6 @@ class EnhancedFeatureExtractor:
|
|
367 |
# 边缘方向一致性 - AI生成图像通常边缘方向过于一致
|
368 |
edge_dir_normalized = edge_dir_hist / np.sum(edge_dir_hist)
|
369 |
features["edge_direction_consistency"] = float(np.max(edge_dir_normalized))
|
370 |
-
|
371 |
def _extract_texture_features(self, img_cv, features, image_id):
|
372 |
"""Extract texture-related features"""
|
373 |
# 获取灰度图
|
@@ -476,7 +475,6 @@ class EnhancedFeatureExtractor:
|
|
476 |
corr_gb = np.corrcoef(noise_g, noise_b)[0,1]
|
477 |
|
478 |
features["noise_channel_correlation"] = float((abs(corr_rg) + abs(corr_rb) + abs(corr_gb)) / 3)
|
479 |
-
|
480 |
def _extract_symmetry_features(self, img_cv, features):
|
481 |
"""Extract symmetry-related features"""
|
482 |
h, w = img_cv.shape[:2]
|
@@ -604,7 +602,6 @@ class EnhancedFeatureExtractor:
|
|
604 |
features["freq_peak_prominence"] = float(np.mean(radial_mean[peaks]))
|
605 |
except:
|
606 |
pass
|
607 |
-
|
608 |
def _extract_advanced_features(self, img_cv, features, image_id):
|
609 |
"""Extract advanced features for AI detection"""
|
610 |
# 获取灰度图
|
@@ -674,99 +671,99 @@ class EnhancedFeatureExtractor:
|
|
674 |
except:
|
675 |
pass
|
676 |
#############################################
|
677 |
-
# 特征分析与决策逻辑部分
|
678 |
#############################################
|
679 |
|
680 |
-
# 基于图像类型的特征阈值
|
681 |
OPTIMIZED_THRESHOLDS = {
|
682 |
"lbp_entropy": {
|
683 |
-
"default":
|
684 |
-
"portrait": 1.
|
685 |
-
"landscape":
|
686 |
},
|
687 |
"freq_anisotropy": {
|
688 |
-
"default": 0.
|
689 |
-
"portrait": 0.
|
690 |
-
"landscape": 0.
|
691 |
},
|
692 |
"texture_correlation": {
|
693 |
-
"default": 0.
|
694 |
-
"portrait": 0.
|
695 |
-
"landscape": 0.
|
696 |
},
|
697 |
"horizontal_symmetry": {
|
698 |
-
"default": 0.
|
699 |
-
"portrait": 0.
|
700 |
-
"landscape": 0.
|
701 |
},
|
702 |
"vertical_symmetry": {
|
703 |
-
"default": 0.
|
704 |
-
"portrait": 0.
|
705 |
-
"landscape": 0.
|
706 |
},
|
707 |
"noise_spatial_std": {
|
708 |
-
"default": 0.
|
709 |
-
"portrait": 0.
|
710 |
-
"landscape": 0.
|
711 |
},
|
712 |
"freq_ratio": {
|
713 |
-
"default": 0.
|
714 |
-
"portrait": 0.05,
|
715 |
-
"landscape": 0.
|
716 |
},
|
717 |
"noise_spectrum_std": {
|
718 |
-
"default":
|
719 |
-
"portrait":
|
720 |
-
"landscape":
|
721 |
},
|
722 |
"color_entropy": {
|
723 |
-
"default": 3.
|
724 |
-
"portrait": 3.
|
725 |
-
"landscape":
|
726 |
},
|
727 |
"lbp_uniformity": {
|
728 |
-
"default": 0.
|
729 |
-
"portrait": 0.
|
730 |
-
"landscape": 0.
|
731 |
},
|
732 |
"noise_channel_correlation": {
|
733 |
-
"default": 0.
|
734 |
-
"portrait": 0.
|
735 |
-
"landscape": 0.
|
736 |
},
|
737 |
"wavelet_h_entropy": {
|
738 |
-
"default":
|
739 |
-
"portrait":
|
740 |
-
"landscape":
|
741 |
},
|
742 |
"dct_entropy": {
|
743 |
-
"default":
|
744 |
-
"portrait":
|
745 |
-
"landscape":
|
746 |
},
|
747 |
"naturalness_index": {
|
748 |
-
"default":
|
749 |
-
"portrait":
|
750 |
-
"landscape":
|
751 |
}
|
752 |
}
|
753 |
|
754 |
-
# 特征重要性权重
|
755 |
FEATURE_IMPORTANCE = {
|
756 |
-
"lbp_entropy": 0.
|
757 |
-
"freq_anisotropy": 0.
|
758 |
-
"texture_correlation": 0.
|
759 |
-
"horizontal_symmetry": 0.
|
760 |
-
"vertical_symmetry": 0.
|
761 |
-
"noise_spatial_std": 0.
|
762 |
-
"freq_ratio": 0.
|
763 |
-
"noise_spectrum_std": 0.
|
764 |
-
"color_entropy": 0.
|
765 |
-
"lbp_uniformity": 0.
|
766 |
-
"noise_channel_correlation": 0.
|
767 |
-
"wavelet_h_entropy": 0.
|
768 |
-
"dct_entropy": 0.
|
769 |
-
"naturalness_index": 0.
|
770 |
}
|
771 |
|
772 |
def get_threshold(feature_name, image_type="default"):
|
@@ -778,7 +775,7 @@ def get_threshold(feature_name, image_type="default"):
|
|
778 |
OPTIMIZED_THRESHOLDS[feature_name]["default"])
|
779 |
|
780 |
def check_ai_specific_features(image_features):
|
781 |
-
"""Enhanced check for AI-generated image features"""
|
782 |
ai_score = 0
|
783 |
ai_signs = []
|
784 |
|
@@ -804,21 +801,32 @@ def check_ai_specific_features(image_features):
|
|
804 |
"freq_ratio", "noise_spectrum_std", "color_entropy",
|
805 |
"wavelet_h_entropy", "dct_entropy", "naturalness_index"]:
|
806 |
if value < threshold:
|
807 |
-
|
808 |
-
|
|
|
809 |
ai_signs.append(f"{feature_name} 异常低 ({value:.2f})")
|
810 |
|
811 |
# 高值表示AI生成的特征
|
812 |
elif feature_name in ["texture_correlation", "horizontal_symmetry", "vertical_symmetry",
|
813 |
"lbp_uniformity", "noise_channel_correlation"]:
|
814 |
if value > threshold:
|
815 |
-
|
816 |
-
|
|
|
817 |
ai_signs.append(f"{feature_name} 异常高 ({value:.2f})")
|
818 |
|
819 |
# 累加加权分数
|
820 |
ai_score += feature_score * importance
|
821 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
822 |
# 计算检测到多少关键特征
|
823 |
critical_count = len(ai_signs)
|
824 |
if critical_count >= 5:
|
@@ -827,7 +835,6 @@ def check_ai_specific_features(image_features):
|
|
827 |
ai_score = max(ai_score, 0.6) # 更保守,从0.7改为0.6
|
828 |
|
829 |
return min(ai_score, 1.0), ai_signs
|
830 |
-
|
831 |
def detect_beauty_filter_signs(image_features):
|
832 |
"""Detect beauty filter traces"""
|
833 |
beauty_score = 0
|
@@ -898,6 +905,27 @@ def detect_photoshop_signs(image_features):
|
|
898 |
|
899 |
return min(ps_score, 1.0), ps_signs
|
900 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
901 |
def calculate_model_consistency(model_results):
|
902 |
"""Calculate the consistency between model predictions"""
|
903 |
if not model_results:
|
@@ -917,46 +945,33 @@ def calculate_model_consistency(model_results):
|
|
917 |
consistency = max(0.0, 1.0 - variance * 5) # 缩放以获得合理的一致性分数
|
918 |
|
919 |
return consistency
|
920 |
-
|
921 |
def get_detailed_analysis(ai_probability, ps_score, beauty_score, ps_signs, ai_signs, beauty_signs,
|
922 |
valid_models_count, ai_feature_score, model_consistency):
|
923 |
-
"""Provide detailed analysis with
|
924 |
|
925 |
-
#
|
926 |
-
if
|
927 |
-
confidence_prefix = "
|
928 |
-
|
|
|
929 |
confidence_prefix = "高置信度:"
|
930 |
-
|
|
|
931 |
confidence_prefix = "中等置信度:"
|
|
|
932 |
else:
|
933 |
confidence_prefix = "低置信度:"
|
|
|
934 |
|
935 |
# 计算编辑分数(在所有路径中都需要)
|
936 |
combined_edit_score = max(ps_score, beauty_score)
|
937 |
|
938 |
-
#
|
939 |
-
if abs(ai_probability - ai_feature_score) > 0.4:
|
940 |
-
confidence_prefix = "低置信度:"
|
941 |
-
explanation = "(模型预测和特征分析结果存在较大差异)"
|
942 |
-
else:
|
943 |
-
explanation = ""
|
944 |
-
|
945 |
-
# 处理特征与模型判断不一致的情况
|
946 |
-
if ai_feature_score > 0.8 and ai_probability > 0.4: # 添加条件
|
947 |
-
ai_probability = max(ai_probability, 0.7) # 更保守,从0.8改为0.7
|
948 |
-
category = confidence_prefix + "AI生成图像" + explanation
|
949 |
-
description = "图像很可能是由AI完全生成,几乎没有真人照片的特征。"
|
950 |
-
main_category = "AI生成"
|
951 |
-
elif ai_feature_score > 0.6 and ai_probability > 0.3: # 添加条件
|
952 |
-
ai_probability = max(ai_probability, 0.6) # 更保守,从0.7改为0.6
|
953 |
-
|
954 |
-
# 第一级分类:AI vs 真实
|
955 |
if ai_probability > 0.6:
|
956 |
category = confidence_prefix + "AI生成图像" + explanation
|
957 |
description = "图像很可能是由AI完全生成,几乎没有真人照片的特征。"
|
958 |
main_category = "AI生成"
|
959 |
-
|
960 |
# 第二级分类:素人 vs 修图
|
961 |
if combined_edit_score > 0.5:
|
962 |
category = confidence_prefix + "真人照片,修图痕迹明显" + explanation
|
@@ -966,15 +981,14 @@ def get_detailed_analysis(ai_probability, ps_score, beauty_score, ps_signs, ai_s
|
|
966 |
category = confidence_prefix + "真实素人照片" + explanation
|
967 |
description = "图像很可能是未经大量处理的真人照片,保留了自然的细节和特征。"
|
968 |
main_category = "真人照片-素人"
|
969 |
-
|
970 |
-
|
971 |
-
|
972 |
-
category = "无法确定" + explanation
|
973 |
description = "系统无法确定该图像是AI生成还是真实照片。模型预测和特征分析结果不一致,需要人工判断。"
|
974 |
main_category = "无法确定"
|
975 |
|
976 |
# 处理边界情况 - AI生成与高度修图
|
977 |
-
|
978 |
category = confidence_prefix + "真人照片,修图痕迹明显(也可能是AI生成)" + explanation
|
979 |
description = "图像可能是真人照片经过大量后期处理,也可能是AI生成图像。由于现代AI技术与高度修图效果相似,难以完全区分。"
|
980 |
main_category = "真人照片-修图明显"
|
@@ -985,8 +999,9 @@ def get_detailed_analysis(ai_probability, ps_score, beauty_score, ps_signs, ai_s
|
|
985 |
beauty_details = "检测到的美颜特征:" + "、".join(beauty_signs) if beauty_signs else "未检测到明显的美颜特征。"
|
986 |
|
987 |
return category, description, ps_details, ai_details, beauty_details, main_category
|
|
|
988 |
def detect_ai_image(image):
|
989 |
-
"""Enhanced main detection function with
|
990 |
if image is None:
|
991 |
return {"error": "未提供图像"}
|
992 |
|
@@ -1020,6 +1035,9 @@ def detect_ai_image(image):
|
|
1020 |
model_result = model_manager.get_model_prediction(key, image)
|
1021 |
|
1022 |
if model_result and "error" not in model_result:
|
|
|
|
|
|
|
1023 |
results[key] = model_result
|
1024 |
weighted_ai_probability += model_result["ai_probability"] * model_info["weight"]
|
1025 |
valid_models += 1
|
@@ -1042,51 +1060,29 @@ def detect_ai_image(image):
|
|
1042 |
ps_score, ps_signs = detect_photoshop_signs(image_features)
|
1043 |
beauty_score, beauty_signs = detect_beauty_filter_signs(image_features)
|
1044 |
|
1045 |
-
#
|
1046 |
-
|
1047 |
|
1048 |
-
#
|
1049 |
-
|
1050 |
-
# 模型一致性高,增加模型预测的权重
|
1051 |
-
if ai_feature_score > 0.8 and final_ai_probability > 0.4:
|
1052 |
-
adjusted_probability = 0.7 * final_ai_probability + 0.3 * ai_feature_score
|
1053 |
-
elif ai_feature_score > 0.6 and final_ai_probability > 0.3:
|
1054 |
-
adjusted_probability = 0.6 * final_ai_probability + 0.4 * ai_feature_score
|
1055 |
-
else:
|
1056 |
-
adjusted_probability = 0.8 * final_ai_probability + 0.2 * ai_feature_score
|
1057 |
-
else:
|
1058 |
-
# 模型一致性低,增加特征分析的权重
|
1059 |
-
if ai_feature_score > 0.8 and final_ai_probability > 0.4:
|
1060 |
-
adjusted_probability = 0.4 * final_ai_probability + 0.6 * ai_feature_score
|
1061 |
-
elif ai_feature_score > 0.6 and final_ai_probability > 0.3:
|
1062 |
-
adjusted_probability = 0.5 * final_ai_probability + 0.5 * ai_feature_score
|
1063 |
-
else:
|
1064 |
-
adjusted_probability = 0.6 * final_ai_probability + 0.4 * ai_feature_score
|
1065 |
|
1066 |
-
#
|
1067 |
-
|
1068 |
-
|
1069 |
-
|
1070 |
-
|
1071 |
-
|
1072 |
-
|
1073 |
-
|
1074 |
-
|
1075 |
-
|
1076 |
-
if "freq_anisotropy" in image_features:
|
1077 |
-
threshold = get_threshold("freq_anisotropy", image_features.get("image_type", "default"))
|
1078 |
-
if image_features["freq_anisotropy"] < threshold:
|
1079 |
-
key_ai_features_count += 1
|
1080 |
-
|
1081 |
-
# 小波熵
|
1082 |
-
if "wavelet_h_entropy" in image_features:
|
1083 |
-
threshold = get_threshold("wavelet_h_entropy", image_features.get("image_type", "default"))
|
1084 |
-
if image_features["wavelet_h_entropy"] < threshold:
|
1085 |
-
key_ai_features_count += 1
|
1086 |
|
1087 |
-
#
|
1088 |
-
if
|
1089 |
-
|
|
|
|
|
|
|
1090 |
|
1091 |
# 确保概率在有效范围内
|
1092 |
adjusted_probability = min(1.0, max(0.0, adjusted_probability))
|
@@ -1094,7 +1090,7 @@ def detect_ai_image(image):
|
|
1094 |
# 获取详细分析
|
1095 |
category, description, ps_details, ai_details, beauty_details, main_category = get_detailed_analysis(
|
1096 |
adjusted_probability, ps_score, beauty_score, ps_signs, ai_signs, beauty_signs,
|
1097 |
-
valid_models,
|
1098 |
)
|
1099 |
|
1100 |
# 构建最终结果
|
@@ -1106,7 +1102,8 @@ def detect_ai_image(image):
|
|
1106 |
"original_ai_probability": final_ai_probability,
|
1107 |
"ps_score": ps_score,
|
1108 |
"beauty_score": beauty_score,
|
1109 |
-
"ai_feature_score":
|
|
|
1110 |
"model_consistency": model_consistency,
|
1111 |
"category": category,
|
1112 |
"main_category": main_category,
|
@@ -1130,7 +1127,6 @@ def detect_ai_image(image):
|
|
1130 |
# 返回两个值:JSON结果和标签数据
|
1131 |
label_data = {main_category: 1.0}
|
1132 |
return final_result, label_data
|
1133 |
-
|
1134 |
def save_user_feedback(image_id, user_feedback):
|
1135 |
"""Save user feedback for continuous learning"""
|
1136 |
if not image_id:
|
|
|
125 |
"model_name": model_info["name"],
|
126 |
"ai_probability": ai_probability,
|
127 |
"predicted_class": model_info["model"].config.id2label[predicted_class_idx],
|
128 |
+
"confidence": float(probabilities[0][predicted_class_idx].item()),
|
129 |
+
"raw_probability": ai_probability # 保存原始概率用于校准
|
130 |
}
|
131 |
|
132 |
except Exception as e:
|
|
|
273 |
self._extract_advanced_features(img_cv, features, image_id)
|
274 |
|
275 |
return features
|
|
|
276 |
def _extract_color_features(self, img_array, features):
|
277 |
"""Extract color-related features"""
|
278 |
if len(img_array.shape) == 3:
|
|
|
367 |
# 边缘方向一致性 - AI生成图像通常边缘方向过于一致
|
368 |
edge_dir_normalized = edge_dir_hist / np.sum(edge_dir_hist)
|
369 |
features["edge_direction_consistency"] = float(np.max(edge_dir_normalized))
|
|
|
370 |
def _extract_texture_features(self, img_cv, features, image_id):
|
371 |
"""Extract texture-related features"""
|
372 |
# 获取灰度图
|
|
|
475 |
corr_gb = np.corrcoef(noise_g, noise_b)[0,1]
|
476 |
|
477 |
features["noise_channel_correlation"] = float((abs(corr_rg) + abs(corr_rb) + abs(corr_gb)) / 3)
|
|
|
478 |
def _extract_symmetry_features(self, img_cv, features):
|
479 |
"""Extract symmetry-related features"""
|
480 |
h, w = img_cv.shape[:2]
|
|
|
602 |
features["freq_peak_prominence"] = float(np.mean(radial_mean[peaks]))
|
603 |
except:
|
604 |
pass
|
|
|
605 |
def _extract_advanced_features(self, img_cv, features, image_id):
|
606 |
"""Extract advanced features for AI detection"""
|
607 |
# 获取灰度图
|
|
|
671 |
except:
|
672 |
pass
|
673 |
#############################################
|
674 |
+
# 特征分析与决策逻辑部分 - 重新调整
|
675 |
#############################################
|
676 |
|
677 |
+
# 基于图像类型的特征阈值 - 更严格的阈值
|
678 |
OPTIMIZED_THRESHOLDS = {
|
679 |
"lbp_entropy": {
|
680 |
+
"default": 1.8, # 更严格的阈值
|
681 |
+
"portrait": 1.7,
|
682 |
+
"landscape": 1.9,
|
683 |
},
|
684 |
"freq_anisotropy": {
|
685 |
+
"default": 0.005, # 更严格的阈值
|
686 |
+
"portrait": 0.004,
|
687 |
+
"landscape": 0.006,
|
688 |
},
|
689 |
"texture_correlation": {
|
690 |
+
"default": 0.97, # 更严格的阈值
|
691 |
+
"portrait": 0.96,
|
692 |
+
"landscape": 0.97,
|
693 |
},
|
694 |
"horizontal_symmetry": {
|
695 |
+
"default": 0.9, # 更严格的阈值
|
696 |
+
"portrait": 0.88,
|
697 |
+
"landscape": 0.9,
|
698 |
},
|
699 |
"vertical_symmetry": {
|
700 |
+
"default": 0.9, # 更严格的阈值
|
701 |
+
"portrait": 0.88,
|
702 |
+
"landscape": 0.9,
|
703 |
},
|
704 |
"noise_spatial_std": {
|
705 |
+
"default": 0.2, # 更严格的阈值
|
706 |
+
"portrait": 0.2,
|
707 |
+
"landscape": 0.25,
|
708 |
},
|
709 |
"freq_ratio": {
|
710 |
+
"default": 0.0, # 更严格的阈值
|
711 |
+
"portrait": -0.05,
|
712 |
+
"landscape": 0.0,
|
713 |
},
|
714 |
"noise_spectrum_std": {
|
715 |
+
"default": 600, # 更严格的阈值
|
716 |
+
"portrait": 600,
|
717 |
+
"landscape": 700,
|
718 |
},
|
719 |
"color_entropy": {
|
720 |
+
"default": 3.0, # 更严格的阈值
|
721 |
+
"portrait": 3.0,
|
722 |
+
"landscape": 3.2,
|
723 |
},
|
724 |
"lbp_uniformity": {
|
725 |
+
"default": 0.25, # 更严格的阈值
|
726 |
+
"portrait": 0.25,
|
727 |
+
"landscape": 0.2,
|
728 |
},
|
729 |
"noise_channel_correlation": {
|
730 |
+
"default": 0.97, # 更严格的阈值
|
731 |
+
"portrait": 0.97,
|
732 |
+
"landscape": 0.95,
|
733 |
},
|
734 |
"wavelet_h_entropy": {
|
735 |
+
"default": 1.5, # 更严格的阈值
|
736 |
+
"portrait": 1.4,
|
737 |
+
"landscape": 1.6,
|
738 |
},
|
739 |
"dct_entropy": {
|
740 |
+
"default": 0.005, # 更严格的阈值
|
741 |
+
"portrait": 0.004,
|
742 |
+
"landscape": 0.006,
|
743 |
},
|
744 |
"naturalness_index": {
|
745 |
+
"default": 0.6, # 更严格的阈值
|
746 |
+
"portrait": 0.55,
|
747 |
+
"landscape": 0.65,
|
748 |
}
|
749 |
}
|
750 |
|
751 |
+
# 特征重要性权重 - 调整权重
|
752 |
FEATURE_IMPORTANCE = {
|
753 |
+
"lbp_entropy": 0.12,
|
754 |
+
"freq_anisotropy": 0.12,
|
755 |
+
"texture_correlation": 0.06,
|
756 |
+
"horizontal_symmetry": 0.02,
|
757 |
+
"vertical_symmetry": 0.02,
|
758 |
+
"noise_spatial_std": 0.06,
|
759 |
+
"freq_ratio": 0.04,
|
760 |
+
"noise_spectrum_std": 0.04,
|
761 |
+
"color_entropy": 0.06,
|
762 |
+
"lbp_uniformity": 0.04,
|
763 |
+
"noise_channel_correlation": 0.12, # 增加权重
|
764 |
+
"wavelet_h_entropy": 0.12, # 增加权重
|
765 |
+
"dct_entropy": 0.12, # 增加权重
|
766 |
+
"naturalness_index": 0.06
|
767 |
}
|
768 |
|
769 |
def get_threshold(feature_name, image_type="default"):
|
|
|
775 |
OPTIMIZED_THRESHOLDS[feature_name]["default"])
|
776 |
|
777 |
def check_ai_specific_features(image_features):
|
778 |
+
"""Enhanced check for AI-generated image features with stricter criteria"""
|
779 |
ai_score = 0
|
780 |
ai_signs = []
|
781 |
|
|
|
801 |
"freq_ratio", "noise_spectrum_std", "color_entropy",
|
802 |
"wavelet_h_entropy", "dct_entropy", "naturalness_index"]:
|
803 |
if value < threshold:
|
804 |
+
# 更严格的评分,只有显著低于阈值才给高分
|
805 |
+
feature_score = min(1.0, (threshold - value) / threshold * 1.5)
|
806 |
+
if feature_score > 0.7: # 提高显著性阈值
|
807 |
ai_signs.append(f"{feature_name} 异常低 ({value:.2f})")
|
808 |
|
809 |
# 高值表示AI生成的特征
|
810 |
elif feature_name in ["texture_correlation", "horizontal_symmetry", "vertical_symmetry",
|
811 |
"lbp_uniformity", "noise_channel_correlation"]:
|
812 |
if value > threshold:
|
813 |
+
# 更严格的评分,只有显著高于阈值才给高分
|
814 |
+
feature_score = min(1.0, (value - threshold) / (1 - threshold) * 1.5)
|
815 |
+
if feature_score > 0.7: # 提高显著性阈值
|
816 |
ai_signs.append(f"{feature_name} 异常高 ({value:.2f})")
|
817 |
|
818 |
# 累加加权分数
|
819 |
ai_score += feature_score * importance
|
820 |
|
821 |
+
# 特征组合分析 - 检查特定组合模式
|
822 |
+
if ("dct_entropy" in image_features and image_features["dct_entropy"] < 0.005 and
|
823 |
+
"wavelet_h_entropy" in image_features and image_features["wavelet_h_entropy"] < 1.5 and
|
824 |
+
"noise_channel_correlation" in image_features and image_features["noise_channel_correlation"] > 0.97):
|
825 |
+
# 这种组合强烈表明是AI生成
|
826 |
+
ai_score = max(ai_score, 0.9)
|
827 |
+
if "特征组合模式" not in ai_signs:
|
828 |
+
ai_signs.append("特征组合模式: 多个关键特征同时异常")
|
829 |
+
|
830 |
# 计算检测到多少关键特征
|
831 |
critical_count = len(ai_signs)
|
832 |
if critical_count >= 5:
|
|
|
835 |
ai_score = max(ai_score, 0.6) # 更保守,从0.7改为0.6
|
836 |
|
837 |
return min(ai_score, 1.0), ai_signs
|
|
|
838 |
def detect_beauty_filter_signs(image_features):
|
839 |
"""Detect beauty filter traces"""
|
840 |
beauty_score = 0
|
|
|
905 |
|
906 |
return min(ps_score, 1.0), ps_signs
|
907 |
|
908 |
+
def calibrate_model_output(probability):
|
909 |
+
"""校准模型输出,解决模型偏向真人的问题"""
|
910 |
+
# 如果模型输出总是在0.3-0.4之间,我们可以拉伸这个范围
|
911 |
+
if 0.25 <= probability <= 0.4:
|
912 |
+
# 将0.25-0.4范围拉伸到0.1-0.9
|
913 |
+
return 0.1 + (probability - 0.25) * (0.9 - 0.1) / (0.4 - 0.25)
|
914 |
+
return probability
|
915 |
+
|
916 |
+
def calibrate_feature_score(score, signs):
|
917 |
+
"""校准特征分析分数,解决特征分析偏向AI的问题"""
|
918 |
+
# 如果特征分数高但检测到的特征少,降低分数
|
919 |
+
if score > 0.7 and len(signs) < 3:
|
920 |
+
return 0.5 + (score - 0.5) * 0.5 # 降低分数
|
921 |
+
# 如果特征分数高且检测到多个特征,保持高分
|
922 |
+
elif score > 0.7 and len(signs) >= 4:
|
923 |
+
return score
|
924 |
+
# 中等分数情况
|
925 |
+
elif 0.4 < score <= 0.7:
|
926 |
+
return 0.4 + (score - 0.4) * 0.7 # 略微降低
|
927 |
+
return score
|
928 |
+
|
929 |
def calculate_model_consistency(model_results):
|
930 |
"""Calculate the consistency between model predictions"""
|
931 |
if not model_results:
|
|
|
945 |
consistency = max(0.0, 1.0 - variance * 5) # 缩放以获得合理的一致性分数
|
946 |
|
947 |
return consistency
|
|
|
948 |
def get_detailed_analysis(ai_probability, ps_score, beauty_score, ps_signs, ai_signs, beauty_signs,
|
949 |
valid_models_count, ai_feature_score, model_consistency):
|
950 |
+
"""Provide detailed analysis with improved confidence assessment"""
|
951 |
|
952 |
+
# 根据模型和特征分析的一致性调整置信度
|
953 |
+
if abs(ai_probability - ai_feature_score) > 0.2:
|
954 |
+
confidence_prefix = "低置信度:"
|
955 |
+
explanation = "(模型预测和特征分析结果存在较大差异)"
|
956 |
+
elif model_consistency > 0.8 and valid_models_count >= 2:
|
957 |
confidence_prefix = "高置信度:"
|
958 |
+
explanation = ""
|
959 |
+
elif model_consistency > 0.6 and valid_models_count >= 2:
|
960 |
confidence_prefix = "中等置信度:"
|
961 |
+
explanation = ""
|
962 |
else:
|
963 |
confidence_prefix = "低置信度:"
|
964 |
+
explanation = ""
|
965 |
|
966 |
# 计算编辑分数(在所有路径中都需要)
|
967 |
combined_edit_score = max(ps_score, beauty_score)
|
968 |
|
969 |
+
# 分类逻辑 - 从中立起点开始
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
970 |
if ai_probability > 0.6:
|
971 |
category = confidence_prefix + "AI生成图像" + explanation
|
972 |
description = "图像很可能是由AI完全生成,几乎没有真人照片的特征。"
|
973 |
main_category = "AI生成"
|
974 |
+
elif ai_probability < 0.4:
|
975 |
# 第二级分类:素人 vs 修图
|
976 |
if combined_edit_score > 0.5:
|
977 |
category = confidence_prefix + "真人照片,修图痕迹明显" + explanation
|
|
|
981 |
category = confidence_prefix + "真实素人照片" + explanation
|
982 |
description = "图像很可能是未经大量处理的真人照片,保留了自然的细节和特征。"
|
983 |
main_category = "真人照片-素人"
|
984 |
+
else:
|
985 |
+
# 处理边界情况 - 添加"无法确定"类别
|
986 |
+
category = confidence_prefix + "无法确定" + explanation
|
|
|
987 |
description = "系统无法确定该图像是AI生成还是真实照片。模型预测和特征分析结果不一致,需要人工判断。"
|
988 |
main_category = "无法确定"
|
989 |
|
990 |
# 处理边界情况 - AI生成与高度修图
|
991 |
+
if 0.45 < ai_probability < 0.55 and combined_edit_score > 0.7:
|
992 |
category = confidence_prefix + "真人照片,修图痕迹明显(也可能是AI生成)" + explanation
|
993 |
description = "图像可能是真人照片经过大量后期处理,也可能是AI生成图像。由于现代AI技术与高度修图效果相似,难以完全区分。"
|
994 |
main_category = "真人照片-修图明显"
|
|
|
999 |
beauty_details = "检测到的美颜特征:" + "、".join(beauty_signs) if beauty_signs else "未检测到明显的美颜特征。"
|
1000 |
|
1001 |
return category, description, ps_details, ai_details, beauty_details, main_category
|
1002 |
+
|
1003 |
def detect_ai_image(image):
|
1004 |
+
"""Enhanced main detection function with improved decision logic"""
|
1005 |
if image is None:
|
1006 |
return {"error": "未提供图像"}
|
1007 |
|
|
|
1035 |
model_result = model_manager.get_model_prediction(key, image)
|
1036 |
|
1037 |
if model_result and "error" not in model_result:
|
1038 |
+
# 校准模型输出
|
1039 |
+
model_result["ai_probability"] = calibrate_model_output(model_result["ai_probability"])
|
1040 |
+
|
1041 |
results[key] = model_result
|
1042 |
weighted_ai_probability += model_result["ai_probability"] * model_info["weight"]
|
1043 |
valid_models += 1
|
|
|
1060 |
ps_score, ps_signs = detect_photoshop_signs(image_features)
|
1061 |
beauty_score, beauty_signs = detect_beauty_filter_signs(image_features)
|
1062 |
|
1063 |
+
# 校准特征分析分数
|
1064 |
+
calibrated_feature_score = calibrate_feature_score(ai_feature_score, ai_signs)
|
1065 |
|
1066 |
+
# 从中立起点开始决策
|
1067 |
+
adjusted_probability = 0.5
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1068 |
|
1069 |
+
# 根据校准后的模型和特征分析调整概率
|
1070 |
+
if final_ai_probability > 0.7 or calibrated_feature_score > 0.8:
|
1071 |
+
# 强AI证据
|
1072 |
+
adjusted_probability = 0.5 + (final_ai_probability * 0.25 + calibrated_feature_score * 0.25)
|
1073 |
+
elif final_ai_probability < 0.3 and calibrated_feature_score < 0.3:
|
1074 |
+
# 强真人证据
|
1075 |
+
adjusted_probability = 0.5 - ((0.3 - final_ai_probability) * 0.25 + (0.3 - calibrated_feature_score) * 0.25)
|
1076 |
+
else:
|
1077 |
+
# 证据不足,保持在中间区域
|
1078 |
+
adjusted_probability = 0.5 + (final_ai_probability - 0.5) * 0.2 + (calibrated_feature_score - 0.5) * 0.2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1079 |
|
1080 |
+
# 特征组合分析 - 强有力的组合证据
|
1081 |
+
if ("dct_entropy" in image_features and image_features["dct_entropy"] < 0.005 and
|
1082 |
+
"wavelet_h_entropy" in image_features and image_features["wavelet_h_entropy"] < 1.5 and
|
1083 |
+
"noise_channel_correlation" in image_features and image_features["noise_channel_correlation"] > 0.97):
|
1084 |
+
# 这种组合强烈表明是AI生成
|
1085 |
+
adjusted_probability = max(adjusted_probability, 0.7)
|
1086 |
|
1087 |
# 确保概率在有效范围内
|
1088 |
adjusted_probability = min(1.0, max(0.0, adjusted_probability))
|
|
|
1090 |
# 获取详细分析
|
1091 |
category, description, ps_details, ai_details, beauty_details, main_category = get_detailed_analysis(
|
1092 |
adjusted_probability, ps_score, beauty_score, ps_signs, ai_signs, beauty_signs,
|
1093 |
+
valid_models, calibrated_feature_score, model_consistency
|
1094 |
)
|
1095 |
|
1096 |
# 构建最终结果
|
|
|
1102 |
"original_ai_probability": final_ai_probability,
|
1103 |
"ps_score": ps_score,
|
1104 |
"beauty_score": beauty_score,
|
1105 |
+
"ai_feature_score": calibrated_feature_score,
|
1106 |
+
"raw_feature_score": ai_feature_score,
|
1107 |
"model_consistency": model_consistency,
|
1108 |
"category": category,
|
1109 |
"main_category": main_category,
|
|
|
1127 |
# 返回两个值:JSON结果和标签数据
|
1128 |
label_data = {main_category: 1.0}
|
1129 |
return final_result, label_data
|
|
|
1130 |
def save_user_feedback(image_id, user_feedback):
|
1131 |
"""Save user feedback for continuous learning"""
|
1132 |
if not image_id:
|