Spaces:
Running
Running
import gradio as gr | |
import torch | |
from PIL import Image | |
import numpy as np | |
import cv2 | |
from transformers import AutoImageProcessor, AutoModelForImageClassification | |
from skimage.feature import graycomatrix, graycoprops, local_binary_pattern | |
from scipy import ndimage, stats | |
# 加载多个检测模型 | |
models = { | |
"model1": { | |
"name": "umm-maybe/AI-image-detector", | |
"processor": None, | |
"model": None, | |
"weight": 0.5 | |
}, | |
"model2": { | |
"name": "microsoft/resnet-50", # 通用图像分类模型 | |
"processor": None, | |
"model": None, | |
"weight": 0.25 | |
}, | |
"model3": { | |
"name": "google/vit-base-patch16-224", # Vision Transformer模型 | |
"processor": None, | |
"model": None, | |
"weight": 0.25 | |
} | |
} | |
# 初始化模型 | |
for key in models: | |
try: | |
models[key]["processor"] = AutoImageProcessor.from_pretrained(models[key]["name"]) | |
models[key]["model"] = AutoModelForImageClassification.from_pretrained(models[key]["name"]) | |
print(f"成功加载模型: {models[key]['name']}") | |
except Exception as e: | |
print(f"加载模型 {models[key]['name']} 失败: {str(e)}") | |
models[key]["processor"] = None | |
models[key]["model"] = None | |
def process_model_output(model_info, outputs, probabilities): | |
"""处理不同模型的输出,统一返回AI生成概率""" | |
model_name = model_info["name"].lower() | |
# 针对不同模型的特殊处理 | |
if "ai-image-detector" in model_name: | |
# umm-maybe/AI-image-detector模型特殊处理 | |
# 检查标签 | |
ai_label_idx = None | |
human_label_idx = None | |
for idx, label in model_info["model"].config.id2label.items(): | |
label_lower = label.lower() | |
if "ai" in label_lower or "generated" in label_lower or "fake" in label_lower: | |
ai_label_idx = idx | |
if "human" in label_lower or "real" in label_lower: | |
human_label_idx = idx | |
# 修正后的标签解释逻辑 | |
if human_label_idx is not None: | |
# 如果预测为human,则AI概率应该低 | |
ai_probability = 1 - float(probabilities[0][human_label_idx].item()) | |
elif ai_label_idx is not None: | |
# 如果预测为AI,则AI概率应该高 | |
ai_probability = float(probabilities[0][ai_label_idx].item()) | |
else: | |
# 默认情况 | |
ai_probability = 0.5 | |
elif "resnet" in model_name: | |
# 通用图像分类模型,使用简单启发式方法 | |
predicted_class_idx = outputs.logits.argmax(-1).item() | |
# 检查是否有与AI相关的类别 | |
predicted_class = model_info["model"].config.id2label[predicted_class_idx].lower() | |
# 简单启发式:检查类别名称是否包含与AI生成相关的关键词 | |
ai_keywords = ["artificial", "generated", "synthetic", "fake", "computer"] | |
for keyword in ai_keywords: | |
if keyword in predicted_class: | |
return float(probabilities[0][predicted_class_idx].item()) | |
# 如果没有明确的AI类别,返回中等概率 | |
return 0.5 | |
elif "vit" in model_name: | |
# Vision Transformer模型 | |
predicted_class_idx = outputs.logits.argmax(-1).item() | |
# 同样检查类别名称 | |
predicted_class = model_info["model"].config.id2label[predicted_class_idx].lower() | |
# 简单启发式:检查类别名称是否包含与AI生成相关的关键词 | |
ai_keywords = ["artificial", "generated", "synthetic", "fake", "computer"] | |
for keyword in ai_keywords: | |
if keyword in predicted_class: | |
return float(probabilities[0][predicted_class_idx].item()) | |
# 如果没有明确的AI类别,返回中等概率 | |
return 0.5 | |
# 默认处理 | |
predicted_class_idx = outputs.logits.argmax(-1).item() | |
predicted_class = model_info["model"].config.id2label[predicted_class_idx].lower() | |
if "ai" in predicted_class or "generated" in predicted_class or "fake" in predicted_class: | |
return float(probabilities[0][predicted_class_idx].item()) | |
else: | |
return 1 - float(probabilities[0][predicted_class_idx].item()) | |
return ai_probability | |
def analyze_image_features(image): | |
"""分析图像特征""" | |
# 转换为OpenCV格式 | |
img_array = np.array(image) | |
if len(img_array.shape) == 3 and img_array.shape[2] == 3: | |
img_cv = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR) | |
else: | |
img_cv = img_array | |
features = {} | |
# 基本特征 | |
features["width"] = image.width | |
features["height"] = image.height | |
features["aspect_ratio"] = image.width / max(1, image.height) | |
# 颜色分析 | |
if len(img_array.shape) == 3: | |
features["avg_red"] = float(np.mean(img_array[:,:,0])) | |
features["avg_green"] = float(np.mean(img_array[:,:,1])) | |
features["avg_blue"] = float(np.mean(img_array[:,:,2])) | |
# 颜色标准差 - 用于检测颜色分布是否自然 | |
features["color_std"] = float(np.std([ | |
features["avg_red"], | |
features["avg_green"], | |
features["avg_blue"] | |
])) | |
# 颜色局部变化 - 真实照片通常有更多局部颜色变化 | |
local_color_variations = [] | |
for i in range(0, img_array.shape[0]-10, 10): | |
for j in range(0, img_array.shape[1]-10, 10): | |
patch = img_array[i:i+10, j:j+10] | |
local_color_variations.append(np.std(patch)) | |
features["local_color_variation"] = float(np.mean(local_color_variations)) | |
# 颜色分布的自然度 - 真实照片的颜色分布更自然 | |
r_hist, _ = np.histogram(img_array[:,:,0], bins=256, range=(0, 256)) | |
g_hist, _ = np.histogram(img_array[:,:,1], bins=256, range=(0, 256)) | |
b_hist, _ = np.histogram(img_array[:,:,2], bins=256, range=(0, 256)) | |
# 计算颜色直方图的熵 - 真实照片通常熵值更高 | |
r_entropy = stats.entropy(r_hist + 1e-10) # 添加小值避免log(0) | |
g_entropy = stats.entropy(g_hist + 1e-10) | |
b_entropy = stats.entropy(b_hist + 1e-10) | |
features["color_entropy"] = float((r_entropy + g_entropy + b_entropy) / 3) | |
# 边缘一致性分析 | |
edges = cv2.Canny(img_cv, 100, 200) | |
features["edge_density"] = float(np.sum(edges > 0) / (image.width * image.height)) | |
# 边缘自然度分析 - 真实照片的边缘通常更自然 | |
if len(img_array.shape) == 3: | |
gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY) | |
sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3) | |
sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3) | |
edge_magnitude = np.sqrt(sobelx**2 + sobely**2) | |
features["edge_variance"] = float(np.var(edge_magnitude)) | |
# 边缘方向分布 - 真实照片的边缘方向分布更自然 | |
edge_direction = np.arctan2(sobely, sobelx) * 180 / np.pi | |
edge_dir_hist, _ = np.histogram(edge_direction[edge_magnitude > 30], bins=36, range=(-180, 180)) | |
features["edge_direction_entropy"] = float(stats.entropy(edge_dir_hist + 1e-10)) | |
# 纹理分析 - 使用灰度共生矩阵 | |
if len(img_array.shape) == 3: | |
gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY) | |
# 计算GLCM | |
distances = [5] | |
angles = [0, np.pi/4, np.pi/2, 3*np.pi/4] | |
glcm = graycomatrix(gray, distances=distances, angles=angles, symmetric=True, normed=True) | |
# 计算GLCM属性 | |
features["texture_contrast"] = float(np.mean(graycoprops(glcm, 'contrast')[0])) | |
features["texture_homogeneity"] = float(np.mean(graycoprops(glcm, 'homogeneity')[0])) | |
features["texture_correlation"] = float(np.mean(graycoprops(glcm, 'correlation')[0])) | |
features["texture_energy"] = float(np.mean(graycoprops(glcm, 'energy')[0])) | |
features["texture_dissimilarity"] = float(np.mean(graycoprops(glcm, 'dissimilarity')[0])) | |
features["texture_ASM"] = float(np.mean(graycoprops(glcm, 'ASM')[0])) | |
# 局部二值模式 (LBP) - 分析微观纹理 | |
try: | |
radius = 3 | |
n_points = 8 * radius | |
lbp = local_binary_pattern(gray, n_points, radius, method='uniform') | |
lbp_hist, _ = np.histogram(lbp, bins=n_points + 2, range=(0, n_points + 2)) | |
lbp_hist = lbp_hist.astype(float) / sum(lbp_hist) | |
features["lbp_entropy"] = float(stats.entropy(lbp_hist + 1e-10)) | |
except: | |
# 如果LBP分析失败,不添加这个特征 | |
pass | |
# 噪声分析 | |
if len(img_array.shape) == 3: | |
blurred = cv2.GaussianBlur(img_cv, (5, 5), 0) | |
noise = cv2.absdiff(img_cv, blurred) | |
features["noise_level"] = float(np.mean(noise)) | |
# 噪声分布 - 用于检测噪声是否自然 | |
features["noise_std"] = float(np.std(noise)) | |
# 噪声频谱分析 - 真实照片的噪声频谱更自然 | |
noise_fft = np.fft.fft2(noise[:,:,0]) | |
noise_fft_shift = np.fft.fftshift(noise_fft) | |
noise_magnitude = np.abs(noise_fft_shift) | |
features["noise_spectrum_std"] = float(np.std(noise_magnitude)) | |
# 噪声的空间一致性 - AI生成图像的噪声空间分布通常不够自然 | |
noise_blocks = [] | |
block_size = 32 | |
for i in range(0, noise.shape[0]-block_size, block_size): | |
for j in range(0, noise.shape[1]-block_size, block_size): | |
block = noise[i:i+block_size, j:j+block_size] | |
noise_blocks.append(np.mean(block)) | |
features["noise_spatial_std"] = float(np.std(noise_blocks)) | |
# 对称性分析 - AI生成图像通常有更高的对称性 | |
if img_cv.shape[1] % 2 == 0: # 确保宽度是偶数 | |
left_half = img_cv[:, :img_cv.shape[1]//2] | |
right_half = cv2.flip(img_cv[:, img_cv.shape[1]//2:], 1) | |
if left_half.shape == right_half.shape: | |
h_symmetry = 1 - float(np.mean(cv2.absdiff(left_half, right_half)) / 255) | |
features["horizontal_symmetry"] = h_symmetry | |
if img_cv.shape[0] % 2 == 0: # 确保高度是偶数 | |
top_half = img_cv[:img_cv.shape[0]//2, :] | |
bottom_half = cv2.flip(img_cv[img_cv.shape[0]//2:, :], 0) | |
if top_half.shape == bottom_half.shape: | |
v_symmetry = 1 - float(np.mean(cv2.absdiff(top_half, bottom_half)) / 255) | |
features["vertical_symmetry"] = v_symmetry | |
# 频率域分析 - 检测不自然的频率分布 | |
if len(img_array.shape) == 3: | |
gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY) | |
f_transform = np.fft.fft2(gray) | |
f_shift = np.fft.fftshift(f_transform) | |
magnitude = np.log(np.abs(f_shift) + 1) | |
# 计算高频和低频成分的比例 | |
h, w = magnitude.shape | |
center_h, center_w = h // 2, w // 2 | |
# 低频区域 (中心区域) | |
low_freq_region = magnitude[center_h-h//8:center_h+h//8, center_w-w//8:center_w+w//8] | |
low_freq_mean = np.mean(low_freq_region) | |
# 高频区域 (边缘区域) | |
high_freq_mean = np.mean(magnitude) - low_freq_mean | |
features["freq_ratio"] = float(high_freq_mean / max(low_freq_mean, 0.001)) | |
# 频率分布的自然度 - 真实照片通常有更自然的频率分布 | |
freq_std = np.std(magnitude) | |
features["freq_std"] = float(freq_std) | |
# 频率分布的各向异性 - 真实照片的频率分布通常更各向异性 | |
freq_blocks = [] | |
for angle in range(0, 180, 20): | |
mask = np.zeros_like(magnitude) | |
cv2.ellipse(mask, (center_w, center_h), (w//2, h//2), angle, -10, 10, 1, -1) | |
freq_blocks.append(np.mean(magnitude * mask)) | |
features["freq_anisotropy"] = float(np.std(freq_blocks)) | |
# 尝试检测人脸 | |
try: | |
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml') | |
gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY) | |
faces = face_cascade.detectMultiScale(gray, 1.1, 4) | |
features["face_count"] = len(faces) | |
if len(faces) > 0: | |
# 分析人脸特征 | |
face_features = [] | |
for (x, y, w, h) in faces: | |
face = img_cv[y:y+h, x:x+w] | |
# 皮肤质感分析 | |
face_hsv = cv2.cvtColor(face, cv2.COLOR_BGR2HSV) | |
skin_mask = cv2.inRange(face_hsv, (0, 20, 70), (20, 150, 255)) | |
skin_pixels = face[skin_mask > 0] | |
if len(skin_pixels) > 0: | |
face_features.append({ | |
"skin_std": float(np.std(skin_pixels)), | |
"skin_local_contrast": float(np.mean(cv2.Laplacian(face, cv2.CV_64F))), | |
"face_symmetry": analyze_face_symmetry(face) | |
}) | |
if face_features: | |
features["face_skin_std"] = np.mean([f["skin_std"] for f in face_features]) | |
features["face_local_contrast"] = np.mean([f["skin_local_contrast"] for f in face_features]) | |
features["face_symmetry"] = np.mean([f["face_symmetry"] for f in face_features]) | |
# 面部微表情分析 | |
for i, (x, y, w, h) in enumerate(faces): | |
face = gray[y:y+h, x:x+w] | |
# 分析面部纹理的局部变化 | |
face_blocks = [] | |
block_size = 8 | |
for bi in range(0, face.shape[0]-block_size, block_size): | |
for bj in range(0, face.shape[1]-block_size, block_size): | |
block = face[bi:bi+block_size, bj:bj+block_size] | |
face_blocks.append(np.std(block)) | |
if face_blocks: | |
features[f"face_{i}_texture_variation"] = float(np.std(face_blocks)) | |
except: | |
# 如果人脸检测失败,不添加人脸特征 | |
pass | |
# 分析物理一致性 | |
physical_features = analyze_physical_consistency(img_cv) | |
features.update(physical_features) | |
# 分析细节连贯性 | |
detail_features = analyze_detail_coherence(img_cv) | |
features.update(detail_features) | |
# 分析衣物细节 | |
clothing_features = analyze_clothing_details(img_cv) | |
features.update(clothing_features) | |
# 分析手部和关节 | |
extremity_features = analyze_extremities(img_cv) | |
features.update(extremity_features) | |
return features | |
def analyze_face_symmetry(face): | |
"""分析人脸对称性""" | |
if face.shape[1] % 2 == 0: # 确保宽度是偶数 | |
left_half = face[:, :face.shape[1]//2] | |
right_half = cv2.flip(face[:, face.shape[1]//2:], 1) | |
if left_half.shape == right_half.shape: | |
return 1 - float(np.mean(cv2.absdiff(left_half, right_half)) / 255) | |
return 0.5 # 默认值 | |
def analyze_physical_consistency(image): | |
"""分析图像中的物理一致性""" | |
features = {} | |
try: | |
# 转换为灰度图 | |
if len(image.shape) == 3: | |
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | |
else: | |
gray = image | |
# 光影一致性分析 | |
# 检测亮度梯度 | |
sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3) | |
sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3) | |
gradient_magnitude = np.sqrt(sobelx**2 + sobely**2) | |
gradient_direction = np.arctan2(sobely, sobelx) | |
# 分析梯度方向的一致性 - 真实照片的光影梯度方向更一致 | |
# 将图像分成块,分析每个块的主要梯度方向 | |
block_size = 32 | |
gradient_dirs = [] | |
for i in range(0, gray.shape[0]-block_size, block_size): | |
for j in range(0, gray.shape[1]-block_size, block_size): | |
block_gradient = gradient_direction[i:i+block_size, j:j+block_size] | |
block_magnitude = gradient_magnitude[i:i+block_size, j:j+block_size] | |
# 只考虑梯度幅值较大的像素 | |
significant_gradients = block_gradient[block_magnitude > np.mean(block_magnitude)] | |
if len(significant_gradients) > 0: | |
# 计算主要方向 | |
hist, _ = np.histogram(significant_gradients, bins=8, range=(-np.pi, np.pi)) | |
main_dir = np.argmax(hist) | |
gradient_dirs.append(main_dir) | |
if gradient_dirs: | |
# 计算主要方向的一致性 | |
hist, _ = np.histogram(gradient_dirs, bins=8, range=(0, 8)) | |
features["light_direction_consistency"] = float(np.max(hist) / max(sum(hist), 1)) | |
# 透视一致性分析 | |
# 使用霍夫变换检测直线 | |
edges = cv2.Canny(gray, 50, 150) | |
lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=50, minLineLength=50, maxLineGap=10) | |
if lines is not None and len(lines) > 1: | |
# 分析消失点 | |
vanishing_points = [] | |
for i in range(len(lines)): | |
for j in range(i+1, len(lines)): | |
x1, y1, x2, y2 = lines[i][0] | |
x3, y3, x4, y4 = lines[j][0] | |
# 计算两条线的交点(可能的消失点) | |
d = (x1-x2)*(y3-y4) - (y1-y2)*(x3-x4) | |
if abs(d) > 0.001: # 避免平行线 | |
px = ((x1*y2 - y1*x2)*(x3-x4) - (x1-x2)*(x3*y4 - y3*x4)) / d | |
py = ((x1*y2 - y1*x2)*(y3-y4) - (y1-y2)*(x3*y4 - y3*x4)) / d | |
# 只考虑图像范围内或附近的交点 | |
img_diag = np.sqrt(gray.shape[0]**2 + gray.shape[1]**2) | |
if -img_diag < px < 2*gray.shape[1] and -img_diag < py < 2*gray.shape[0]: | |
vanishing_points.append((px, py)) | |
if vanishing_points: | |
# 分析消失点的聚集程度 - 真实照片的消失点通常更聚集 | |
vp_x = [p[0] for p in vanishing_points] | |
vp_y = [p[1] for p in vanishing_points] | |
features["perspective_consistency"] = float(1 / (1 + np.std(vp_x) + np.std(vp_y))) | |
except: | |
# 如果分析失败,不添加物理一致性特征 | |
pass | |
return features | |
def analyze_detail_coherence(image): | |
"""分析图像细节的连贯性""" | |
features = {} | |
try: | |
# 转换为灰度图 | |
if len(image.shape) == 3: | |
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | |
else: | |
gray = image | |
# 多尺度细节分析 | |
scales = [3, 5, 9] | |
detail_levels = [] | |
for scale in scales: | |
# 使用不同尺度的拉普拉斯算子提取细节 | |
laplacian = cv2.Laplacian(gray, cv2.CV_64F, ksize=scale) | |
abs_laplacian = np.abs(laplacian) | |
detail_levels.append(np.mean(abs_laplacian)) | |
# 计算细节随尺度变化的一致性 - 真实照片的细节随尺度变化更自然 | |
if len(detail_levels) > 1: | |
features["detail_scale_consistency"] = float(np.std(detail_levels) / max(np.mean(detail_levels), 0.001)) | |
# 分析图像不同区域的细节一致性 | |
block_size = 64 | |
detail_blocks = [] | |
for i in range(0, gray.shape[0]-block_size, block_size): | |
for j in range(0, gray.shape[1]-block_size, block_size): | |
block = gray[i:i+block_size, j:j+block_size] | |
# 计算块的细节水平 | |
block_laplacian = cv2.Laplacian(block, cv2.CV_64F) | |
detail_blocks.append(np.mean(np.abs(block_laplacian))) | |
if detail_blocks: | |
# 计算细节分布的均匀性 - AI生成图像的细节分布通常不够均匀 | |
features["detail_spatial_std"] = float(np.std(detail_blocks)) | |
features["detail_spatial_entropy"] = float(stats.entropy(detail_blocks + 1e-10)) | |
# 边缘过渡分析 | |
edges = cv2.Canny(gray, 50, 150) | |
dilated = cv2.dilate(edges, np.ones((3,3), np.uint8)) | |
edge_transition = cv2.absdiff(dilated, edges) | |
# 计算边缘过渡区域的特性 - 真实照片的边缘过渡更自然 | |
if np.sum(edge_transition) > 0: | |
transition_values = gray[edge_transition > 0] | |
if len(transition_values) > 0: | |
features["edge_transition_std"] = float(np.std(transition_values)) | |
except: | |
# 如果分析失败,不添加细节连贯性特征 | |
pass | |
return features | |
def analyze_clothing_details(image): | |
"""分析衣物细节的自然度""" | |
features = {} | |
try: | |
# 转换为灰度图 | |
if len(image.shape) == 3: | |
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | |
else: | |
gray = image | |
# 使用Canny边缘检测 | |
edges = cv2.Canny(gray, 50, 150) | |
# 使用霍夫变换检测直线 | |
lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=50, minLineLength=50, maxLineGap=10) | |
if lines is not None: | |
# 计算直线的角度分布 | |
angles = [] | |
for line in lines: | |
x1, y1, x2, y2 = line[0] | |
if x2 - x1 != 0: # 避免除以零 | |
angle = np.arctan((y2 - y1) / (x2 - x1)) * 180 / np.pi | |
angles.append(angle) | |
if angles: | |
# 计算角度的标准差 - AI生成的衣物褶皱通常角度分布不自然 | |
features["clothing_angle_std"] = float(np.std(angles)) | |
# 计算角度的直方图 - 检查是否有过多相似角度(AI生成特征) | |
hist, _ = np.histogram(angles, bins=18, range=(-90, 90)) | |
max_count = np.max(hist) | |
total_count = np.sum(hist) | |
features["clothing_angle_uniformity"] = float(max_count / max(total_count, 1)) | |
# 分析纹理的一致性 | |
# 将图像分成小块,计算每个块的纹理特征 | |
block_size = 32 | |
h, w = gray.shape | |
texture_variations = [] | |
for i in range(0, h-block_size, block_size): | |
for j in range(0, w-block_size, block_size): | |
block = gray[i:i+block_size, j:j+block_size] | |
# 计算局部LBP特征或简单的方差 | |
texture_variations.append(np.var(block)) | |
if texture_variations: | |
# 计算纹理变化的标准差 - 真实衣物纹理变化更自然 | |
features["clothing_texture_std"] = float(np.std(texture_variations)) | |
# 计算纹理变化的均值 - AI生成的衣物纹理通常变化较小 | |
features["clothing_texture_mean"] = float(np.mean(texture_variations)) | |
# 计算纹理变化的熵 - 真实衣物纹理变化的熵更高 | |
features["clothing_texture_entropy"] = float(stats.entropy(texture_variations + 1e-10)) | |
# 褶皱分析 - 使用形态学操作提取可能的褶皱 | |
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) | |
kernel = np.ones((3,3), np.uint8) | |
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel) | |
# 寻找轮廓 | |
contours, _ = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) | |
# 分析轮廓的复杂度 | |
if contours: | |
contour_complexities = [] | |
for contour in contours: | |
area = cv2.contourArea(contour) | |
if area > 100: # 忽略太小的轮廓 | |
perimeter = cv2.arcLength(contour, True) | |
complexity = perimeter / max(np.sqrt(area), 1) | |
contour_complexities.append(complexity) | |
if contour_complexities: | |
features["clothing_contour_complexity"] = float(np.mean(contour_complexities)) | |
features["clothing_contour_std"] = float(np.std(contour_complexities)) | |
except: | |
# 如果分析失败,不添加衣物特征 | |
pass | |
return features | |
def analyze_extremities(image): | |
"""分析手指、脚趾等末端细节""" | |
features = {} | |
try: | |
# 转换为灰度图 | |
if len(image.shape) == 3: | |
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | |
else: | |
gray = image | |
# 使用形态学操作提取可能的手部区域 | |
_, thresh = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY) | |
kernel = np.ones((5,5), np.uint8) | |
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel) | |
# 寻找轮廓 | |
contours, _ = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) | |
# 分析轮廓的复杂度 | |
if contours: | |
# 计算轮廓的周长与面积比 - 手指等细节会增加这个比值 | |
perimeter_area_ratios = [] | |
for contour in contours: | |
area = cv2.contourArea(contour) | |
if area > 100: # 忽略太小的轮廓 | |
perimeter = cv2.arcLength(contour, True) | |
ratio = perimeter / max(area, 1) | |
perimeter_area_ratios.append(ratio) | |
if perimeter_area_ratios: | |
features["extremity_perimeter_area_ratio"] = float(np.mean(perimeter_area_ratios)) | |
# 计算凸包缺陷 - 手指之间的间隙会产生凸包缺陷 | |
defect_depths = [] | |
for contour in contours: | |
if len(contour) > 5: # 需要足够多的点来计算凸包 | |
hull = cv2.convexHull(contour, returnPoints=False) | |
if len(hull) > 3: # 需要至少4个点来计算凸包缺陷 | |
try: | |
defects = cv2.convexityDefects(contour, hull) | |
if defects is not None: | |
for i in range(defects.shape[0]): | |
_, _, _, depth = defects[i, 0] | |
defect_depths.append(depth) | |
except: | |
pass | |
if defect_depths: | |
features["extremity_defect_depth_mean"] = float(np.mean(defect_depths)) | |
features["extremity_defect_depth_std"] = float(np.std(defect_depths)) | |
# 分析缺陷的分布 - 真实手指的缺陷分布更自然 | |
defect_hist, _ = np.histogram(defect_depths, bins=10) | |
features["extremity_defect_entropy"] = float(stats.entropy(defect_hist + 1e-10)) | |
# 分析轮廓的曲率变化 - 真实手指的曲率变化更自然 | |
curvature_variations = [] | |
for contour in contours: | |
if len(contour) > 20: # 需要足够多的点来计算曲率 | |
# 简化轮廓以减少噪声 | |
epsilon = 0.01 * cv2.arcLength(contour, True) | |
approx = cv2.approxPolyDP(contour, epsilon, True) | |
# 计算相邻点之间的角度变化 | |
angles = [] | |
for i in range(len(approx)): | |
p1 = approx[i][0] | |
p2 = approx[(i+1) % len(approx)][0] | |
p3 = approx[(i+2) % len(approx)][0] | |
# 计算两个向量之间的角度 | |
v1 = p2 - p1 | |
v2 = p3 - p2 | |
if np.linalg.norm(v1) > 0 and np.linalg.norm(v2) > 0: | |
cos_angle = np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)) | |
cos_angle = np.clip(cos_angle, -1.0, 1.0) # 确保在有效范围内 | |
angle = np.arccos(cos_angle) | |
angles.append(angle) | |
if angles: | |
curvature_variations.append(np.std(angles)) | |
if curvature_variations: | |
features["extremity_curvature_std"] = float(np.mean(curvature_variations)) | |
except: | |
# 如果分析失败,不添加末端特征 | |
pass | |
return features | |
def check_ai_specific_features(image_features): | |
"""检查AI生成图像的典型特征,增强对最新AI模型的检测能力""" | |
ai_score = 0 | |
ai_signs = [] | |
# 增加对微观纹理的分析权重 | |
if "lbp_entropy" in image_features: | |
if image_features["lbp_entropy"] < 2.0: | |
ai_score += 0.4 # 提高权重 | |
ai_signs.append("微观纹理熵极低,典型AI生成特征") | |
elif image_features["lbp_entropy"] < 3.0: | |
ai_score += 0.3 | |
ai_signs.append("微观纹理熵异常低") | |
# 增加对频率分布各向异性的分析权重 | |
if "freq_anisotropy" in image_features: | |
if image_features["freq_anisotropy"] < 0.05: | |
ai_score += 0.4 # 提高权重 | |
ai_signs.append("频率分布各向异性极低,典型AI生成特征") | |
elif image_features["freq_anisotropy"] < 0.5: | |
ai_score += 0.3 | |
ai_signs.append("频率分布各向异性异常低") | |
# 增加对细节一致性的分析 | |
if "detail_scale_consistency" in image_features and "detail_spatial_std" in image_features: | |
if image_features["detail_scale_consistency"] < 0.2 and image_features["detail_spatial_std"] < 5: | |
ai_score += 0.3 | |
ai_signs.append("细节一致性异常,典型AI生成特征") | |
# 检查对称性 - AI生成图像通常对称性高 | |
if "horizontal_symmetry" in image_features and "vertical_symmetry" in image_features: | |
avg_symmetry = (image_features["horizontal_symmetry"] + image_features["vertical_symmetry"]) / 2 | |
if avg_symmetry > 0.8: | |
ai_score += 0.15 | |
ai_signs.append("图像对称性异常高") | |
elif avg_symmetry > 0.7: | |
ai_score += 0.1 | |
ai_signs.append("图像对称性较高") | |
# 检查纹理相关性 - AI生成图像通常纹理相关性高 | |
if "texture_correlation" in image_features: | |
if image_features["texture_correlation"] > 0.95: # 提高阈值 | |
ai_score += 0.15 | |
ai_signs.append("纹理相关性异常高") | |
elif image_features["texture_correlation"] > 0.9: | |
ai_score += 0.1 | |
ai_signs.append("纹理相关性较高") | |
# 检查边缘与噪声的关系 - AI生成图像通常边缘清晰但噪声不自然 | |
if "edge_density" in image_features and "noise_level" in image_features: | |
edge_noise_ratio = image_features["edge_density"] / max(image_features["noise_level"], 0.001) | |
if edge_noise_ratio < 0.01: | |
ai_score += 0.15 | |
ai_signs.append("边缘与噪声分布不自然") | |
# 检查颜色平滑度 - AI生成图像通常颜色过渡更平滑 | |
if "color_std" in image_features and image_features["color_std"] < 10: | |
ai_score += 0.1 | |
ai_signs.append("颜色过渡异常平滑") | |
# 检查颜色熵 - AI生成图像通常颜色熵较低 | |
if "color_entropy" in image_features and image_features["color_entropy"] < 5: | |
ai_score += 0.15 | |
ai_signs.append("颜色分布熵值异常低") | |
# 检查纹理能量 - AI生成图像通常纹理能量分布不自然 | |
if "texture_energy" in image_features and image_features["texture_energy"] < 0.01: | |
ai_score += 0.15 | |
ai_signs.append("纹理能量分布不自然") | |
# 检查频率比例 - AI生成图像通常频率分布不自然 | |
if "freq_ratio" in image_features: | |
if image_features["freq_ratio"] < 0.1 or image_features["freq_ratio"] > 2.0: | |
ai_score += 0.1 | |
ai_signs.append("频率分布不自然") | |
# 检查噪声频谱 - 真实照片的噪声频谱更自然 | |
if "noise_spectrum_std" in image_features and image_features["noise_spectrum_std"] < 1000: | |
ai_score += 0.15 | |
ai_signs.append("噪声频谱异常规则") | |
# 检查噪声空间分布 - 真实照片的噪声空间分布更自然 | |
if "noise_spatial_std" in image_features and image_features["noise_spatial_std"] < 0.5: | |
ai_score += 0.15 | |
ai_signs.append("噪声空间分布异常均匀") | |
# 检查细节尺度一致性 - AI生成图像的细节尺度一致性通常不够自然 | |
if "detail_scale_consistency" in image_features and image_features["detail_scale_consistency"] < 0.2: | |
ai_score += 0.15 | |
ai_signs.append("细节尺度变化异常均匀") | |
# 检查细节空间分布 - AI生成图像的细节空间分布通常不够自然 | |
if "detail_spatial_std" in image_features and image_features["detail_spatial_std"] < 5: | |
ai_score += 0.15 | |
ai_signs.append("细节空间分布异常均匀") | |
# 检查细节空间熵 - AI生成图像的细节空间熵通常较低 | |
if "detail_spatial_entropy" in image_features and image_features["detail_spatial_entropy"] < 1.5: | |
ai_score += 0.15 | |
ai_signs.append("细节空间熵异常低") | |
# 检查边缘过渡 - AI生成图像的边缘过渡通常不够自然 | |
if "edge_transition_std" in image_features and image_features["edge_transition_std"] < 10: | |
ai_score += 0.15 | |
ai_signs.append("边缘过渡异常均匀") | |
# 检查光影一致性 - AI生成图像的光影一致性通常过高 | |
if "light_direction_consistency" in image_features and image_features["light_direction_consistency"] > 0.7: | |
ai_score += 0.15 | |
ai_signs.append("光影方向一致性异常高") | |
# 检查透视一致性 - AI生成图像的透视一致性通常过高 | |
if "perspective_consistency" in image_features and image_features["perspective_consistency"] > 0.7: | |
ai_score += 0.15 | |
ai_signs.append("透视一致性异常高") | |
# 检查人脸特征 - AI生成的人脸通常有特定特征 | |
if "face_symmetry" in image_features and image_features["face_symmetry"] > 0.8: | |
ai_score += 0.15 | |
ai_signs.append("人脸对称性异常高") | |
if "face_skin_std" in image_features and image_features["face_skin_std"] < 10: | |
ai_score += 0.2 | |
ai_signs.append("皮肤质感异常均匀") | |
# 检查面部纹理变化 - AI生成的面部纹理变化通常不够自然 | |
face_texture_keys = [k for k in image_features.keys() if k.startswith("face_") and k.endswith("_texture_variation")] | |
if face_texture_keys: | |
face_texture_variations = [image_features[k] for k in face_texture_keys] | |
if np.mean(face_texture_variations) < 5: | |
ai_score += 0.2 | |
ai_signs.append("面部纹理变化异常均匀") | |
# 检查衣物特征 - AI生成的衣物通常有特定问题 | |
if "clothing_angle_uniformity" in image_features and image_features["clothing_angle_uniformity"] > 0.3: | |
ai_score += 0.2 | |
ai_signs.append("衣物褶皱角度分布不自然") | |
if "clothing_texture_std" in image_features and image_features["clothing_texture_std"] < 100: | |
ai_score += 0.15 | |
ai_signs.append("衣物纹理变化异常均匀") | |
if "clothing_texture_entropy" in image_features and image_features["clothing_texture_entropy"] < 1.5: | |
ai_score += 0.15 | |
ai_signs.append("衣物纹理熵异常低") | |
if "clothing_contour_complexity" in image_features and image_features["clothing_contour_complexity"] < 5: | |
ai_score += 0.15 | |
ai_signs.append("衣物轮廓复杂度异常低") | |
# 检查手部特征 - AI生成的手部通常有特定问题 | |
if "extremity_perimeter_area_ratio" in image_features: | |
if image_features["extremity_perimeter_area_ratio"] < 0.05: | |
ai_score += 0.2 | |
ai_signs.append("手部/末端轮廓异常平滑") | |
if "extremity_defect_depth_std" in image_features and image_features["extremity_defect_depth_std"] < 10: | |
ai_score += 0.15 | |
ai_signs.append("手指间隙异常均匀") | |
if "extremity_defect_entropy" in image_features and image_features["extremity_defect_entropy"] < 1.0: | |
ai_score += 0.15 | |
ai_signs.append("手指间隙分布熵异常低") | |
if "extremity_curvature_std" in image_features and image_features["extremity_curvature_std"] < 0.2: | |
ai_score += 0.15 | |
ai_signs.append("手部曲率变化异常均匀") | |
# 特别关注最新AI模型的特征组合 | |
# 当多个特征同时出现时,这是强有力的AI生成证据 | |
ai_feature_count = len(ai_signs) | |
if ai_feature_count >= 5: # 如果检测到多个AI特征 | |
ai_score = max(ai_score, 0.9) # 确保AI分数很高 | |
elif ai_feature_count >= 3: | |
ai_score = max(ai_score, 0.7) | |
return min(ai_score, 1.0), ai_signs | |
def detect_beauty_filter_signs(image_features): | |
"""检测美颜滤镜痕迹""" | |
beauty_score = 0 | |
beauty_signs = [] | |
# 检查皮肤质感 | |
if "face_skin_std" in image_features: | |
if image_features["face_skin_std"] < 15: | |
beauty_score += 0.3 | |
beauty_signs.append("皮肤质感过于均匀,典型美颜特征") | |
elif image_features["face_skin_std"] < 25: | |
beauty_score += 0.2 | |
beauty_signs.append("皮肤质感较为均匀,可能使用了美颜") | |
# 检查局部对比度 - 美颜通常会降低局部对比度 | |
if "face_local_contrast" in image_features: | |
if image_features["face_local_contrast"] < 5: | |
beauty_score += 0.2 | |
beauty_signs.append("面部局部对比度低,典型美颜特征") | |
# 检查边缘平滑度 - 美颜通常会平滑边缘 | |
if "edge_density" in image_features: | |
if image_features["edge_density"] < 0.03: | |
beauty_score += 0.2 | |
beauty_signs.append("边缘过于平滑,典型美颜特征") | |
elif image_features["edge_density"] < 0.05: | |
beauty_score += 0.1 | |
beauty_signs.append("边缘较为平滑,可能使用了美颜") | |
# 检查噪点 - 美颜通常会减少噪点 | |
if "noise_level" in image_features: | |
if image_features["noise_level"] < 1.0: | |
beauty_score += 0.2 | |
beauty_signs.append("噪点异常少,典型美颜特征") | |
elif image_features["noise_level"] < 2.0: | |
beauty_score += 0.1 | |
beauty_signs.append("噪点较少,可能使用了美颜") | |
# 检查人脸对称性 - 美颜通常会增加对称性 | |
if "face_symmetry" in image_features: | |
if image_features["face_symmetry"] > 0.8: | |
beauty_score += 0.2 | |
beauty_signs.append("面部对称性异常高,典型美颜特征") | |
elif image_features["face_symmetry"] > 0.7: | |
beauty_score += 0.1 | |
beauty_signs.append("面部对称性较高,可能使用了美颜") | |
# 检查面部纹理变化 - 美颜通常会使面部纹理更均匀 | |
face_texture_keys = [k for k in image_features.keys() if k.startswith("face_") and k.endswith("_texture_variation")] | |
if face_texture_keys: | |
face_texture_variations = [image_features[k] for k in face_texture_keys] | |
if np.mean(face_texture_variations) < 10: | |
beauty_score += 0.2 | |
beauty_signs.append("面部纹理变化异常均匀,典型美颜特征") | |
# 检查边缘过渡 - 美颜通常会使边缘过渡更平滑 | |
if "edge_transition_std" in image_features and image_features["edge_transition_std"] < 15: | |
beauty_score += 0.2 | |
beauty_signs.append("边缘过渡异常平滑,典型美颜特征") | |
return min(beauty_score, 1.0), beauty_signs | |
def detect_photoshop_signs(image_features): | |
"""检测图像中的PS痕迹""" | |
ps_score = 0 | |
ps_signs = [] | |
# 检查皮肤质感 | |
if "texture_homogeneity" in image_features: | |
if image_features["texture_homogeneity"] > 0.4: | |
ps_score += 0.2 | |
ps_signs.append("皮肤质感过于均匀") | |
elif image_features["texture_homogeneity"] > 0.3: | |
ps_score += 0.1 | |
ps_signs.append("皮肤质感较为均匀") | |
# 检查边缘不自然 | |
if "edge_density" in image_features: | |
if image_features["edge_density"] < 0.01: | |
ps_score += 0.2 | |
ps_signs.append("边缘过于平滑") | |
elif image_features["edge_density"] < 0.03: | |
ps_score += 0.1 | |
ps_signs.append("边缘较为平滑") | |
# 检查颜色不自然 | |
if "color_std" in image_features: | |
if image_features["color_std"] > 50: | |
ps_score += 0.2 | |
ps_signs.append("颜色分布极不自然") | |
elif image_features["color_std"] > 30: | |
ps_score += 0.1 | |
ps_signs.append("颜色分布略不自然") | |
# 检查噪点不一致 | |
if "noise_level" in image_features and "noise_std" in image_features: | |
noise_ratio = image_features["noise_std"] / max(image_features["noise_level"], 0.001) | |
if noise_ratio < 0.5: | |
ps_score += 0.2 | |
ps_signs.append("噪点分布不自然") | |
elif noise_ratio < 0.7: | |
ps_score += 0.1 | |
ps_signs.append("噪点分布略不自然") | |
# 检查频率分布不自然 | |
if "freq_ratio" in image_features: | |
if image_features["freq_ratio"] < 0.2: | |
ps_score += 0.2 | |
ps_signs.append("频率分布不自然,可能有过度模糊处理") | |
elif image_features["freq_ratio"] > 2.0: | |
ps_score += 0.2 | |
ps_signs.append("频率分布不自然,可能有过度锐化处理") | |
# 检查细节不一致 | |
if "detail_spatial_std" in image_features and image_features["detail_spatial_std"] > 50: | |
ps_score += 0.2 | |
ps_signs.append("图像细节分布不一致,可能有局部修饰") | |
# 检查边缘过渡不自然 | |
if "edge_transition_std" in image_features: | |
if image_features["edge_transition_std"] > 50: | |
ps_score += 0.2 | |
ps_signs.append("边缘过渡不自然,可能有选区修饰") | |
elif image_features["edge_transition_std"] < 5: | |
ps_score += 0.2 | |
ps_signs.append("边缘过渡过于平滑,可能有过度修饰") | |
return min(ps_score, 1.0), ps_signs | |
# 在这里添加get_detailed_analysis函数 | |
def get_detailed_analysis(ai_probability, ps_score, beauty_score, ps_signs, ai_signs, beauty_signs, valid_models_count, ai_feature_score, image_features=None): | |
"""提供更详细的分析结果,使用二级分类框架,优先考虑AI特征分析""" | |
# 根据有效模型数量调整置信度描述 | |
confidence_prefix = "" | |
if valid_models_count >= 3: | |
confidence_prefix = "极高置信度:" | |
elif valid_models_count == 2: | |
confidence_prefix = "高置信度:" | |
elif valid_models_count == 1: | |
confidence_prefix = "中等置信度:" | |
# 特征与模型判断严重不一致时的处理 | |
if ai_feature_score > 0.8 and ai_probability < 0.6: | |
ai_probability = max(0.8, ai_probability) # 当AI特征分数非常高时,覆盖模型判断 | |
category = confidence_prefix + "AI生成图像(基于特征分析)" | |
description = "基于多种典型AI特征分析,该图像很可能是AI生成的,尽管模型判断结果不确定。" | |
main_category = "AI生成" | |
elif ai_feature_score > 0.6 and ai_probability < 0.5: | |
ai_probability = max(0.7, ai_probability) # 当AI特征分数高时,提高AI概率 | |
# 特定关键特征的硬性覆盖 | |
if image_features is not None: | |
if "lbp_entropy" in image_features and image_features["lbp_entropy"] < 2.0: | |
if "freq_anisotropy" in image_features and image_features["freq_anisotropy"] < 0.05: | |
# 当微观纹理熵极低且频率分布各向异性极低时,几乎可以确定是AI生成 | |
ai_probability = 0.95 | |
category = confidence_prefix + "AI生成图像(确定)" | |
description = "检测到多个决定性AI生成特征,该图像几乎可以确定是AI生成的。" | |
main_category = "AI生成" | |
# 添加具体的PS痕迹描述 | |
if ps_signs: | |
ps_details = "检测到的修图痕迹:" + "、".join(ps_signs) | |
else: | |
ps_details = "未检测到明显的修图痕迹。" | |
# 添加AI特征描述 | |
if ai_signs: | |
ai_details = "检测到的AI特征:" + "、".join(ai_signs) | |
else: | |
ai_details = "未检测到明显的AI生成特征。" | |
# 添加美颜特征描述 | |
if beauty_signs: | |
beauty_details = "检测到的美颜特征:" + "、".join(beauty_signs) | |
else: | |
beauty_details = "未检测到明显的美颜特征。" | |
return category, description, ps_details, ai_details, beauty_details, main_category | |
# 第一级分类:AI生成 vs 真人照片 | |
if ai_probability > 0.6: # 降低AI判定阈值,提高AI检出率 | |
category = confidence_prefix + "AI生成图像" | |
description = "图像很可能是由AI完全生成,几乎没有真人照片的特征。" | |
main_category = "AI生成" | |
else: | |
# 第二级分类:真实素人 vs 修图痕迹明显 | |
combined_edit_score = max(ps_score, beauty_score) # 取PS和美颜中的较高分 | |
if combined_edit_score > 0.5: | |
category = confidence_prefix + "真人照片,修图痕迹明显" | |
description = "图像基本是真人照片,但经过了明显的后期处理或美颜,修饰痕迹明显。" | |
main_category = "真人照片-修图明显" | |
else: | |
category = confidence_prefix + "真实素人照片" | |
description = "图像很可能是未经大量处理的真人照片,保留了自然的细节和特征。" | |
main_category = "真人照片-素人" | |
# 处理边界情况 - 当AI概率和修图分数都很高时 | |
if ai_probability > 0.45 and combined_edit_score > 0.7: | |
# 这是一个边界情况,可能是高度修图的真人照片,也可能是AI生成的 | |
category = confidence_prefix + "真人照片,修图痕迹明显(也可能是AI生成)" | |
description = "图像可能是真人照片经过大量后期处理,也可能是AI生成图像。由于现代AI技术与高度修图效果相似,难以完全区分。" | |
main_category = "真人照片-修图明显" | |
# 添加具体的PS痕迹描述 | |
if ps_signs: | |
ps_details = "检测到的修图痕迹:" + "、".join(ps_signs) | |
else: | |
ps_details = "未检测到明显的修图痕迹。" | |
# 添加AI特征描述 | |
if ai_signs: | |
ai_details = "检测到的AI特征:" + "、".join(ai_signs) | |
else: | |
ai_details = "未检测到明显的AI生成特征。" | |
# 添加美颜特征描述 | |
if beauty_signs: | |
beauty_details = "检测到的美颜特征:" + "、".join(beauty_signs) | |
else: | |
beauty_details = "未检测到明显的美颜特征。" | |
return category, description, ps_details, ai_details, beauty_details, main_category | |
def detect_ai_image(image): | |
"""主检测函数""" | |
if image is None: | |
return {"error": "未提供图像"} | |
results = {} | |
valid_models = 0 | |
weighted_ai_probability = 0 | |
# 使用每个模型进行预测 | |
for key, model_info in models.items(): | |
if model_info["processor"] is not None and model_info["model"] is not None: | |
try: | |
# 处理图像 | |
inputs = model_info["processor"](images=image, return_tensors="pt") | |
with torch.no_grad(): | |
outputs = model_info["model"](**inputs) | |
# 获取概率 | |
probabilities = torch.nn.functional.softmax(outputs.logits, dim=-1) | |
# 使用适配器处理不同模型的输出 | |
ai_probability = process_model_output(model_info, outputs, probabilities) | |
# 添加到结果 | |
predicted_class_idx = outputs.logits.argmax(-1).item() | |
results[key] = { | |
"model_name": model_info["name"], | |
"ai_probability": ai_probability, | |
"predicted_class": model_info["model"].config.id2label[predicted_class_idx] | |
} | |
# 累加加权概率 | |
weighted_ai_probability += ai_probability * model_info["weight"] | |
valid_models += 1 | |
except Exception as e: | |
results[key] = { | |
"model_name": model_info["name"], | |
"error": str(e) | |
} | |
# 计算最终加权概率 | |
if valid_models > 0: | |
final_ai_probability = weighted_ai_probability / sum(m["weight"] for k, m in models.items() if m["processor"] is not None and m["model"] is not None) | |
else: | |
return {"error": "所有模型加载失败"} | |
# 分析图像特征 | |
image_features = analyze_image_features(image) | |
# 检查AI特定特征 | |
ai_feature_score, ai_signs = check_ai_specific_features(image_features) | |
# 分析PS痕迹 | |
ps_score, ps_signs = detect_photoshop_signs(image_features) | |
# 分析美颜痕迹 | |
beauty_score, beauty_signs = detect_beauty_filter_signs(image_features) | |
# 应用特征权重调整AI概率 | |
adjusted_probability = final_ai_probability | |
# 提高AI特征分数的权重 | |
if ai_feature_score > 0.8: # 当AI特征非常明显时 | |
adjusted_probability = max(adjusted_probability, 0.8) # 大幅提高AI概率 | |
elif ai_feature_score > 0.6: | |
adjusted_probability = max(adjusted_probability, 0.7) | |
elif ai_feature_score > 0.4: | |
adjusted_probability = max(adjusted_probability, 0.6) | |
# 特别关注关键AI特征 | |
key_ai_features_count = 0 | |
# 检查微观纹理熵 - 这是AI生成的强力指标 | |
if "lbp_entropy" in image_features and image_features["lbp_entropy"] < 2.5: | |
key_ai_features_count += 1 | |
adjusted_probability += 0.1 | |
# 检查频率分布各向异性 - 这是AI生成的强力指标 | |
if "freq_anisotropy" in image_features and image_features["freq_anisotropy"] < 0.1: | |
key_ai_features_count += 1 | |
adjusted_probability += 0.1 | |
# 检查细节空间分布 - 这是AI生成的强力指标 | |
if "detail_spatial_std" in image_features and image_features["detail_spatial_std"] < 5: | |
key_ai_features_count += 1 | |
adjusted_probability += 0.1 | |
# 如果多个关键AI特征同时存在,这是强有力的AI生成证据 | |
if key_ai_features_count >= 2: | |
adjusted_probability = max(adjusted_probability, 0.7) | |
# 降低美颜特征对AI判断的影响 | |
# 即使美颜分数高,如果AI特征也明显,仍应判定为AI生成 | |
if beauty_score > 0.6 and ai_feature_score > 0.7: | |
# 不再降低AI概率,而是保持较高的AI概率 | |
pass | |
# 如果检测到衣物或手部异常,大幅提高AI概率 | |
if "clothing_angle_uniformity" in image_features and image_features["clothing_angle_uniformity"] > 0.3: | |
adjusted_probability = max(adjusted_probability, 0.7) | |
if "extremity_perimeter_area_ratio" in image_features and image_features["extremity_perimeter_area_ratio"] < 0.05: | |
adjusted_probability = max(adjusted_probability, 0.7) | |
# 确保概率在0-1范围内 | |
adjusted_probability = min(1.0, max(0.0, adjusted_probability)) | |
# 获取详细分析 | |
category, description, ps_details, ai_details, beauty_details, main_category = get_detailed_analysis( | |
adjusted_probability, ps_score, beauty_score, ps_signs, ai_signs, beauty_signs, valid_models, ai_feature_score | |
) | |
# 构建最终结果 | |
final_result = { | |
"ai_probability": adjusted_probability, | |
"original_ai_probability": final_ai_probability, | |
"ps_score": ps_score, | |
"beauty_score": beauty_score, | |
"ai_feature_score": ai_feature_score, | |
"category": category, | |
"main_category": main_category, | |
"description": description, | |
"ps_details": ps_details, | |
"ai_details": ai_details, | |
"beauty_details": beauty_details, | |
"individual_model_results": results, | |
"features": image_features | |
} | |
# 返回两个值:JSON结果和Label数据 | |
label_data = {main_category: 1.0} | |
return final_result, label_data | |
# 创建Gradio界面 | |
iface = gr.Interface( | |
fn=detect_ai_image, | |
inputs=gr.Image(type="pil"), | |
outputs=[ | |
gr.JSON(label="详细分析结果"), | |
gr.Label(label="主要分类", num_top_classes=1) | |
], | |
title="增强型AI图像检测API", | |
description="多模型集成检测图像是否由AI生成或真人照片(素人/修图)", | |
examples=None, | |
allow_flagging="never" | |
) | |
iface.launch() | |