# yudao-pose-ai **Repository Path**: ziyucoding/yudao-pose-ai ## Basic Information - **Project Name**: yudao-pose-ai - **Description**: 拍照姿势参考工具 - **Primary Language**: TypeScript - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-07-24 - **Last Updated**: 2026-07-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # YudaoPoseAI YudaoPoseAI 是一个中文拍照姿势参考工具。上传一张 JPG/PNG 场景图并选择姿势模板后,应用会生成叠加在原图上的半透明参考模特、五类中文动作指导,以及可下载的 PNG。 默认模式完全在本地完成 SVG/Canvas 合成,不需要 API Key;也可以显式接入兼容 OpenAI Images edits 的远程图像服务。远程服务不可用时,应用会自动降级为本地合成,并在界面中如实标明结果来源。 ![YudaoPoseAI 咖啡馆姿势参考效果](public/yudaoposeai-guide.png) ## 功能概览 - 支持拖放或选择 JPG、PNG 场景图,单张最大 10 MB。 - 内置 8 种站姿、坐姿、行走和舒展模板。 - 输出站位、身体朝向、手部动作、头部/视线、拍摄建议五类中文指导。 - 本地模式使用归一化关键点、SVG 预览和 Canvas PNG 导出,不依赖外部图像服务。 - 可选调用 OpenAI 兼容的 `POST /images/edits` 接口生成完整编辑图。 - 远程调用失败或缺少密钥时自动回退,并区分“AI 图像编辑”和“本地合成”。 - 下载的 PNG 沿用原图尺寸和宽高比。 - 项目本身不提供账号、支付、历史记录、数据库或持久化图片存储。 > 默认本地模式不会分析图片中的物体或场景语义。它根据原图比例和用户选择的模板生成确定性的姿势关键点;需要模型参与图像编辑时,请启用远程 provider。 ## 快速开始 ### 环境要求 - Node.js 20 - npm - GNU Make(推荐;Windows 需要自行安装) ### 安装并启动 ```bash npm install make start ``` 服务会在后台以开发模式启动。打开: - 首页: - 生成器: 查看状态或停止服务: ```bash make status make stop ``` 如果本机没有 GNU Make,也可以直接以前台开发模式运行: ```bash npm run dev -- --hostname 127.0.0.1 --port 38090 ``` ## 使用方法 1. 打开 `/generator`,拖入或选择一张没有人物、构图清楚的 JPG/PNG 背景图。 2. 从 8 个模板中选择与场景匹配的动作。 3. 点击“生成姿势”。 4. 在中间预览中确认人物位置,并在右侧依次查看五项动作指导。 5. 查看结果来源:`AI 图像编辑` 表示远程接口成功,`本地合成` 表示本地结果或远程失败后的降级结果。 6. 点击“下载 PNG”保存合成图,默认文件名为 `yudaoposeai-guide.png`。 为了让参考图更易使用,建议上传主体明确、光线稳定、没有人物且保留足够站位空间的背景。 ## 姿势模板 模板定义的唯一来源是 [`lib/pose/poseTemplates.ts`](lib/pose/poseTemplates.ts)。当前内置模板如下: | ID | 名称 | 推荐场景 | | --- | --- | --- | | `travel_stand` | 旅行自然站姿 | 街道 / 地标 | | `walking_lookback` | 行走回望 | 公路 / 巷道 | | `cafe_sit` | 咖啡馆侧坐 | 咖啡馆 / 室内 | | `wall_lean` | 墙边轻靠 | 建筑 / 走廊 | | `stair_sit` | 台阶错落坐姿 | 台阶 / 庭院 | | `beach_stretch` | 海边舒展 | 海边 / 草原 | | `street_pocket` | 街头插袋站姿 | 街区 / 店铺 | | `chin_rest` | 托腮近景 | 桌边 / 窗前 | 默认模板是 `cafe_sit`。 ## 图像 Provider 配置 ### 本地合成(默认) 默认配置已经可以直接工作,不需要任何密钥: ```dotenv YUDAOPOSEAI_IMAGE_PROVIDER=mock ``` API 返回归一化姿势关键点,浏览器使用 SVG 显示参考模特,并使用 Canvas 合成下载文件。 ### 远程图像编辑 以 [`.env.example`](.env.example) 为模板创建未提交的 `.env.local`,然后配置: ```dotenv YUDAOPOSEAI_IMAGE_PROVIDER=openai OPENAI_URL=https://relay.example.com/v1 OPENAI_API_KEY=replace-with-a-server-side-key OPENAI_IMAGE_MODEL=gpt-image-2 OPENAI_IMAGE_QUALITY=low ``` 上例使用 OpenAI 兼容中转站。直连官方接口时可将 `OPENAI_URL` 留空,或设置为 `https://api.openai.com/v1`。 修改环境变量后需要重启服务: ```bash make restart ``` | 变量 | 默认值 | 说明 | | --- | --- | --- | | `YUDAOPOSEAI_IMAGE_PROVIDER` | `mock` | 只有精确设置为 `openai` 才启用远程图像编辑 | | `OPENAI_URL` | `https://api.openai.com/v1` | 官方地址、中转站根地址、API 基地址或完整 edits 端点 | | `OPENAI_API_KEY` | 空 | 仅由服务端读取的访问密钥 | | `OPENAI_IMAGE_MODEL` | `gpt-image-2` | 发送给兼容接口的模型名 | | `OPENAI_IMAGE_QUALITY` | `low` | 发送给兼容接口的质量参数 | `OPENAI_URL` 会按以下规则解析: | 配置示例 | 实际请求地址 | | --- | --- | | 留空 | `https://api.openai.com/v1/images/edits` | | `https://relay.example.com` | `https://relay.example.com/v1/images/edits` | | `https://relay.example.com/v1` | `https://relay.example.com/v1/images/edits` | | `https://relay.example.com/openai/v1` | `https://relay.example.com/openai/v1/images/edits` | | `https://relay.example.com/custom/images/edits` | 原样使用该完整端点 | 中转站必须兼容 OpenAI Images edits:使用 `Authorization: Bearer `,接收 `image`、`prompt`、`model`、`quality` 和 `size=auto` 的 multipart 请求,并返回 `data[0].b64_json` 或 `data[0].url`。应用不会手动设置 multipart 的 `Content-Type`,以便运行时附带正确的 boundary;相对的 `data[0].url` 会基于中转站地址解析。 Provider 的实际行为: | 条件 | 结果 | 界面状态 | | --- | --- | --- | | provider 不是 `openai` | SVG/Canvas 本地结果 | 本地合成 | | provider 是 `openai`,但没有 API Key | 自动使用本地结果 | 本地合成 | | Images edits 请求成功 | 使用接口返回的完整图片 | AI 图像编辑 | | 上游拒绝、超时、响应无图片或解析失败 | 自动使用本地结果 | 本地合成 | | Windows 下 Node `fetch` 失败、PowerShell 备用路径成功 | 使用接口返回的完整图片 | AI 图像编辑 | `OPENAI_API_KEY` 不得改成 `NEXT_PUBLIC_*`,也不要写入源码、日志、截图或已提交的文档。启用远程 provider 后,上传图片会发送到所配置的第三方接口,其数据处理规则由该服务提供方决定。 ## 服务管理与生产运行 `Makefile` 提供跨平台的后台服务管理入口: ```bash make start make status make restart make stop ``` 可通过 Make 变量覆盖监听地址、端口、模式和超时: ```bash make start HOST=0.0.0.0 PORT=39000 make start TIMEOUT=120 ``` | 变量 | 默认值 | 可选值/范围 | | --- | --- | --- | | `HOST` | `127.0.0.1` | 有效主机名或 IP | | `PORT` | `38090` | `1` 到 `65535` | | `MODE` | `development` | `development` 或 `production` | | `TIMEOUT` | `60` | `1` 到 `600` 秒 | 服务状态、心跳和日志保存在未提交的 `.runtime/` 目录,主要日志文件是 `.runtime/service.log`。 生产模式不会自动构建。先停止开发服务,再执行: ```bash make stop npm run build make start MODE=production ``` 不要让 `next dev` 和 `next build` 同时使用同一个 `.next` 目录。服务管理的完整说明见 [`docs/service-operations.md`](docs/service-operations.md)。 ## API ### `POST /api/generate-pose` 请求类型为 `multipart/form-data`: | 字段 | 类型 | 说明 | | --- | --- | --- | | `image` | File | 必填;MIME 必须为 `image/jpeg` 或 `image/png`,最大 10 MB | | `width` | number string | 必填;大于 0 的原图宽度 | | `height` | number string | 必填;大于 0 的原图高度 | | `poseTemplate` | string | 必填;上表中的模板 ID | | `renderMode` | string | 必填;固定为 `ai_overlay` | 调用示例: ```bash curl -X POST http://127.0.0.1:38090/api/generate-pose \ -F "image=@./public/yudaoposeai-cafe-before.png;type=image/png" \ -F "width=1448" \ -F "height=1086" \ -F "poseTemplate=cafe_sit" \ -F "renderMode=ai_overlay" ``` 成功响应包含: - `guide.canvas`:原图尺寸。 - `guide.personBox`:归一化人物区域。 - `guide.keypoints`:13 个归一化姿势关键点。 - `guide.instructions`:五类中文动作指导。 - `overlay.mode`:固定为 `ai_overlay`。 - `overlay.provider`:`mock` 或 `image-to-image`。 - `overlay.kind`:`vector` 或 `image`。 - `overlay.note`:面向界面的结果来源说明。 - `overlay.imageUrl`:远程成功时的完整图片 data URL 或 URL。 错误响应统一为: ```json { "error": "面向用户的中文错误信息" } ``` | 状态码 | 场景 | | --- | --- | | `400` | 缺少图片、尺寸无效、模板无效或生成模式无效 | | `413` | 图片超过 10 MB | | `415` | 图片不是 JPG/PNG | | `500` | 请求解析或生成管线出现未处理异常 | ## 技术栈 - Next.js 14 App Router、React 18 - TypeScript 严格模式 - Tailwind CSS 3 - Radix Slot、本地 shadcn/ui 风格组件、Lucide 图标 - SVG 预览、Canvas PNG 导出 - Vitest 单元测试、Playwright Core 视觉验收 - 本地打包的 Inter Variable、Noto Sans SC Variable 字体 ## 项目结构 ```text app/ api/generate-pose/route.ts # multipart 生成接口 generator/page.tsx # 生成器页面 page.tsx # 首页 components/ GeneratorWorkspace.tsx # 上传、请求、结果和导出状态编排 ImageUploader.tsx # 文件选择、拖放和前端校验 PoseCanvas.tsx # 预览、SVG 模特和 PNG 导出 PoseInstructions.tsx # 五类中文指导 PoseTemplateSelector.tsx # 姿势模板选择 lib/pose/ analyzeImage.ts # API 校验与生成管线 overlayProvider.ts # 远程编辑、本地回退、Windows 备用路径 poseTemplates.ts # 8 个模板的唯一来源 mockPose.ts # 本地关键点生成 types.ts # 领域类型 uploadRules.ts # 前后端共享上传规则 scripts/ service-manager.mjs # 后台服务管理 openai-image-edit.ps1 # Windows 远程请求备用路径 visual-qa.mjs # 桌面与移动端视觉验收 tests/ # Vitest 测试 docs/ # 架构图和服务文档 specs/ # 工程重建与视觉规范 ``` 架构资料: - [系统架构图](docs/architecture.svg) - [内部模块依赖图](docs/module-deps.svg) - [外部依赖图](docs/external-deps.svg) - [开发与重建指南](specs/CODEX_REBUILD_GUIDE.md) - [视觉与交互规范](specs/VISUAL_SPEC.md) ## 测试与检查 ```bash npm test npm run lint npm run build ``` 视觉验收前需要先启动开发服务器: ```bash make start npm run qa:visual ``` 当前视觉 QA 脚本固定使用 macOS 的 Google Chrome 路径 `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`,检查 `1440 x 1000` 和 `390 x 844` 两个 viewport,并将截图输出到 `/private/tmp/yudaoposeai-qa`。可通过 `YUDAOPOSEAI_QA_URL` 指向非默认服务地址。 ## 常见问题 ### 配置了远程接口,为什么仍显示“本地合成”? 依次确认 `YUDAOPOSEAI_IMAGE_PROVIDER` 是否精确为 `openai`、服务进程是否能读取 `OPENAI_API_KEY`、环境变量修改后是否已经重启,以及 `OPENAI_URL` 是否为中转站根地址、API 基地址或完整 `/images/edits` 端点。中转站还必须支持 Images edits multipart 协议和 Bearer 鉴权。上游错误也会触发自动降级,可在 `.runtime/service.log` 中查找 `OpenAI image edit failed`。 ### `make start` 提示端口被占用 先运行 `make status`。如果当前项目已用另一组参数运行,先 `make stop`;如果端口属于其他程序,改用 `make start PORT=39000`。 ### 生产模式提示没有构建 执行 `make stop`,然后运行 `npm run build`,最后使用 `make start MODE=production`。服务管理器不会隐式执行生产构建。 ### 远程图片可以预览,但 PNG 无法导出 当前导出实现没有为跨源图片请求设置 `crossOrigin="anonymous"`。因此远程 URL 即使可以预览,也可能在绘制到 Canvas 时触发跨源限制;仅配置响应端 CORS 头不能保证当前版本可导出。生产环境应优先让兼容接口返回 `b64_json`,或返回同源图片 URL。 ## 数据与项目边界 - 默认本地模式下,图片只存在于浏览器内存和当前 HTTP 请求中。 - Windows PowerShell 备用路径会把图片和 prompt 写入系统临时目录,并在调用结束后删除。 - 项目不主动把上传图或结果写入数据库、缓存、对象存储或历史记录。 - 启用远程 provider 时,图片会离开本机并发送到所配置的接口。 - 当前版本不包含账号、权限、支付、后台管理或多用户隔离能力。 仓库当前未声明项目级开源许可证。第三方字体许可信息见 [`THIRD_PARTY_NOTICES.md`](THIRD_PARTY_NOTICES.md)。