moxuan — 照片查重清理工具(v0.1.0)
识别近重复照片(连拍、同场景短间隔、轻微构图调整)并推荐每组保留最优的一张。
- 一键清理流程:
scan(扫描→特征→聚类→评分)→groups审核 →quarantine隔离(可还原)→purge彻底删除 - 网页审核界面:分组浏览 / 大图对比 / 手动改标最优 / 批量移入隔离区
- 可配置:相似度阈值、时间窗、评分权重(config.toml + 网页设置面板)
- 支持 RAW(CR2/NEF/ARW/RAF/DNG):内嵌预览参与查重/评分,大图按需 demosaic(3200px 或全尺寸)
安装
任选一种,均无需手动激活虚拟环境;首次运行自动下载模型(DINOv2 权重约 90MB + 人脸/BRISQUE 模型,缓存在 ~/.cache/moxuan/):
# 方式一:uvx 即用即走(推荐,零安装)
uvx moxuan scan <照片目录> --threshold 0.88
# 方式二:uv tool 全局安装(一次安装,命令常驻 PATH)
uv tool install moxuan
# 方式三:pip 直接安装(常规 Python 环境)
pip install moxuan
- 人脸眼睛睁开检测默认不装,需要时用
[face]extra:uv tool install "moxuan[face]"(uvx 加--extra face) - 源码开发(改网页前端时需要):
git clone https://ifilevault.myasustor.com:3101/mohoutech/moxuan
cd moxuan
uv venv --python 3.13
uv pip install -e ".[face]"
使用(CLI)
uvx 用户把下面的 moxuan 换成 uvx moxuan:
moxuan models # (可选)预置模型,scan 会自动补齐
moxuan scan <照片目录> --threshold 0.88 # 一键全流程:扫描→特征→聚类→评分
moxuan groups # 查看分组
moxuan report --format html # 生成 HTML/CSV 审核报告
moxuan quarantine <group_id> # 确认组清理:保留最优,其余移入隔离区
moxuan restore <quarantine_id> # 还原隔离区照片
moxuan purge <quarantine_id> # 彻底删除(不可恢复)
moxuan restore --prune # 清理手动删除 quarantine/ 后的失效记录
--threshold 为相似度阈值(默认 0.88,见 config.toml)。
网页审核界面
moxuan serve start # 打开 http://localhost:9100(前端后端一体)
moxuan serve status # 查询状态
moxuan serve stop # 停止服务
安装版(pip/uvx)的网页界面已内置在 wheel,由后端同一端口(默认 9100)提供;开发模式则双进程:后端 9100 + 前端 Vite cd ui && npm run dev(5173,/api 代理到 9100;npm 缓存目录无写权限报 EPERM 时加 --cache 参数规避)。
- 工作台:顶部展示当前工作路径,输入照片目录后依次执行 扫描→特征→聚类→评分(实时进度);任务记录可单条删除或一键清空已完成;照片全库统一聚类/评分,换目录前先「重置数据」清空旧照片,避免多目录混在一起
- 审核:分组列表按 张数/最佳评分/拍摄时间 排序;网格滑块调尺寸(56–380px,默认 176px);悬停勾选可批量移入隔离区;点照片看大图(
←/→翻页、Esc关闭、可直接 设为最优/移出组),RAW 可「加载全尺寸」 - 清理区:还原或彻底删除(与审核页同套网格/大图)
- 设置:调整阈值/时间窗/评分权重
API
FastAPI REST 接口(api/routes.py):
POST /api/tasks/{scan|embed|cluster|score}、GET /api/tasks[/{id}]:任务执行与进度;DELETE /api/tasks[/{id}]:删除记录 / 清空全部已完成(运行中的不可删);DELETE /api/data:清空照片与分组(换目录重新开始)GET /api/groups、GET /api/photos?group_id=:分组与照片GET /api/thumbnail/{photo_id}、GET /api/original/{photo_id}:缩略图与原图(ETag/304 缓存);浏览器可解码格式直出原图,RAW 渲染缓存 JPEG(默认 3200px,?full=1全尺寸)PATCH /api/groups/{id}/best、POST /api/groups/{id}/{remove|restore}:设最优 / 移出组 / 移回组GET /api/quarantine、POST /api/quarantine/confirm、POST /api/quarantine/{id}/restore、DELETE /api/quarantine/{id}:隔离区GET|PATCH /api/settings:运行参数
.thumbnails/ 是可丢弃缓存(删后自动重建):缩略图 {id}.jpg;不可解码原图(如 TIFF)与 RAW 大图为 {id}_hires.jpg(3200px),RAW 全尺寸为 {id}_full.jpg。
配置
config.toml(项目根)是配置入口,环境变量 MOXUAN_CONFIG 可指定其他路径:
[service] host / port:网页服务监听地址与端口[algorithm] threshold / window:相似度阈值(默认 0.88)与时间窗(默认 300s)[algorithm.weights]:评分权重(清晰度/锐度/人脸/曝光/裁剪/对比度/动态范围/色彩/噪点/BRISQUE/EXIF)
网页「设置」面板的修改存数据库、优先于配置文件。安装版数据(用户 config、SQLite、日志、PID、隔离区)位于 Windows %APPDATA%\moxuan、Linux/macOS ~/.config/moxuan(MOXUAN_DATA 可重定向)。
算法
- 相似度:DINOv2 embedding + 时间窗分桶(默认 300s;无 EXIF 时间单独互比)+ 余弦阈值连通分量 + SSIM 边界复核
- 选优:清晰度 / 曝光 / 人脸(YuNet+眼睛睁开)/ BRISQUE / EXIF(分辨率/ISO/快门)加权评分
- 清理安全:确认后移入隔离区,可还原;
purge彻底删除
环境要求
- Python 3.11+(uv 自动下载所需版本)
- 首次运行联网下载模型(
moxuan models可预置);Python 访问 github.com 超时时自动改用 curl - 运行时依赖:torch、opencv-contrib-python-headless、numpy、rawpy、Pillow、click、fastapi、uvicorn;可选 mediapipe(
[face])
发布(PyPI)
python scripts/build_frontend.py # 构建前端到 api/web/(发布者需 node/npm)
uv build # 生成 sdist + wheel 到 dist/
uv publish # 需 UV_PUBLISH_TOKEN(PyPI API token)
测试
uv venv --python 3.13
uv pip install -e ".[dev]"
uv run pytest
Metadata
Release files for moxuan 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| moxuan-0.1.0.tar.gz | 3.8 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| moxuan-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 7.7 MB
Release files / moxuan-0.1.0.tar.gz
| Download URL | moxuan-0.1.0.tar.gz |
|---|---|
| Size | 3.8 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d36c7c36aab4a45b42e32a5f56c80852559dead5d53e0e9998fef1b5eab22dfb
|
|
BLAKE2b-256 checksum How to use checksums |
09cfd49836f35d1f4aba75f07af95b604046fae91e7fe8b8731332348dcf389e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.13 {"installer":{"name":"uv","version":"0.9.13"},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / moxuan-0.1.0-py3-none-any.whl
| Download URL | moxuan-0.1.0-py3-none-any.whl |
|---|---|
| Size | 3.8 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e858e93b289e05d8424789bf52c31279fa07f7fd066f64eaa8db8d4217b20c78
|
|
BLAKE2b-256 checksum How to use checksums |
0d8330fc5395b0f36c26a19d569b53a198359c7d0788d94c7985db381ab069d2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.9.13 {"installer":{"name":"uv","version":"0.9.13"},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|