cang-ying/tools/vision-analyzer.py

133 lines
4.6 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""铸渊之眼 · 视觉分析器 · 定量对比两张图的风格一致性
用法:
python3 vision-analyzer.py <image1.jpg> <image2.jpg> # 对比两张图
python3 vision-analyzer.py <image.jpg> # 分析单张图
输出JSON: style_consistency_score, color_palettes, texture_similarity, composition_analysis
"""
import sys
import json
import colorsys
from PIL import Image
import numpy as np
def analyze_image(path, label):
"""分析单张图片的视觉特征"""
img = Image.open(path).convert('RGB')
w, h = img.size
arr = np.array(img)
# 1. 整体色调分析 — HSV直方图
hsv_arr = np.array([colorsys.rgb_to_hsv(r/255, g/255, b/255)
for r,g,b in arr.reshape(-1, 3)])
hue_hist = np.histogram(hsv_arr[:,0], bins=12, range=(0,1))[0]
sat_hist = np.histogram(hsv_arr[:,1], bins=8, range=(0,1))[0]
val_hist = np.histogram(hsv_arr[:,2], bins=8, range=(0,1))[0]
# 主色调
dominant_hues = []
for i in np.argsort(hue_hist)[-3:]:
hue_name = ["","","","黄绿","绿","青绿","","","","品红","粉红",""][i]
dominant_hues.append(hue_name)
# 2. 构图分析 — 9宫格亮度和边缘密度
grid_mask = np.zeros(h, dtype=int)
for i in range(1,9):
grid_mask = np.where(np.arange(h) < h*i/9, i, grid_mask)
# 简化:水平/垂直分三区的平均亮度
bands_h = [arr[h*i//3:h*(i+1)//3, :, :].mean() for i in range(3)]
bands_v = [arr[:, w*i//3:w*(i+1)//3, :].mean() for i in range(3)]
# 3. 纹理复杂度 — 标准差
texture_std = arr.std(axis=(0,1)).mean()
# 4. 色彩丰富度
color_variance = hsv_arr[:,1].std()
# 5. 亮暗对比度
contrast = arr.max() - arr.min()
avg_brightness = arr.mean()
return {
"label": label,
"size": [w, h],
"dominant_hues": dominant_hues,
"avg_brightness": round(float(avg_brightness), 1),
"contrast": round(float(contrast), 1),
"texture_std": round(float(texture_std), 1),
"color_variance": round(float(color_variance), 3),
"horizontal_brightness": [round(float(b), 1) for b in bands_h],
"vertical_brightness": [round(float(b), 1) for b in bands_v],
"hue_distribution": [int(h) for h in hue_hist],
"sat_distribution": [int(s) for s in sat_hist],
"val_distribution": [int(v) for v in val_hist]
}
def compare_images(a, b):
"""对比两张图并给出风格一致性评分"""
# 色调相似度 — hue分布的相关性
h1, h2 = np.array(a["hue_distribution"]), np.array(b["hue_distribution"])
if h1.sum() > 0 and h2.sum() > 0:
h1_norm, h2_norm = h1/h1.sum(), h2/h2.sum()
hue_corr = np.corrcoef(h1_norm, h2_norm)[0,1]
else:
hue_corr = 0
hue_score = max(0, float(hue_corr))
# 亮度相似度
bright_diff = abs(a["avg_brightness"] - b["avg_brightness"])
bright_score = max(0, 1 - bright_diff / 100)
# 纹理相似度
tex_diff = abs(a["texture_std"] - b["texture_std"])
tex_score = max(0, 1 - tex_diff / 50)
# 色彩丰富度相似度
cv_diff = abs(a["color_variance"] - b["color_variance"])
cv_score = max(0, 1 - cv_diff * 10)
# 综合评分
consistency = round(float(hue_score * 0.35 + bright_score * 0.25 + tex_score * 0.25 + cv_score * 0.15) * 100, 1)
verdict = (
"✅ 高度一致" if consistency >= 85 else
"🟢 基本一致" if consistency >= 70 else
"🟡 有差异" if consistency >= 50 else
"🔴 严重不一致"
)
return {
"style_consistency_score": consistency,
"verdict": verdict,
"breakdown": {
"色调相似度": round(float(hue_score * 100), 1),
"亮度相似度": round(float(bright_score * 100), 1),
"纹理相似度": round(float(tex_score * 100), 1),
"色彩丰富度相似度": round(float(cv_score * 100), 1)
},
"issues": []
}
if __name__ == "__main__":
if len(sys.argv) < 2:
print(json.dumps({"error": "usage: vision-analyzer.py <image1> [image2]"}))
sys.exit(1)
if len(sys.argv) == 2:
result = analyze_image(sys.argv[1], sys.argv[1])
else:
a = analyze_image(sys.argv[1], sys.argv[1])
b = analyze_image(sys.argv[2], sys.argv[2])
comparison = compare_images(a, b)
result = {
"image_a": a,
"image_b": b,
"comparison": comparison
}
print(json.dumps(result, ensure_ascii=False, indent=2))