diff --git a/face_features.py b/face_features.py index b93165e..d3bf0a0 100644 --- a/face_features.py +++ b/face_features.py @@ -116,7 +116,7 @@ def _resolve_image_bytes(image_bytes: bytes = None, image_url: str = None) -> by def _local_face_shape(image_bytes: bytes = None, image_url: str = None) -> str: - """MediaPipe 脸型分类,返回 display(含混合脸型描述)或主脸型。""" + """MediaPipe 脸型分类,只返回主脸型(7 类之一),不含混合描述。""" import cv2 import numpy as np from face.face_shape_classifier import classify_from_image @@ -126,11 +126,12 @@ def _local_face_shape(image_bytes: bytes = None, image_url: str = None) -> str: if bgr is None: raise ValueError("图片格式不支持,无法解码") result = classify_from_image(bgr, return_details=True, return_annotated=False) - shape = result.get("display") or result["face_shape"] + shape = result["face_shape"] logger.info( - "local face_shape=%s conf=%.3f", + "local face_shape=%s conf=%.3f display=%s", shape, float(result.get("confidence") or 0), + result.get("display") or shape, ) return shape diff --git a/scripts/organize_annotations_by_day.py b/scripts/organize_annotations_by_day.py new file mode 100644 index 0000000..f39ab39 --- /dev/null +++ b/scripts/organize_annotations_by_day.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +"""把 static/annotations 根目录下的图片按创建时间(天)归入 YYYY-MM-DD 子目录。 + +用法: + python3 scripts/organize_annotations_by_day.py # 实际移动 + python3 scripts/organize_annotations_by_day.py --dry-run # 只预览 + python3 scripts/organize_annotations_by_day.py --dir /path # 指定目录 +""" +from __future__ import annotations + +import argparse +import os +import shutil +import sys +from collections import Counter +from datetime import datetime +from pathlib import Path + +IMAGE_EXTS = {".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp"} + + +def file_day(path: Path) -> str: + """取文件创建日(优先 birth time,否则 mtime),本地时区 YYYY-MM-DD。""" + st = path.stat() + ts = getattr(st, "st_birthtime", None) or st.st_mtime + return datetime.fromtimestamp(ts).strftime("%Y-%m-%d") + + +def main() -> int: + repo = Path(__file__).resolve().parent.parent + parser = argparse.ArgumentParser(description="按天整理 annotations 图片") + parser.add_argument( + "--dir", + type=Path, + default=repo / "static" / "annotations", + help="annotations 目录(默认 static/annotations)", + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="只打印将要执行的操作,不实际移动", + ) + args = parser.parse_args() + root: Path = args.dir.resolve() + + if not root.is_dir(): + print(f"目录不存在: {root}", file=sys.stderr) + return 1 + + files = [ + p + for p in root.iterdir() + if p.is_file() and p.suffix.lower() in IMAGE_EXTS + ] + if not files: + print(f"根目录没有待整理图片: {root}") + return 0 + + by_day: Counter[str] = Counter() + moved = 0 + skipped = 0 + + for src in sorted(files): + day = file_day(src) + dest_dir = root / day + dest = dest_dir / src.name + by_day[day] += 1 + + if dest.exists(): + print(f"SKIP 目标已存在: {dest.relative_to(root)}") + skipped += 1 + continue + + if args.dry_run: + print(f"MOVE {src.name} -> {day}/") + else: + dest_dir.mkdir(parents=True, exist_ok=True) + shutil.move(str(src), str(dest)) + moved += 1 + + print("---") + print(f"目录: {root}") + print(f"{'预览' if args.dry_run else '完成'}: {moved} 个文件" + + (f", 跳过 {skipped}" if skipped else "")) + for day in sorted(by_day): + print(f" {day}: {by_day[day]}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())