设计家庭照片整理工具,支持时间,地点,人物分类,自动生成相册,帮助家庭保存回忆。

29 阅读5分钟

我来帮你设计这个家庭照片整理工具。这是一个结合创新创业思维和新媒体运营的实用项目,核心是通过技术赋能家庭记忆保存。

核心代码实现

-- coding: utf-8 --

""" 家庭照片整理工具 v1.0 支持时间、地点、人物智能分类,自动生成数字相册 """

import os import shutil import exifread from PIL import Image, ImageDraw, ImageFont import face_recognition from geopy.geocoders import Nominatim from datetime import datetime import json import logging

配置日志

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(name)

class MetadataExtractor: """元数据提取模块:负责从图片中提取时间、地点、人物信息"""

def __init__(self):
    self.geolocator = Nominatim(user_agent="family_photo_organizer")
    self.known_face_encodings = {}
    self.known_face_names = {}

def extract_datetime(self, image_path):
    """提取拍摄时间"""
    try:
        with open(image_path, 'rb') as f:
            tags = exifread.process_file(f)
            if 'EXIF DateTimeOriginal' in tags:
                date_str = str(tags['EXIF DateTimeOriginal'])
                return datetime.strptime(date_str, '%Y:%m:%d %H:%M:%S')
    except Exception as e:
        logger.warning(f"无法提取时间信息 {image_path}: {e}")
    return None

def extract_location(self, image_path):
    """提取地理位置信息"""
    try:
        with open(image_path, 'rb') as f:
            tags = exifread.process_file(f)
            if 'GPS GPSLatitude' in tags and 'GPS GPSLongitude' in tags:
                lat_ref = str(tags.get('GPS GPSLatitudeRef', 'N'))
                lon_ref = str(tags.get('GPS GPSLongitudeRef', 'E'))
                
                lat = self._convert_to_degrees(tags['GPS GPSLatitude'])
                lon = self._convert_to_degrees(tags['GPS GPSLongitude'])
                
                if lat_ref == 'S': lat = -lat
                if lon_ref == 'W': lon = -lon
                
                location = self.geolocator.reverse((lat, lon), language='zh')
                return location.address if location else f"{lat:.4f},{lon:.4f}"
    except Exception as e:
        logger.warning(f"无法提取位置信息 {image_path}: {e}")
    return "未知地点"

def _convert_to_degrees(self, value):
    """转换GPS坐标为度数"""
    d = float(value.values[0].num) / float(value.values[0].den)
    m = float(value.values[1].num) / float(value.values[1].den)
    s = float(value.values[2].num) / float(value.values[2].den)
    return d + (m / 60.0) + (s / 3600.0)

def extract_faces(self, image_path, known_faces_dir=None):
    """识别图片中的人脸"""
    try:
        image = face_recognition.load_image_file(image_path)
        face_locations = face_recognition.face_locations(image)
        face_encodings = face_recognition.face_encodings(image, face_locations)
        
        # 加载已知人脸
        if known_faces_dir and os.path.exists(known_faces_dir):
            for filename in os.listdir(known_faces_dir):
                if filename.lower().endswith(('.jpg', '.jpeg', '.png')):
                    name = os.path.splitext(filename)[0]
                    known_image = face_recognition.load_image_file(os.path.join(known_faces_dir, filename))
                    encodings = face_recognition.face_encodings(known_image)
                    if encodings:
                        self.known_face_encodings[name] = encodings[0]
        
        # 识别人脸
        recognized_names = []
        for face_encoding in face_encodings:
            matches = face_recognition.compare_faces(
                list(self.known_face_encodings.values()), face_encoding
            )
            if True in matches:
                matched_name = list(self.known_face_encodings.keys())[matches.index(True)]
                recognized_names.append(matched_name)
        
        return recognized_names if recognized_names else ["未知人物"]
    except Exception as e:
        logger.warning(f"人脸识别失败 {image_path}: {e}")
        return ["识别失败"]

class PhotoClassifier: """照片分类管理模块"""

def __init__(self, output_base_dir="organized_photos"):
    self.output_base_dir = output_base_dir
    os.makedirs(output_base_dir, exist_ok=True)

def classify_by_time(self, image_path, datetime_obj):
    """按时间分类:年/月"""
    if datetime_obj:
        year = datetime_obj.strftime("%Y")
        month = datetime_obj.strftime("%Y-%m")
        time_dir = os.path.join(self.output_base_dir, "按时间", year, month)
    else:
        time_dir = os.path.join(self.output_base_dir, "按时间", "未知时间")
    os.makedirs(time_dir, exist_ok=True)
    return time_dir

def classify_by_location(self, image_path, location):
    """按地点分类"""
    # 简化处理:提取城市名
    city = "未知城市"
    if "省" in location and "市" in location:
        parts = location.split("省")
        if len(parts) > 1:
            city_part = parts[1].split("市")[0] + "市"
            city = city_part
    elif "市" in location:
        city = location.split("市")[0] + "市"
    
    location_dir = os.path.join(self.output_base_dir, "按地点", city)
    os.makedirs(location_dir, exist_ok=True)
    return location_dir

def classify_by_person(self, image_path, person_names):
    """按人物分类"""
    person_dirs = []
    for name in person_names:
        person_dir = os.path.join(self.output_base_dir, "按人物", name)
        os.makedirs(person_dir, exist_ok=True)
        person_dirs.append(person_dir)
    return person_dirs

class AlbumGenerator: """相册生成模块"""

def __init__(self, output_dir="organized_photos"):
    self.output_dir = output_dir

def generate_html_album(self, category_type, category_name):
    """生成HTML相册"""
    category_path = os.path.join(self.output_dir, f"按{category_type}", category_name)
    if not os.path.exists(category_path):
        return
    
    html_content = f"""<!DOCTYPE html>
{category_name} - 家庭相册 body {{ font-family: Arial, sans-serif; margin: 20px; }} .photo-grid {{ display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 15px; }} .photo-item {{ text-align: center; }} .photo-item img {{ width: 100%; height: 150px; object-fit: cover; border-radius: 8px; }} h1 {{ color: #333; text-align: center; }}

{category_name}

"""
    for filename in os.listdir(category_path):
        if filename.lower().endswith(('.jpg', '.jpeg', '.png')):
            html_content += f"""
    <div class="photo-item">
        <img src="{filename}" alt="{filename}" onclick="window.open('{filename}', '_blank')">
        <p>{filename}</p>
    </div>"""
    
    html_content += """
</div>
"""
    with open(os.path.join(category_path, "index.html"), 'w', encoding='utf-8') as f:
        f.write(html_content)

class FamilyPhotoOrganizer: """主程序类:整合各模块功能"""

def __init__(self, input_dir, output_dir="organized_photos", known_faces_dir=None):
    self.input_dir = input_dir
    self.metadata_extractor = MetadataExtractor()
    self.classifier = PhotoClassifier(output_dir)
    self.album_generator = AlbumGenerator(output_dir)
    self.known_faces_dir = known_faces_dir
    
    # 配置已知人脸目录
    if known_faces_dir:
        self.metadata_extractor.known_face_encodings = self._load_known_faces(known_faces_dir)

def _load_known_faces(self, faces_dir):
    """加载已知人脸编码"""
    known_encodings = {}
    for filename in os.listdir(faces_dir):
        if filename.lower().endswith(('.jpg', '.jpeg', '.png')):
            name = os.path.splitext(filename)[0]
            try:
                image = face_recognition.load_image_file(os.path.join(faces_dir, filename))
                encodings = face_recognition.face_encodings(image)
                if encodings:
                    known_encodings[name] = encodings[0]
            except Exception as e:
                logger.warning(f"加载人脸失败 {filename}: {e}")
    return known_encodings

def process_photos(self):
    """处理所有照片"""
    if not os.path.exists(self.input_dir):
        logger.error(f"输入目录不存在: {self.input_dir}")
        return
    
    photo_files = [f for f in os.listdir(self.input_dir) 
                  if f.lower().endswith(('.jpg', '.jpeg', '.png'))]
    
    for filename in photo_files:
        image_path = os.path.join(self.input_dir, filename)
        logger.info(f"处理照片: {filename}")
        
        # 提取元数据
        datetime_obj = self.metadata_extractor.extract_datetime(image_path)
        location = self.metadata_extractor.extract_location(image_path)
        persons = self.metadata_extractor.extract_faces(image_path, self.known_faces_dir)
        
        # 分类存储
        time_dir = self.classifier.classify_by_time(image_path, datetime_obj)
        location_dir = self.classifier.classify_by_location(image_path, location)
        person_dirs = self.classifier.classify_by_person(image_path, persons)
        
        # 复制到各分类目录
        shutil.copy2(image_path, os.path.join(time_dir, filename))
        shutil.copy2(image_path, os.path.join(location_dir, filename))
        for person_dir in person_dirs:
            shutil.copy2(image_path, os.path.join(person_dir, filename))
        
        logger.info(f"完成分类: 时间={datetime_obj}, 地点={location}, 人物={persons}")
    
    # 生成相册
    self._generate_all_albums()
    logger.info("所有照片处理完成!")

def _generate_all_albums(self):
    """生成所有分类的相册"""
    categories = ["时间", "地点", "人物"]
    for category in categories:
        category_path = os.path.join(self.classifier.output_base_dir, f"按{category}")
        if os.path.exists(category_path):
            for item in os.listdir(category_path):
                item_path = os.path.join(category_path, item)
                if os.path.isdir(item_path):
                    self.album_generator.generate_html_album(category, item)

def main(): """主函数示例""" # 配置路径 input_directory = "family_photos" # 原始照片目录 output_directory = "organized_family_photos" # 整理后输出目录 known_faces_directory = "known_faces" # 已知人脸照片目录(可选)

# 创建整理器实例
organizer = FamilyPhotoOrganizer(
    input_dir=input_directory,
    output_dir=output_directory,
    known_faces_dir=known_faces_directory
)

# 开始处理
organizer.process_photos()

if name == "main": main()

README文件

家庭照片整理工具

项目简介

智能整理家庭照片,支持时间、地点、人物自动分类,生成数字相册,让珍贵回忆井井有条。

核心功能

  • 📅 按拍摄时间智能分类(年/月)
  • 🌍 按地理位置自动分组
  • 👥 人脸识别技术标记家庭成员
  • 📸 一键生成HTML数字相册
  • 🗂️ 模块化设计,易于扩展维护

安装指南

克隆项目

git clone github.com/your-repo/f… cd family-photo-organizer

安装依赖

pip install exifread pillow face_recognition geopy jinja2

使用方法

  1. 准备照片:将待整理照片放入 "family_photos"文件夹
  2. 准备人脸库(可选):在 "known_faces"文件夹放入家庭成员单人照,命名为姓名
  3. 运行程序: "python main.py"
  4. 查看结果:在 "organized_family_photos"文件夹查看分类结果

目录结构

├── main.py # 主程序入口 ├── photo_organizer/ │ ├── init.py │ ├── metadata_extractor.py # 元数据提取 │ ├── classifier.py # 分类管理 │ └── album_generator.py # 相册生成 ├── family_photos/ # 原始照片 ├── known_faces/ # 已知人脸库 └── organized_family_photos/ # 整理结果

核心知识点卡片

📚 EXIF元数据解析

  • 知识点:读取照片内置的拍摄时间、GPS坐标等信息
  • 应用:自动按时间地点分类,无需手动标记
  • 创新点:挖掘照片"隐藏信息",提升整理效率

🧠 人脸识别技术

  • 知识点:使用face_recognition库进行人脸检测与识别
  • 应用:自动识别家庭成员,建立人物相册集
  • 新媒体价值:个性化内容推送,增强用户粘性

🌐 地理编码服务

  • 知识点:通过GPS坐标反向解析具体地址
  • 应用:按城市地区自动分组照片
  • 创业机会:结合旅游营销,生成"足迹地图"

🏗️ 模块化架构设计

  • 知识点:将系统拆分为独立功能模块
  • 优势:便于维护升级,支持功能扩展
  • 商业思维:标准化模块可复用到其他整理场景

📱 新媒体运营思维

  • 核心理念:技术赋能情感连接,数字化保存珍贵回忆
  • 变现模式:SaaS服务、定制相册、回忆分享社区
  • 用户价值:降低记忆整理成本,提升家庭幸福感

这个项目完美结合了技术创新与人文关怀,通过Python自动化处理让家庭记忆管理变得简单有趣。你可以先从基础版本开始,逐步添加AI美化、云端同步等高级功能! 关注我,有更多实用程序等着你!