家庭相册整理 Agent 开工:从 20000 张杂乱照片中打捞回忆

每到九月初,家里的手机相册总会堆满令人绝望的数字废墟。
老爸手机里存了八千多张照片,绝大部分是重复拍摄的花花草草、手抖拍糊的风景、微信群里随手保存的养生表情包、以及同一角度连按了十几下的模糊连拍;老妈手机里更是堆满了买菜截图、超市打折海报、以及几千张随手拍的家常菜;而我自己硬盘里还散落着近五年单反拍摄的无损 RAW 格式原片。
三部手机加一块,足足有超过两万张未经整理的杂乱图片。过去老妈想找一张"去年春节全家在老家大门口的合影",要在手机相册里翻划二十分钟,眼睛看花也找不到,最后只能作罢。
数字化让人类拍照的边际成本归零,却让回忆的检索成本成倍飙升。
在第二周的开发计划中,我正式立项并启动了**家庭私有相册整理 Agent(Family Photo Archivist Agent)**的研发,目标是用端侧轻量视觉模型与规则流水线,把两万张数字垃圾变成一本井井有条的家庭岁月画册。
flowchart TD
RawDump[全家 20,000+ 张杂乱照片资产] –> Ingestion[私有相册摄入管道 (Exif 提取 + 格式归一)]
Ingestion –> FilterPass[一阶轻量过滤: 截图/表情包/严重模糊连拍剔除]
FilterPass –> MLCluster[二阶多模态理解: 本地 CLIP 视觉特征打标 + 人脸聚类]
MLCluster –> StoryEngine[三阶回忆故事线编排: 按生活事件聚合 (如'2025春节老家团圆')]
StoryEngine –> Output[生成'今日时光卡片'并推送到客厅相框]
相册整理 Agent 的三大架构原则
- 全家人的面孔、私人生活场景是极其敏感的生物特征与隐私资产,所有多模态向量提取、人脸识别与聚类必须在家庭 NAS 或本地 GPU 算力盒上 100% 离线完成。
- 两万张照片的本地推理计算量极大,不能一次性把 NAS 跑死;必须作为低优先级的后台后台进程(nice -n 19),在夜间闲时平滑切片处理。
- 算法识别出的"重复模糊照",绝对不直接物理删除,而是统一移入"待归档废片箱",保留子女一键撤回通道。
基于 Python 的 Exif 解析与画质清晰度初筛
import os
import cv2
import exifread
from datetime import datetime
from dataclasses import dataclass
from typing import Optional, Dict
@dataclass
class PhotoMetadata:
file_path: str
capture_time: Optional[datetime]
camera_model: str
blur_score: float # 模糊度评分 (Laplacian 方差)
is_screenshot: bool
is_poor_quality: bool
class PhotoPreFilterPipeline:
def __init__(self, blur_threshold: float = 85.0):
self.blur_threshold = blur_threshold
def calculate_blur_score(self, image_path: str) -> float:
"""
利用拉普拉斯算子方差评估图像清晰度 (低于阈值判定为手抖严重模糊)
"""
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
if img is None:
return 0.0
# 缩小尺寸加速计算
small_img = cv2.resize(img, (512, 512))
variance = cv2.Laplacian(small_img, cv2.CV_64F).var()
return float(variance)
def extract_metadata_and_filter(self, image_path: str) -> PhotoMetadata:
blur = self.calculate_blur_score(image_path)
is_blurry = blur < self.blur_threshold
# 判定是否为屏幕截图 (根据命名或 Exif 缺失)
filename = os.path.basename(image_path).lower()
is_screenshot = any(k in filename for k in ["screenshot", "screen_shot", "截图", "snip"])
capture_time = None
camera = "Unknown"
try:
with open(image_path, 'rb') as f:
tags = exifread.process_file(f, stop_tag="EXIF DateTimeOriginal")
if "EXIF DateTimeOriginal" in tags:
time_str = str(tags["EXIF DateTimeOriginal"])
capture_time = datetime.strptime(time_str, "%Y:%m:%d %H:%M:%S")
if "Image Model" in tags:
camera = str(tags["Image Model"])
except Exception:
pass
return PhotoMetadata(
file_path=image_path,
capture_time=capture_time,
camera_model=camera,
blur_score=blur,
is_screenshot=is_screenshot,
is_poor_quality=is_blurry or is_screenshot
)
让数字垃圾变成闪光的时光
敲定相册 Agent 的第一步,不仅是在理清技术链路,更是在梳理一个家庭走过的岁月轨迹。
当杂乱的截图与模糊连拍被一层层剥离,那些真正记录着父母笑容、小狗奔跑与节日饭菜的生动瞬间,才得以从数字灰尘中重新熠熠生辉。
