我来帮你设计这个家庭照片整理工具。这是一个结合创新创业思维和新媒体运营的实用项目,核心是通过技术赋能家庭记忆保存。
核心代码实现
-- 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
使用方法
- 准备照片:将待整理照片放入 "family_photos"文件夹
- 准备人脸库(可选):在 "known_faces"文件夹放入家庭成员单人照,命名为姓名
- 运行程序: "python main.py"
- 查看结果:在 "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美化、云端同步等高级功能! 关注我,有更多实用程序等着你!