# pyheic-struct

`pyheic-struct` 诞生的初衷，是为了解决我个人在迁移照片时遇到的需求：把三星手机拍摄的动态照片（Motion Photo）批量转换成 iOS/macOS 可以直接识别、导入的 Live Photo。  
工具会自动拆分并重建 HEIC 与 MOV 配对文件，补齐 Apple 生态所必需的 `ContentIdentifier`、`PhotoIdentifier` 与 MakerNote 元数据，让 macOS「照片」或 iOS 设备将它们视为同一张 Live Photo，而不是互不关联的静态图与视频。

> Looking for an English version? Please see [README.en.md](README.en.md). *(本 README 以中文为主，当需要英文说明时可跳转到该文件。)*

同时，转换流程也促使我编写了一套可复用的 HEIF/HEIC 结构化解析、诊断与重建工具：它能读出盒结构、修复三星特有的 shifted item ID、重算 `iloc` 偏移，并以可预测的方式重新写回文件。即便你暂时不需要完整的 Live Photo 转换，也能够将其中的模块用在其它 HEIC 调试或自定义处理流程中。

---

## 功能亮点

- **三星 Motion Photo → Apple Live Photo**：自动生成匹配的 HEIC 与 MOV，并写入统一的标识符；导入后即是一张可播放的 Live Photo。
- **完整元数据补齐**：在 MOV 中注入 `ContentIdentifier`，在 HEIC 中写入 MakerNote / PhotoIdentifier 等 Apple 生态所需字段。
- **HEIC 深层解析能力**：提供 `HEICFile`、`HEICBuilder` 等类，可读取 `ftyp`、`meta`、`iloc`、`iinf`、`iprp`、`iref` 等关键盒，适用于诊断和自定义修复。
- **安全重建机制**：`HEICBuilder` 会自动重算偏移量与引用关系，避免手工编辑造成的结构损坏。
- **CLI 与 Python API 并存**：既能在命令行一键转换，也可以在脚本、流水线甚至 GUI 中复用底层逻辑。
- **真实样例随仓库提供**：包含原始三星 HEIC、转换后的 Apple 兼容文件与苹果原生样例，便于对照调试。

---

## 组件概览

| 组件 | 作用 |
| --- | --- |
| `scripts/samsung_live_photo.py` | 命令行转换脚本，输出苹果兼容的 HEIC + MOV |
| `pyheic_struct.HEICFile` | 解析 HEIC 盒结构、重建主图、提取 Motion Photo 等 |
| `pyheic_struct.HEICBuilder` | 在修改元数据后重新写出 HEIC，确保偏移正确 |
| `pyheic_struct.convert_motion_photo` | 通用转换入口，可指定厂商与目标适配器 |
| `pyheic_struct.AppleTargetAdapter` | Apple 生态适配器，负责写入品牌、MakerNote、ContentIdentifier 等 |
| `pyheic_struct.handlers.SamsungHandler` | 三星专用处理逻辑：识别 `mpvd`、校正 shifted IDs |
| `inspect_heic.py` | 快速巡检脚本，输出盒结构、偏移与引用信息 |

---

## 安装与环境

- **Python 3.10+**
- 必装依赖：`Pillow>=10.0.0`、`pillow-heif>=0.15.0`
- 新增依赖：`piexif>=1.1.3`（用于 MakerNote 写入）
- 可选依赖：`exiftool-wrapper>=0.5.0`
- 推荐系统安装 [ExifTool](https://exiftool.org/)
  - macOS: `brew install exiftool`
  - Ubuntu/Debian: `sudo apt install libimage-exiftool-perl`
  - Windows: 下载官方 exe 并加入 `PATH`

安装方式：

```bash
# 仓库根目录安装
pip install .

# 或开发模式（含可选依赖）
pip install -e .[full]
```

如仅需解析能力，可忽略 `.[full]`；若要写入 MOV 的 `ContentIdentifier`，请启用 `full` 并确保系统可调用 `exiftool`。

---

## 快速开始：把三星动态照片变成 Live Photo

### 1. 准备样例

仓库中已经附带常用测试素材：

- `samsung.heic`：原始三星 Motion Photo
- `samsung_apple_compatible.HEIC` / `.MOV`：转换后的示例输出
- `apple.HEIC`：来自苹果的原生 Live Photo 参考

### 2. 命令行一键转换

```bash
python3 scripts/samsung_live_photo.py samsung.heic \
  --output-dir output/live
```

生成的 `output/live/samsung_apple_compatible.heic` 与 `.mov` 拥有同一个 `ContentIdentifier` 和 `PhotoIdentifier`，可直接导入 macOS「照片」验证。

常用参数：

| 参数 | 说明 |
| ---- | ---- |
| `source` | 必选，原始三星 HEIC |
| `--output-dir` | 输出目录，默认与源文件相同路径 |
| `--heic-name` / `--mov-name` | 输出文件名（不含路径），默认 `*_apple_compatible` |
| `--skip-mov-tag` | 无 `exiftool` 时跳过 MOV 的 `ContentIdentifier` 注入 |

### 3. 通过 API 集成

```python
from pathlib import Path
from pyheic_struct import convert_samsung_motion_photo, HEICFile, HEICBuilder

heic_path, mov_path = convert_samsung_motion_photo(
    "samsung.heic",
    output_still=Path("converted/apple_ready.HEIC"),
    output_video=Path("converted/apple_ready.MOV"),
)

# 读取重建后的 HEIC，执行进一步操作
rebuilt = HEICFile(str(heic_path))
rebuilt.set_content_identifier("MY-INTERNAL-ID")
HEICBuilder(rebuilt).write("converted/customized.HEIC")
```

---

## Live Photo 转换管线解读

1. **解析原始 HEIC**：读取 `ftyp`、`meta`、`iloc` 等盒；通过 handler 自动识别三星文件并查找 `mpvd` 盒。
2. **重建主图像**：借助 `pillow-heif` 还原三星的网格化主图，写入临时平面 HEIC（`mif1` brand）。
3. **提取并存储 MOV**：抽取 `mpvd` 中的嵌入视频，写成独立 MOV，并用 `exiftool` 写入 `ContentIdentifier`（如未安装可跳过）。
4. **修正 item ID 与引用**：三星会将 item ID 左移 16 位；工具会同步修复 `iinf`、`ipma`、`iref`。
5. **补齐 Apple 元数据**：设置苹果兼容的 `ftyp` 品牌，写入新的 `ContentIdentifier`、`PhotoIdentifier`，填充 MakerNote。
6. **最终重建**：`HEICBuilder` 重新计算 `iloc` 偏移、盒大小并写回，释放临时文件。

了解以上步骤，有助于调试其它厂商的 Motion Photo，或在需要时定制新的 TargetAdapter。

---

## 核心 API 快速索引

- `pyheic_struct.convert_motion_photo(...)`：通用转换接口，可指定 `vendor_hint`（如 `"samsung"`）与 `TargetAdapter`。
- `pyheic_struct.convert_samsung_motion_photo(...)`：三星专用封装，调用更简洁。
- `pyheic_struct.HEICFile`：
  - `get_primary_item_id()`、`reconstruct_primary_image()`、`list_items()` 等解析方法
  - `set_content_identifier()`、`set_exif_maker_note()` 等元数据操作
- `pyheic_struct.HEICBuilder`：将修改后的 `HEICFile` 安全写回磁盘。
- `pyheic_struct.handlers.VendorHandler`：厂商自定义扩展基类。
- `pyheic_struct.targets.TargetAdapter` / `AppleTargetAdapter`：目标生态适配接口。

---

## 扩展与自定义

1. **实现新的厂商 Handler**：继承 `VendorHandler`，实现 `matches`、`extract_motion_video`、`prepare_flat_heic` 等方法，并注册到 `HANDLER_REGISTRY`。
2. **接入其它目标生态**：继承 `TargetAdapter`，在 `apply_to_flat_heic` / `post_process_mov` 中写入所需元数据或外部脚本。
3. **诊断 HEIC 文件**：使用 `inspect_heic.py` 或直接调 `HEICFile` API，输出盒结构、偏移与引用，便于对比不同厂商实现。

---

## 常见问题

- **导入照片后仍显示两个文件？**  
  请确认系统安装了 `exiftool`，并在转换时未使用 `--skip-mov-tag`。同时建议使用工具生成的最新 HEIC/MOV，避免手动修改导致标识符不匹配。

- **能否支持其它厂商的 Motion Photo？**  
  可以。通过实现新的 handler 并组合自定义 target adapter，即可重用现有的重建管线。

- **英文说明在哪里？**  
  参见 [README.en.md](README.en.md)；若你想贡献改进英文文档，也欢迎提交 PR。

---

## 开发提示

- 项目使用 `ruff` / `black` 风格指南，可根据需要自行配置。
- 在修改 HEIC 结构后务必重新运行转换脚本，确保 `HEICBuilder` 输出的文件能被 `pillow-heif` 与 `exiftool` 读取。
- 欢迎围绕 HEIC 解析、Motion Photo 适配提交 issue 或 PR，特别是其它安卓厂商的兼容性案例。
