feat: 接口2/5 发际线叠图改为透明 PNG(仅曲线),生发图不变

接口2(/api/v1/hair/grow)的 image_url 和接口5(/api/v1/hairline/generate)
的 image_middle/high/low_url 从「原图+白线合成 JPG」改为「透明底 PNG(仅含
发际线曲线)」,前端需叠加原图显示。grown_image_url 生发图保持不变(ComfyUI
完整人像照片)。

实现:
- app.py 新增 _rgba_png_b64() 编码 RGBA 透明层为 PNG base64(保留 alpha)
- hairline/service.py 接口2/5 改用 build_overlay_layer(返回 RGBA 透明层)
  替代 render_hairline_overlay(合成到原图)
- 网关零改动:rewrite_base64_to_url 已按 \x89PNG 魔数嗅探落盘为 .png

测试页:test_interface2/5.html 改为「原图打底 + 透明PNG 绝对定位叠加」显示
(复用 .img-stack 结构,固定叠加无开关)。

文档:接口文档.md / integration.html 更新接口2/5 图片字段说明。
测试:43 passed,接口2 image_base64 改断言为 PNG 魔数,接口5 三档叠图同。
This commit is contained in:
xsl
2026-07-13 23:38:55 +08:00
parent 28255ef7c2
commit 95c6a2d929
7 changed files with 81 additions and 40 deletions
+6 -5
View File
@@ -90,8 +90,8 @@ def test_grow_female_returns_5(client, monkeypatch):
results = body["data"]["results"]
assert [x["hairline_type"] for x in results] == ["ellipse", "flower", "heart", "straight", "wave"]
assert [x["order"] for x in results] == [1, 2, 3, 4, 5]
assert base64.b64decode(results[0]["image_base64"])[:3] == b"\xff\xd8\xff" # JPEG
assert base64.b64decode(results[0]["grown_image_base64"])[:3] == b"\xff\xd8\xff" # JPEG
assert base64.b64decode(results[0]["image_base64"])[:8] == b"\x89PNG\r\n\x1a\n" # 透明 PNG(发际线曲线)
assert base64.b64decode(results[0]["grown_image_base64"])[:3] == b"\xff\xd8\xff" # JPEG(生发图)
assert "image_url" not in results[0]
@@ -149,9 +149,10 @@ def test_hairline_gen_female(client, monkeypatch):
# 只返回选中发型,order = 发型序号
assert [x["order"] for x in imgs] == [1, 3]
assert [x["hairline_type"] for x in imgs] == ["ellipse", "heart"]
# 三档叠图 + 生发图为 JPEGworker 返回 base64,非 url
for k in ("image_middle_base64", "image_high_base64", "image_low_base64", "grown_image_base64"):
assert base64.b64decode(imgs[0][k])[:3] == b"\xff\xd8\xff", k
# 三档叠图为透明 PNG(发际线曲线,含 alpha),生发图为 JPEG完整人像
for k in ("image_middle_base64", "image_high_base64", "image_low_base64"):
assert base64.b64decode(imgs[0][k])[:8] == b"\x89PNG\r\n\x1a\n", k
assert base64.b64decode(imgs[0]["grown_image_base64"])[:3] == b"\xff\xd8\xff"
assert "image_middle_url" not in imgs[0]
c = d["best_hairline_center_point"]
assert 0 <= c["x"] <= 682 and 0 <= c["y"] <= 811 # 落在原图范围内