Files
aiData/Test/analyze_chat_avatars.py

154 lines
5.7 KiB
Python
Raw Normal View History

2026-01-21 11:50:34 +08:00
# coding=utf-8
import cv2
import numpy as np
import os
def analyze_chat_image(image_path, output_path):
print(f"正在读取图片: {image_path}")
# 读取图片
# 注意:cv2.imread 不支持中文路径,需要用 np.fromfile 读取
try:
img_data = np.fromfile(image_path, dtype=np.uint8)
img = cv2.imdecode(img_data, cv2.IMREAD_COLOR)
except Exception as e:
print(f"读取图片失败: {e}")
return
if img is None:
print("图片读取为空")
return
height, width = img.shape[:2]
print(f"图片尺寸: {width}x{height}")
# 1. 预处理
# 转为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 使用自适应阈值二值化
thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY_INV, 11, 2)
# 形态学操作:闭运算,填充内部空洞
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# 2. 轮廓查找
contours, _ = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
print(f"检测到轮廓数量: {len(contours)}")
# 收集所有符合条件的头像
avatars = []
# 3. 筛选轮廓
for contour in contours:
# 获取外接矩形
x, y, w, h = cv2.boundingRect(contour)
# 筛选条件优化:
# 1. 形状接近正方形 (放宽宽高比限制: 0.8 ~ 1.2)
aspect_ratio = float(w) / h
# 2. 尺寸适中
# 假设头像宽度在屏幕宽度的 6% 到 15% 之间
min_w = width * 0.06
max_w = width * 0.15
# 3. 位置筛选
# 排除底部输入框区域 (假设底部 10% 为输入区域)
if y > height * 0.9:
continue
# 左侧头像:靠左边 (x < width * 0.18)
# 右侧头像:靠右边 (x > width * 0.82)
is_left = x < width * 0.18
is_right = x > width * 0.82
if 0.8 <= aspect_ratio <= 1.2 and min_w < w < max_w:
if is_left or is_right:
side = "Left" if is_left else "Right"
avatars.append({
'x': x, 'y': y, 'w': w, 'h': h,
'side': side
})
# 按 y 坐标排序
avatars.sort(key=lambda a: a['y'])
print(f"找到有效头像数量: {len(avatars)}")
# 4. 绘制对话内容框 (Green/Red Boxes)
# 策略:按顺序遍历头像,如果发现同侧连续,则视为一组。
# 从当前组的第一个头像上方开始,直到下一个不同侧的头像上方(或底部)。
if avatars:
i = 0
while i < len(avatars):
current_group_start = i
current_side = avatars[i]['side']
# 找到当前组的结束位置 (即下一个不同侧头像的索引)
j = i + 1
while j < len(avatars) and avatars[j]['side'] == current_side:
j += 1
# 当前组范围: avatars[i] ... avatars[j-1]
# 确定绘制区域的 Y 轴范围
# Start Y: 当前组第一个头像的上方 (例如 -10px)
start_y = max(0, avatars[i]['y'] - 10)
# End Y: 下一组第一个头像的上方 (减去较大间距,例如 -30px),或者当前组最后一个头像的底部加上边距
# 为了让框之间有明显间隔,我们采取策略:
# 如果有下一组,End Y = 下一组第一个头像的 y - 30 (留出间隙)
# 如果没有下一组,End Y = 屏幕底部区域上方
if j < len(avatars):
end_y = max(start_y + 10, avatars[j]['y'] - 30)
else:
end_y = int(height * 0.9) # 到底部输入框上方
# 绘制大框
# 左侧 (Left) -> 对方 -> 绿色 (0, 255, 0)
# 右侧 (Right) -> 我 -> 红色 (0, 0, 255)
# 注意 OpenCV 颜色是 BGR
box_color = (0, 255, 0) if current_side == "Left" else (0, 0, 255)
# 绘制矩形 (空心,线宽 5)
# X 轴范围:0 到 width
cv2.rectangle(img, (0, start_y), (width, end_y), box_color, 5)
print(f"绘制内容框: 侧别={current_side}, 范围 Y={start_y} to {end_y}")
# 移动到下一组
i = j
# 5. 绘制头像框 (Blue/Yellow Boxes) - 画在内容框之上
for av in avatars:
x, y, w, h = av['x'], av['y'], av['w'], av['h']
# 左侧:蓝色 (BGR: 255, 0, 0)
# 右侧:黄色 (BGR: 0, 255, 255)
color = (255, 0, 0) if av['side'] == "Left" else (0, 255, 255)
cv2.rectangle(img, (x, y), (x + w, y + h), color, 10)
print(f"绘制头像: 位置=({x},{y}), 侧别={av['side']}")
# 6. 保存结果
try:
# cv2.imwrite 不支持中文路径,使用 imencode + tofile
ext = os.path.splitext(output_path)[1]
cv2.imencode(ext, img)[1].tofile(output_path)
print(f"结果已保存至: {output_path}")
except Exception as e:
print(f"保存图片失败: {e}")
if __name__ == "__main__":
# 输入文件路径
input_file = r"d:\dsWork\aiData\Test\Screenshots\chat_result_20260121_113553.jpg"
# 输出文件路径
output_file = r"d:\dsWork\aiData\Test\Screenshots\chat_result_analyzed.jpg"
analyze_chat_image(input_file, output_file)