教程
如何通过 HiAPI API 使用 recraft/creative-upscale:curl 与 Python 请求指南
recraft/creative-upscale 在开发者工作流中的作用
recraft/creative-upscale 是一个图生图端点:你发送一张已有图片,模型返回一张更大、且由模型重建的版本。这里的关键词是创意(creative)。经典的上采样步骤(双线性、Lanczos,或通用超分辨率模型)尽可能忠实地保留输入。创意上采样则允许重新生成源像素中从未存在的细节——边缘被重新合成,纹理被推断,小而模糊的区域可以被锐化成看似合理的内容,而不仅仅是插值。
这使得它区别于简单的缩放工具,也决定了它在流水线中处于不同的位置。
何时使用它
- 素材升级。 一张缩略图或旧版产品图需要以更大的显示尺寸呈现,而普通缩放与原生分辨率的邻居相比会显得模糊。
- 参考图和效果图工作。 概念艺术、故事板或占位图像需要在大尺寸展示时不暴露其低分辨率来源。
- 生成循环内的上采样。 你在较小的工作尺寸上生成了图片,想要一个最终素材,而无需在更大的画布上重新运行整个生成流程。
何时不要使用它
如果需求是逐字节精确保真——档案扫描件、文档、像素值即数据的纹理图集——不要使用创意模型。重新生成的细节是合理的猜测,而非重建;对于测量或归档目的,这种猜测就是缺陷。这种情况下请使用确定性重采样器,把创意模型留给感知性输出。
调用的形态
该端点遵循与其他 HiAPI 图像端点相同的请求模式:向模型路径发送带认证的 JSON POST 请求,包含一个输入图像和一个参数对象。如果你已经集成了平台上的任何其他图像模型,这种心智模型可以直接迁移——这正是通过统一接口路由这些调用的意义所在。一个可用的 curl 调用就成为你后续接触的每个图像端点的模板。
理解这一步的一个有用方式:它消费一张你已经信任的图像(构图、内容、颜色都已确定),返回一个更大、更详细的变体。内容决策属于上游;recraft/creative-upscale 是收尾阶段。
前置条件与端點设置
在编写第一个请求之前,收集四样东西。它们都不需要特殊的 SDK——整个集成就是带有 JSON 请求体的纯 HTTPS 通信。
1. API 密钥
你需要一个能访问图像模型的 HiAPI API 密钥。把它当作机密:从环境变量加载,而不是粘贴到脚本中,也绝不要提交到版本库。
export HIAPI_API_KEY="your-key-here"
密钥通过请求头传递,不在 URL 中,也不在 JSON 请求体中。将它排除在查询字符串之外,也就将它排除在 shell 历史和代理日志之外。
2. 基础 URL
基础 URL 是你的 HiAPI API 主机。所有模型调用都是其下的相对路径,因此你可以将其设置为一个变量,并在每个脚本中复用:
export HIAPI_BASE_URL="https://api.hiapi.ai"
查阅平台 API 参考文档以获取你账户的规范基础 URL 和路径前缀,并将其存储为这个单一变量。这样,主机或版本变更就是一行编辑。
3. 模型标识符
模型 ID 是一个路径段,必须严格按发布拼写:recraft/creative-upscale。它包含一个斜杠,这在某些工具中很重要——调试部分会详细说明。大小写和连字符都算数。一个错误的字符会产生模型未找到的错误,如果你只是粗略浏览消息,它看起来像认证问题。
4. 测试图像
在磁盘上准备好一张图像,并知道其确切路径和 MIME 类型。首次运行时使用小文件,这样失败就明确是请求问题,而不是负载大小或超时问题。你之后会复用同一张图像进行对比运行,所以选择有可见细节的东西(织物、植被、无文字的标牌),而不是平坦的渐变。
请求头检查清单
从这里开始的每个请求都携带相同的请求头:
Authorization: Bearer $HIAPI_API_KEYContent-Type: application/json
如果你在 JSON 请求体中以 base64 或 data URI 形式发送图像,这两个请求头就足够了。如果你改为先上传文件以获取 URL,请将该上传作为单独的调用,并将生成的 URL 与模型请求分开保存。
有了密钥、基础 URL、模型 ID 和一张测试图像,你可以在不到一分钟内发起真实调用。
使用 curl 的工作请求
以下是一个可直接运行的调用。将图像值替换为你自己的输入——可以是公开可达的 URL 或 base64 编码的 data URI,取决于模型的输入模式接受什么。
curl -sS -X POST "$HIAPI_BASE_URL/v1/models/recraft/creative-upscale" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": {
"image": "https://example.com/test-input.png"
},
"parameters": {
"upscale": 2
}
}' \
-o response.json
使用 -o 将响应写入文件,而不是让它流式输出到终端,可以保持 JSON 完整以便解析,并使调用可脚本化。
各部分的作用
-X POST—— 该端点是 POST;GET 会返回方法错误,容易被误读为权限问题。- URL —— 基础 URL,然后是带完整模型 ID 的模型路径。由于 ID 本身包含斜杠,路径在模型集合之后有两个段;将其保持为一个字符串,除非文档说明这是预期行为,否则不要让客户端库将其重新编码为
recraft%2Fcreative-upscale。 Authorization—— bearer 令牌。如果你在这个确切的命令上看到 401,说明密钥或请求头格式错误;请求体中还没有其他内容被验证。Content-Type: application/json—— 必需。没有它,某些网关会将请求体视为表单数据,请求永远到达不了模型。input.image—— 源图像。迭代时优先使用 URL:它保持命令简短,差异清晰。parameters.upscale—— 上采样控制。从适中的倍数开始,确认响应后再提高。
两个实用变体
要以 base64 形式发送本地文件而不是 URL:
B64=$(base64 -w0 ./test-input.png)
curl -sS -X POST "$HIAPI_BASE_URL/v1/models/recraft/creative-upscale" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"input\": { \"image\": \"data:image/png;base64,${B64}\" },
\"parameters\": { \"upscale\": 2 }
}" \
-o response.json
要在调试时同时查看 HTTP 状态码和响应体,添加 -w '\nHTTP %{http_code}\n' 并去掉 -o。
将这一个命令视为你的模板。一旦它返回图像,后续的每次更改——不同的倍数、不同的源图像——都只是编辑两个字段,而不是新的集成。
Python 中的相同请求
Python 版本不应是 curl 请求体的复制粘贴;它应该是一个小的可复用函数,以便后续的对比运行每次只需一次调用。仅使用标准库——无需 SDK。
import base64
import json
import os
import pathlib
import urllib.request
HIAPI_BASE_URL = os.environ["HIAPI_BASE_URL"].rstrip("/")
HIAPI_API_KEY = os.environ["HIAPI_API_KEY"]
MODEL = "recraft/creative-upscale"
def creative_upscale(image: str, upscale: int = 2, timeout: int = 120) -> dict:
"""将一张图像发送到 recraft/creative-upscale 并返回解析后的 JSON。"""
payload = {
"input": {"image": image},
"parameters": {"upscale": upscale},
}
request = urllib.request.Request(
url=f"{HIAPI_BASE_URL}/v1/models/{MODEL}",
data=json.dumps(payload).encode("utf-8"),
headers={
"Authorization": f"Bearer {HIAPI_API_KEY}",
"Content-Type": "application/json",
},
method="POST",
)
with urllib.request.urlopen(request, timeout=timeout) as response:
return json.loads(response.read().decode("utf-8"))
def as_data_uri(path: str) -> str:
data = pathlib.Path(path).read_bytes()
mime = "image/png" if path.lower().endswith(".png") else "image/jpeg"
return f"data:{mime};base64,{base64.b64encode(data).decode('ascii')}"
if __name__ == "__main__":
result = creative_upscale(as_data_uri("test-input.png"), upscale=2)
print(json.dumps(result, indent=2)[:2000])
为什么采用这种形态
- 从环境变量获取配置。 基础 URL 和密钥在导入时读取一次,因此文件中不会出现任何机密内容,切换环境只需一次 export。
- 一个函数,一个职责。
creative_upscale接收图像引用和倍数,返回解析后的 JSON。所有关于参数的信息在调用点都保持可见。 - 显式超时。 图像模型比文本端点耗时更长。有意设置超时,而不是继承过短的默认值,并保持足够长,以便慢但成功的调用不会被不必要地重试。
- 本地文件的辅助函数。
as_data_uri意味着你可以传入 URL 或本地路径,而无需改动请求逻辑。
如果你更喜欢基于会话的客户端
使用 httpx 的相同调用只需两行替换——构造一个客户端,设置默认的 Authorization 请求头,然后 post JSON 请求体。重要的不变项没有改变:模型 ID 保持为单个未编码的路径段,密钥保留在请求头中,请求体保持 input / parameters 的拆分。匹配这三点,你的客户端就与平台上的其他任何客户端可互换。
当你将其接入流水线时,返回解析后的字典,让调用方决定如何持久化图像——这种分离使得下一节的响应处理可复用。
上采样与创意参数
你发送的值不如在哪里发送它们重要。先确定位置正确,再调优数值。
两个桶,一份契约
每个请求分为:
input—— 图像本身。操作的对象。任何关于模型行为的内容都不放在这里。parameters—— 控制项。上采样倍数和创意设置都属于这个对象,互为兄弟。
这种拆分就是契约。首次运行应确认模型接受你的放置——即调用至少返回图像——然后再开始优化值。如果参数放错了桶,你通常会得到用默认值生成的图像,或者一个指出未知字段的验证错误。无论哪种情况,都是放置错误,不是质量错误,追逐质量设置不会修复它。
上采样倍数
倍数控制输出相对于输入的尺寸。关于它的推理:
- 它是乘数,所以成本和延迟随之增长。 从适中的倍数提升到最大值,在渲染时间上不是线性步骤。将每一步提升视为一个新实验,而不是微调滑块。
- 输入分辨率仍然重要。 当存在一些结构时,创意模型有更多可发挥的余地。极小的输入给它更多自由去发明,有时这是目标,有时是意外。
- 根据目标尺寸选择倍数,而不是根据最大值。 如果图像需要 2× 才能放入其位置,就请求 2×。请求最大值然后再缩小会丢弃收益并为此付费。
创意控制
创意是决定模型可以替换(而非放大)多少的旋钮。低设置表现更接近忠实的上采样;高设置让模型更激进地重新合成,这可以挽救模糊或块状的源图,也同样可能引入你不想要的细节。
将该参数视为保真度杠杆,两端都有具体的解读:
- 保留 —— 标志、人脸、产品目录图像,任何观看者可能与原图比较的内容。
- 重新生成 —— 概念艺术、背景、纹理、装饰性图像,其中合理性胜过精确性。
工作方法
一次更改一个参数,保持输入固定,并将值与输出一起记录。一个小的矩阵——同一张图像在两个倍数下,每个倍数在两个创意设置下——就足以在你自己的内容上刻画该端点的特性。不要借用别人的"最佳"值;创意模型与输入强烈交互,适合照片的设置很少适合扁平插画。
如果某个值被直接拒绝而不是产生差的结果,那是契约在告诉你参数的域——查阅端点的参数参考以获取可接受范围,而不是猜测。
读取响应并保存图像
防御性地解析响应。负载类型是契约的一部分,但它可能在 URL 和内联 base64 块之间不同,你的客户端不应因收到哪种而崩溃。
首先,查看原始 JSON
在编写解析器之前,先用 -o response.json 运行一次 curl 调用并读取文件。识别携带图像的字段。它通常是以下之一:
- 指向生成文件的 URL,或
- 内联字符串,通常是 base64 或 data URI。
还要寻找值得记录元数据:尺寸、回显的模型 ID,以及任何可用于提交支持请求的标识符。
一个容错的保存函数
import base64
import pathlib
import urllib.request
def extract_image(result: dict) -> str:
"""无论返回何种形态,都找到图像负载。"""
for container in (result, result.get("data"), result.get("output")):
if isinstance(container, list) and container:
container = container[0]
if isinstance(container, dict):
for key in ("image", "url", "b64_json", "image_url"):
value = container.get(key)
if isinstance(value, str) and value:
return value
raise ValueError(f"no image field in response: {sorted(result)}")
def save_image(payload: str, out_path: str = "upscaled.png") -> pathlib.Path:
path = pathlib.Path(out_path)
if payload.startswith("data:"):
payload = payload.split(",", 1)[1]
path.write_bytes(base64.b64decode(payload))
elif payload.startswith("http://") or payload.startswith("https://"):
with urllib.request.urlopen(payload) as response:
path.write_bytes(response.read())
else:
path.write_bytes(base64.b64decode(payload))
return path
重点不是这是唯一正确的解析器——而是该函数陈述了它的假设(data:、http(s)://、纯 base64),并在都不匹配时抛出清晰的错误。当响应形态改变时,只需更新一个函数,而不是每个消费图像的脚本。
在 curl 流程中安全保存
curl -sS -X POST "$HIAPI_BASE_URL/v1/models/recraft/creative-upscale" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d @request.json -o response.json
python - <<'PY'
import json
from save_image import extract_image, save_image
result = json.load(open("response.json"))
print(save_image(extract_image(result), "out.png"))
PY
两个值得保持的习惯:每次运行写入新文件名,这样失败或部分写入永远不会覆盖好结果;保存后验证输出尺寸——一个成功的 HTTP 200 但尺寸意外,意味着参数没有生效,这在此处比在流水线三个阶段之后更容易发现。
调试常见故障
大多数首次运行的故障都很无聊。按顺序处理这个列表;它涵盖了绝大多数情况。
401 / 403 —— 认证
- 密钥是否加载在同一 shell 中运行命令?
echo $HIAPI_API_KEY应打印非空值。 - 请求头是否恰好是
Authorization: Bearer <key>——包括空格和单词Bearer? - 密钥是否最终出现在 URL 或 JSON 请求体中而不是请求头中?把它移回去。
- 密钥是否限定于图像模型?对某个产品区域有效的密钥可能不适用于此端点。
404 —— 模型或路由未找到
这几乎总是模型 ID 的问题。逐字符与发布的标识符 recraft/creative-upscale 比较:斜杠、小写和 creative-upscale 中的连字符都是关键。一个相关的陷阱是客户端 URL 转义——如果你在请求线上看到 recraft%2Fcreative-upscale 且调用 404,你的 HTTP 层编码了斜杠;配置它不要动路径段。
还要确认你没有重复路径前缀(例如基础 URL 已经以版本段结尾,而路径又重复了它)。为基 URL 使用一个变量使这很容易发现。
400 —— 请求被理解但被拒绝
阅读消息而不是猜测。常见原因:
- 图像输入格式错误。 截断的 base64(通常来自 shell 变量被截断,或缺少
data:image/png;base64,前缀而模式期望完整 data URI),或无法公开获取的 URL。 - 参数超出范围或放错位置。 模型不接受的倍数,或
upscale发送在顶层而不是parameters内部。 - 缺少内容类型。
Content-Type为text/plain或完全缺失,会将 JSON 请求体变成无法解析的文档。
413 / 422 / 超时 —— 大小与时长
非常大的源图像可能在模型运行前就验证失败。适度缩小输入,重试,并在发送全分辨率文件之前确认请求形态合理。对于超时,区分慢和失败:将客户端超时增加一次,如果调用随后成功,修复属于客户端配置,而不是负载。
快速分诊规则
如果错误提到认证、模型或输入格式,你根本没有到达上采样逻辑——还不涉及任何创意或倍数值。修复包装器,重试尽可能小的请求(一张小测试图像,默认参数),然后才开始改变设置。按此顺序调试可以防止经典错误:通过调整参数来补偿损坏的请求头。
构建可重复的对比测试
创意上采样非常依赖判断,所以用固定装置代替记忆。目标是一个小型测试框架,回答"这个设置真的有用吗?"而无需你重新目视旧文件。
固定输入
- 一张测试图像,签入版本库。 选择有可恢复细节的东西——织物、植被、建筑边缘。将其提交在脚本旁边,以便每次运行和每个团队成员使用相同的源。
- 固定参数。 倍数和创意值存在于脚本中的列表里,而不是你的头脑或聊天消息中。
- 固定模型 ID。 与请求头检查清单同样的理由:
recraft/creative-upscale拼写一次,放在一个常量中。
一个最小的矩阵运行器
import itertools, json, pathlib
FACTORS = [2, 4]
CREATIVITY = ["preserve", "regenerate"] # 使用文档中确切接受的值
src = as_data_uri("fixtures/test-input.png")
out_dir = pathlib.Path("runs"); out_dir.mkdir(exist_ok=True)
for factor, mode in itertools.product(FACTORS, CREATIVITY):
result = creative_upscale(src, upscale=factor, creativity=mode)
name = f"f{factor}-{mode}"
save_image(extract_image(result), out_dir / f"{name}.png")
(out_dir / f"{name}.json").write_text(json.dumps(result, indent=2))
print(name, "ok")
两个细节比代码更重要。第一,将原始 JSON 响应保存在每张图像旁边——当结果看起来不对时,响应告诉你参数是已应用还是被静默忽略。第二,从参数值命名文件,这样目录列表就是矩阵,你永远不用怀疑哪个文件来自哪个设置。
以固定方式比较
- 在预期显示尺寸下查看输出,而不是放大看。在 400% 下令人印象深刻的创意细节,在用户实际看到的尺寸下可能显得不自然。
- 作为运行的一部分以编程方式检查尺寸。不匹配意味着倍数没有生效,这是对你自己的请求的 bug 报告,而不是美学问题。
- 根据定义的标准评判:对源的保真度与感知锐度,按资产类别选择。将该标准写在矩阵旁边,以便下一个人知道"更好"意味着什么。
一旦矩阵稳定,框架就变成回归测试:在客户端代码的任何更改后重新运行它,并与之前的运行目录比较。由于本指南中的每个调用与平台的其他图像端点共享相同的请求形态,你在这里构建的运行器可以推广到你接下来集成的任何模型。