- Update ASR, face, OCR, pose processors - Add release pre-flight check script - Add synonym generation, chunk processing scripts - Add face recognition, stamp search utilities
358 lines
12 KiB
Python
358 lines
12 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
提取女性最多的畫面並標記人臉
|
||
"""
|
||
|
||
import cv2
|
||
import numpy as np
|
||
import json
|
||
import os
|
||
from datetime import datetime
|
||
|
||
|
||
def draw_female_faces(image_path, frame_number, output_dir="/tmp/female_faces"):
|
||
"""在圖像上標記女性人臉"""
|
||
|
||
# 創建輸出目錄
|
||
os.makedirs(output_dir, exist_ok=True)
|
||
|
||
# 讀取圖像
|
||
image = cv2.imread(image_path)
|
||
if image is None:
|
||
print(f"❌ 無法讀取圖像: {image_path}")
|
||
return None
|
||
|
||
# 從數據庫獲取女性人臉信息
|
||
import psycopg2
|
||
|
||
conn = psycopg2.connect(
|
||
host="localhost",
|
||
port=5432,
|
||
database="momentry",
|
||
user="accusys",
|
||
password="accusys",
|
||
)
|
||
|
||
cursor = conn.cursor()
|
||
cursor.execute(
|
||
"""
|
||
SELECT x, y, width, height, confidence,
|
||
(attributes->>'age')::numeric as age
|
||
FROM face_detections
|
||
WHERE frame_number = %s
|
||
AND attributes->>'gender' = 'female'
|
||
ORDER BY confidence DESC
|
||
""",
|
||
(frame_number,),
|
||
)
|
||
|
||
female_faces = cursor.fetchall()
|
||
cursor.close()
|
||
conn.close()
|
||
|
||
if not female_faces:
|
||
print(f"❌ 在幀 {frame_number} 中未找到女性人臉")
|
||
return None
|
||
|
||
print(f"✅ 在幀 {frame_number} 中找到 {len(female_faces)} 個女性人臉")
|
||
|
||
# 複製圖像用於標記
|
||
marked_image = image.copy()
|
||
|
||
# 標記每個人臉
|
||
for i, (x, y, w, h, confidence, age) in enumerate(female_faces):
|
||
# 繪製邊界框(粉色表示女性)
|
||
color = (255, 105, 180) # 粉色
|
||
thickness = 3
|
||
|
||
# 繪製矩形邊界框
|
||
cv2.rectangle(marked_image, (x, y), (x + w, y + h), color, thickness)
|
||
|
||
# 添加標籤
|
||
label = f"女 {i + 1}"
|
||
if age:
|
||
label += f" ({int(age)}歲)"
|
||
label += f" {confidence:.1%}"
|
||
|
||
# 計算標籤位置
|
||
label_size, baseline = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.7, 2)
|
||
label_y = max(y - 10, label_size[1] + 10)
|
||
|
||
# 繪製標籤背景
|
||
cv2.rectangle(
|
||
marked_image,
|
||
(x, label_y - label_size[1] - 10),
|
||
(x + label_size[0] + 10, label_y + 5),
|
||
color,
|
||
-1, # 填充
|
||
)
|
||
|
||
# 繪製標籤文字
|
||
cv2.putText(
|
||
marked_image,
|
||
label,
|
||
(x + 5, label_y - 5),
|
||
cv2.FONT_HERSHEY_SIMPLEX,
|
||
0.7,
|
||
(255, 255, 255), # 白色文字
|
||
2,
|
||
)
|
||
|
||
print(
|
||
f" 人臉 {i + 1}: 位置 [{x},{y},{w},{h}], 置信度 {confidence:.1%}, 年齡 {int(age) if age else '未知'}"
|
||
)
|
||
|
||
# 添加標題
|
||
title = f"女性最多的畫面 - 幀 {frame_number} - {len(female_faces)} 個女性"
|
||
title_size, _ = cv2.getTextSize(title, cv2.FONT_HERSHEY_SIMPLEX, 1.2, 3)
|
||
|
||
# 繪製標題背景
|
||
cv2.rectangle(
|
||
marked_image,
|
||
(10, 10),
|
||
(10 + title_size[0] + 20, 10 + title_size[1] + 20),
|
||
(0, 0, 0), # 黑色背景
|
||
-1,
|
||
)
|
||
|
||
# 繪製標題
|
||
cv2.putText(
|
||
marked_image,
|
||
title,
|
||
(20, 20 + title_size[1]),
|
||
cv2.FONT_HERSHEY_SIMPLEX,
|
||
1.2,
|
||
(255, 255, 255), # 白色文字
|
||
3,
|
||
)
|
||
|
||
# 添加時間戳信息
|
||
timestamp = frame_number / 59.94 # 假設 59.94 FPS
|
||
minutes = int(timestamp // 60)
|
||
seconds = int(timestamp % 60)
|
||
time_info = f"時間: {minutes:02d}:{seconds:02d}"
|
||
|
||
cv2.putText(
|
||
marked_image,
|
||
time_info,
|
||
(20, 60 + title_size[1]),
|
||
cv2.FONT_HERSHEY_SIMPLEX,
|
||
0.8,
|
||
(200, 200, 200), # 淺灰色
|
||
2,
|
||
)
|
||
|
||
# 保存標記後的圖像
|
||
output_path = os.path.join(output_dir, f"female_faces_frame_{frame_number}.jpg")
|
||
cv2.imwrite(output_path, marked_image)
|
||
|
||
print(f"✅ 已保存標記圖像: {output_path}")
|
||
|
||
# 創建縮略圖(便於查看)
|
||
height, width = marked_image.shape[:2]
|
||
scale = 800 / width
|
||
thumbnail = cv2.resize(marked_image, (800, int(height * scale)))
|
||
thumbnail_path = os.path.join(
|
||
output_dir, f"female_faces_frame_{frame_number}_thumbnail.jpg"
|
||
)
|
||
cv2.imwrite(thumbnail_path, thumbnail)
|
||
|
||
print(f"✅ 已保存縮略圖: {thumbnail_path}")
|
||
|
||
return {
|
||
"original_image": image_path,
|
||
"marked_image": output_path,
|
||
"thumbnail": thumbnail_path,
|
||
"frame_number": frame_number,
|
||
"timestamp_seconds": timestamp,
|
||
"timestamp_formatted": f"{minutes:02d}:{seconds:02d}",
|
||
"female_count": len(female_faces),
|
||
"female_faces": [
|
||
{
|
||
"index": i + 1,
|
||
"x": int(x),
|
||
"y": int(y),
|
||
"width": int(w),
|
||
"height": int(h),
|
||
"confidence": float(confidence),
|
||
"age": int(age) if age else None,
|
||
}
|
||
for i, (x, y, w, h, confidence, age) in enumerate(female_faces)
|
||
],
|
||
}
|
||
|
||
|
||
def create_female_faces_report(female_frames_info, output_dir="/tmp/female_faces"):
|
||
"""創建女性人臉報告"""
|
||
|
||
report_path = os.path.join(output_dir, "female_faces_report.md")
|
||
|
||
with open(report_path, "w", encoding="utf-8") as f:
|
||
f.write("# 女性人臉分析報告\n\n")
|
||
f.write(f"生成時間: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
|
||
|
||
f.write("## 📊 統計摘要\n\n")
|
||
total_females = sum(info["female_count"] for info in female_frames_info)
|
||
f.write(f"- **總女性人臉數**: {total_females}\n")
|
||
f.write(f"- **分析畫面數**: {len(female_frames_info)}\n")
|
||
f.write(
|
||
f"- **女性最多畫面**: {max(female_frames_info, key=lambda x: x['female_count'])['female_count']} 個女性\n\n"
|
||
)
|
||
|
||
f.write("## 🖼️ 女性最多的畫面\n\n")
|
||
|
||
for info in female_frames_info:
|
||
if info["female_count"] >= 2: # 只顯示有2個或以上女性的畫面
|
||
f.write(
|
||
f"### 幀 {info['frame_number']} - {info['timestamp_formatted']}\n\n"
|
||
)
|
||
f.write(f"- **女性數量**: {info['female_count']} 人\n")
|
||
f.write(
|
||
f"- **時間位置**: {info['timestamp_formatted']} ({info['timestamp_seconds']:.1f}秒)\n"
|
||
)
|
||
f.write(f"- **標記圖像**: `{os.path.basename(info['marked_image'])}`\n")
|
||
f.write(f"- **縮略圖**: `{os.path.basename(info['thumbnail'])}`\n\n")
|
||
|
||
f.write("#### 人臉詳細信息\n\n")
|
||
f.write("| 編號 | 位置 (x,y,w,h) | 置信度 | 年齡 |\n")
|
||
f.write("|------|----------------|--------|------|\n")
|
||
|
||
for face in info["female_faces"]:
|
||
position = (
|
||
f"{face['x']},{face['y']},{face['width']},{face['height']}"
|
||
)
|
||
confidence = f"{face['confidence']:.1%}"
|
||
age = str(face["age"]) if face["age"] else "未知"
|
||
f.write(
|
||
f"| {face['index']} | {position} | {confidence} | {age} |\n"
|
||
)
|
||
|
||
f.write("\n")
|
||
|
||
# 添加圖像引用
|
||
f.write(f"})\n\n")
|
||
f.write(
|
||
f"*圖像大小: 原始 {os.path.getsize(info['original_image']):,} bytes, 標記 {os.path.getsize(info['marked_image']):,} bytes*\n\n"
|
||
)
|
||
|
||
f.write("## 📁 生成文件\n\n")
|
||
f.write("以下文件已生成:\n\n")
|
||
|
||
for info in female_frames_info:
|
||
if info["female_count"] >= 2:
|
||
f.write(
|
||
f"- `{os.path.basename(info['marked_image'])}` - 標記女性人臉的完整圖像\n"
|
||
)
|
||
f.write(
|
||
f"- `{os.path.basename(info['thumbnail'])}` - 縮略圖(800px寬)\n"
|
||
)
|
||
|
||
f.write(f"- `female_faces_report.md` - 本報告文件\n\n")
|
||
|
||
f.write("## 🔍 分析說明\n\n")
|
||
f.write("1. **邊界框顏色**: 粉色 (RGB: 255,105,180) 表示女性人臉\n")
|
||
f.write("2. **標籤格式**: `女 [編號] ([年齡]歲) [置信度]`\n")
|
||
f.write("3. **置信度**: 人臉檢測的準確度,越高越好\n")
|
||
f.write("4. **年齡**: 基於深度學習模型的估計,可能有±5歲誤差\n")
|
||
f.write("5. **時間位置**: 從視頻開始計算的時間\n\n")
|
||
|
||
f.write("## 🎬 視頻內容分析\n\n")
|
||
|
||
# 根據女性分布推測視頻內容
|
||
multi_female_frames = [
|
||
info for info in female_frames_info if info["female_count"] >= 2
|
||
]
|
||
|
||
if multi_female_frames:
|
||
f.write("根據女性人臉分布,視頻可能包含:\n\n")
|
||
f.write("1. **社交場合**: 多個女性同時出現,可能是聚會或社交活動\n")
|
||
f.write("2. **對話場景**: 女性之間的對話或互動\n")
|
||
f.write("3. **群體鏡頭**: 包含多個女性的群體畫面\n")
|
||
f.write(
|
||
f"4. **女性主導場景**: 在 {len(multi_female_frames)} 個畫面中有2個或以上女性\n"
|
||
)
|
||
else:
|
||
f.write("視頻中女性主要單獨出現,可能包含:\n\n")
|
||
f.write("1. **單人鏡頭**: 女性單獨出現的特寫\n")
|
||
f.write("2. **分散場景**: 女性分散在不同的畫面中\n")
|
||
f.write("3. **配角角色**: 女性可能不是主要角色\n")
|
||
|
||
print(f"✅ 報告已生成: {report_path}")
|
||
return report_path
|
||
|
||
|
||
def main():
|
||
print("=" * 70)
|
||
print("提取女性最多的畫面")
|
||
print("=" * 70)
|
||
|
||
# 輸出目錄
|
||
output_dir = "/tmp/female_faces"
|
||
|
||
# 找到女性最多的幾個畫面
|
||
female_frames = [
|
||
19778, # 3個女性(最多)
|
||
17980, # 2個女性
|
||
62930, # 2個女性
|
||
66526, # 2個女性
|
||
70122, # 2個女性
|
||
71920, # 2個女性
|
||
]
|
||
|
||
print(f"分析以下幀的女性人臉: {female_frames}")
|
||
print()
|
||
|
||
female_frames_info = []
|
||
|
||
for frame_number in female_frames:
|
||
image_path = (
|
||
f"/tmp/face_analysis_results/384b0ff44aaaa1f1_frame_{frame_number:06d}.jpg"
|
||
)
|
||
|
||
if os.path.exists(image_path):
|
||
print(f"處理幀 {frame_number}...")
|
||
info = draw_female_faces(image_path, frame_number, output_dir)
|
||
if info:
|
||
female_frames_info.append(info)
|
||
print()
|
||
else:
|
||
print(f"❌ 圖像文件不存在: {image_path}")
|
||
|
||
if female_frames_info:
|
||
# 創建報告
|
||
report_path = create_female_faces_report(female_frames_info, output_dir)
|
||
|
||
print("=" * 70)
|
||
print("✅ 提取完成!")
|
||
print("=" * 70)
|
||
|
||
# 顯示摘要
|
||
max_females = max(info["female_count"] for info in female_frames_info)
|
||
max_frame_info = [
|
||
info for info in female_frames_info if info["female_count"] == max_females
|
||
][0]
|
||
|
||
print(f"📊 統計摘要:")
|
||
print(f" - 總分析畫面: {len(female_frames_info)}")
|
||
print(f" - 女性最多畫面: 幀 {max_frame_info['frame_number']}")
|
||
print(f" - 女性數量: {max_females} 人")
|
||
print(f" - 時間位置: {max_frame_info['timestamp_formatted']}")
|
||
print()
|
||
|
||
print(f"📁 生成文件:")
|
||
print(f" - 標記圖像: {output_dir}/female_faces_frame_*.jpg")
|
||
print(f" - 縮略圖: {output_dir}/female_faces_frame_*_thumbnail.jpg")
|
||
print(f" - 分析報告: {report_path}")
|
||
print()
|
||
|
||
print(f"🔍 查看結果:")
|
||
print(f" ls -la {output_dir}/")
|
||
print(f" open {output_dir}/female_faces_report.md")
|
||
|
||
else:
|
||
print("❌ 未找到任何女性人臉畫面")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|