万物套图
描述
根据输入图片和结构化提示词重新生成图片。可在提示词中描述需要新增的文字、需要保留与修改的画面内容,以及禁止出现的内容,用于商品展示图等图片的改版与再创作。
版本
1.0
图片要求
支持 JPG、PNG 格式。图片可通过 URL 或 Base64 编码传入。以下示例使用一张原图。
调用 URL
- 正式环境:`https://openapi.meitu.com`
- 任务提交接口:`https://openapi.meitu.com/api/v1/sdk/sync/push`
- 任务名称(`task`):`/v1/Encompassing_Image/495717`
- 任务类型(`task_type`):`formula`调用方法
POST
Content-Type: application/json
权限
使用 Access Key(AK)和 Secret Key(SK)进行请求签名,详见开放平台接口签名。
请求参数
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 必选 | params | string | 算法参数序列化后的 JSON 字符串,结构见下文。 |
| 必选 | init_images | object[] | 输入图片列表。 |
| 必选 | task | string | 固定为 /v1/Encompassing_Image/495717。 |
| 必选 | task_type | string | 固定为 formula。 |
| 可选 | sync_timeout | int | 同步等待超时时间,单位为秒,默认 30。返回 data.status = 9 时继续查询任务结果。 |
init_images 元素
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 必选 | url | string | 图片 URL 或 Base64 编码数据;URL 地址不进行 Base64 编码。 |
| 必选 | profile | object | 图片属性信息。 |
profile
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 必选 | media_profiles | object | 媒体传输信息。 |
| 必选 | version | string | 固定为 v1。 |
| 可选 | media_extra | object | 图片附加参数,无附加配置时可传 {}。 |
media_profiles
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 必选 | media_data_type | string | url 表示图片 URL,jpg 表示 Base64 编码数据。 |
params
先构造以下参数对象,再序列化为字符串,作为请求体的 params 值。
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 必选 | parameter | object | 算法核心参数。 |
| 可选 | extra | object | 算法请求的附加信息,无附加配置时可传 {}。 |
parameter
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 必选 | prompt | object | 结构化提示词,字段说明和示例见下文。在 params 解码后的 parameter 中保持对象类型。 |
| 可选 | height | number | 输出图片高度,单位为像素。未传入时从 prompt 中解析。 |
| 可选 | width | number | 输出图片宽度,单位为像素。未传入时从 prompt 中解析。 |
| 可选 | rsp_media_type | string | 结果返回方式:url 为图片 URL,jpg 为 Base64 编码数据。默认 url。 |
| 可选 | seed | number | 随机种子,默认 42;-1 表示使用随机种子。 |
prompt
以下字段展示结构化提示词的组织方式。字段名按示例保留,字段值填写具体的图片编辑要求。
| 字段名 | 类型 | 说明 |
|---|---|---|
| image_ratio | string | 输出图片比例,示例值为 1:1。 |
| 文字新增 | string | 需要新增的文字,以及字体、位置等排版要求。 |
| 画面不变内容 | string | 希望保留的主体、结构、布局等内容。 |
| 画面修改内容 | string | 希望调整的形状、光影、质感等内容。 |
| 画面禁止内容 | string | 不希望生成或修改的内容。 |
输入值示例
以下示例修改手机壳展示图,并以 URL 返回结果。请替换示例图片地址。
params 是字符串,解码后包含 parameter 和 extra;其中 parameter.prompt 仍是对象。
{
"task": "/v1/Encompassing_Image/495717",
"task_type": "formula",
"init_images": [
{
"url": "https://example.com/input.png",
"profile": {
"media_profiles": {
"media_data_type": "url"
},
"media_extra": {},
"version": "v1"
}
}
],
"params": "{\"parameter\":{\"prompt\":{\"image_ratio\":\"1:1\",\"文字新增\":\"新增文字“多材质可选 蓝光款抗黄”,字体为现代粗黑体,位于画面顶部左侧留白区域;新增文字“极速退款 | 现货速发 | 厂家直营”,字体为细黑体,位于画面底部边缘留白区域\",\"画面不变内容\":\"保留两款手机壳的结构、证书布局、原图抗黄对比和证书背景结构\",\"画面修改内容\":\"将 UI 气泡从直角改为圆角,强化新手机壳的通透感与光照亮度,突出与旧款的质感差异\",\"画面禁止内容\":\"禁止改变两款手机壳的颜色和对比结构;禁止新手机壳出现发黄质感;禁止文字遮挡手机壳和证书主体;禁止使用廉价色块\"},\"seed\":-1,\"rsp_media_type\":\"url\"},\"extra\":{}}",
"sync_timeout": 30
}返回值说明
注意,生成的结果会定期清理,请及时下载保存。code = 0 表示请求处理正常,任务是否完成需同时检查 data.status。任务成功时,从 data.result.data.media_info_list 读取结果图片。
| 字段 | 类型 | 说明 |
|---|---|---|
| request_id | string | 请求标识。 |
| trace_id | string | 链路标识。 |
| code | int | 业务状态码,0 表示正常,非 0 表示错误。 |
| error_code | int | 错误码,示例中与 code 一致。 |
| message | string | 响应消息或错误信息。 |
| data | object | 任务状态与结果。 |
| tips | null | 附加提示,本文失败示例中为 null,可能不返回。 |
data
| 字段 | 类型 | 说明 |
|---|---|---|
| status | int | 状态码:-1 未找到任务;0 创建成功;1 执行中;2 失败;9 任务超时(同步等待超时),使用任务查询接口继续查询;10 成功。 |
| result | object | 任务结果封装;尚未完成时可能仅包含 id。 |
| task_id | string | 网关任务 ID,查询时作为 task_id 传入。 |
| progress | number | 任务进度。 |
| predict_elapsed | number | 预计耗时。 |
| create_time | number | 任务创建时间。 |
| custom_task_id | string | 自定义任务标识。 |
| trace_id | string | 任务链路标识。 |
| client_info | string | 客户端附加信息。 |
| init_images | array/null | 输入图片信息,示例中为 null。 |
result
| 字段 | 类型 | 说明 |
|---|---|---|
| id | string | 网关任务 ID,与 data.task_id 对应。 |
| code | int | 算法状态码。 |
| data | object | 算法返回的原始结果,字段如下。 |
| msg | string | 算法响应消息。 |
| msg_id | string | 算法请求标识,不用于替代网关 task_id。 |
result.data
| 字段 | 类型 | 说明 |
|---|---|---|
| error_code | int | 错误码,0 表示成功。 |
| error_msg | string | 处理结果或错误说明。 |
| msg_id | string | 算法请求的唯一标识。此标识及算法结果保留 24 小时后删除;网关查询使用 data.task_id。 |
| media_info_list | object[] | 处理后的图片列表。 |
| parameter | object/null | 结果返回方式及算法版本信息;失败时可能为 null。 |
| duration | object | 处理过程的详细耗时。 |
| extra | object | 附加信息;示例中包含 algo_msg。 |
| callback_msg | string | 回调相关信息,可为空字符串。 |
result.data.media_info_list 元素
| 字段 | 类型 | 说明 |
|---|---|---|
| media_data | string | 结果图片 URL 或 Base64 编码数据。 |
| media_extra | object/null | 图片附加信息,可为 null。 |
| media_profiles | object | 结果图片的传输信息。 |
| media_profiles.media_data_type | string | url 表示 URL,jpg 表示 Base64 编码数据。 |
result.data.parameter
| 字段 | 类型 | 说明 |
|---|---|---|
| rsp_media_type | string | 图片返回方式:url 或 jpg。 |
| version | string | 算法版本,返回示例为 1.0.0。 |
result.data.duration
| 字段 | 类型 | 说明 |
|---|---|---|
| created_timestamp | int | 算法接收请求的时间戳,单位为秒。 |
| pull_timestamp | int | 任务出队列的时间戳,单位为秒。 |
| waiting_time | int | 队列等待耗时,单位为毫秒。 |
| alg_process_time | int | 算法处理耗时,包含下载时间,单位为毫秒。 |
| upload_time | int | 上传耗时,单位为毫秒。 |
| repost_time | int | 异步回调接口耗时,单位为毫秒。 |
返回 data.status = 9 表示同步等待已超时,不代表算法执行失败。请保存 data.task_id,按任务查询接口获取结果,不要使用算法 msg_id 代替网关任务 ID。算法标识及结果仅保留 24 小时,请及时查询并保存。
返回值示例
请求成功返回示例
Response Status: 200
Content-Type: application/json; charset=utf-8
{
"request_id": "example-request-id",
"trace_id": "example-trace-id",
"code": 0,
"error_code": 0,
"message": "success",
"data": {
"status": 10,
"result": {
"id": "example-task-id",
"code": 0,
"data": {
"callback_msg": "",
"duration": {
"alg_process_time": 18828,
"created_timestamp": 1770117759,
"pull_timestamp": 1770118674,
"repost_time": 22,
"upload_time": 105,
"waiting_time": 914880
},
"error_code": 0,
"error_msg": "success",
"extra": {
"algo_msg": {}
},
"media_info_list": [
{
"media_data": "https://example.com/result.jpeg",
"media_extra": null,
"media_profiles": {
"media_data_type": "url"
}
}
],
"msg_id": "example-message-id",
"parameter": {
"rsp_media_type": "url",
"version": "1.0.0"
}
},
"msg": "success",
"msg_id": "example-message-id"
},
"progress": 1,
"predict_elapsed": 10000,
"create_time": 1770117759000,
"task_id": "example-task-id",
"custom_task_id": "",
"trace_id": "example-trace-id",
"client_info": "",
"init_images": null
}
}需要查询返回示例
使用 data.task_id 调用任务查询接口。
Response Status: 200
Content-Type: application/json; charset=utf-8
{
"request_id": "example-request-id",
"trace_id": "example-trace-id",
"code": 0,
"error_code": 0,
"message": "success",
"data": {
"status": 9,
"result": {
"id": "example-task-id"
},
"progress": 0,
"predict_elapsed": 10000,
"create_time": 1770117759000,
"task_id": "example-task-id",
"custom_task_id": "",
"trace_id": "example-trace-id",
"client_info": "",
"init_images": null
}
}请求失败返回示例
以下为算法执行异常示例,其他错误以实际响应和错误码文档为准。
Response Status: 400
Content-Type: application/json; charset=utf-8
{
"request_id": "example-request-id",
"trace_id": "example-trace-id",
"code": 20001,
"error_code": 20001,
"message": "ALGO_MODEL_CRASH",
"data": {
"status": 2,
"result": {
"id": "example-task-id",
"code": 20001,
"data": {
"duration": {
"alg_process_time": 0,
"created_timestamp": 1770117759,
"pull_timestamp": 1770117759,
"repost_time": 0,
"upload_time": 0,
"waiting_time": 0
},
"error_code": 20001,
"error_msg": "ALGO_MODEL_CRASH",
"extra": {},
"media_info_list": [],
"msg_id": "example-message-id",
"parameter": null
},
"msg": "ALGO_MODEL_CRASH",
"msg_id": "example-message-id"
},
"progress": 1,
"predict_elapsed": 10000,
"create_time": 1770117759000,
"task_id": "example-task-id",
"custom_task_id": "",
"trace_id": "example-trace-id",
"client_info": "",
"init_images": null
},
"tips": null
}通用的错误代码与信息
详见 API 错误码。
SDK 调用示例
以下示例在代码中构造完整请求体,无需额外的 JSON 文件。按对应文档引入签名 SDK 后,替换代码中的 AK、SK 和图片 URL,并根据需要修改提示词。params 保持为 JSON 字符串。
以下代码执行签名后提交请求;返回 data.status = 9 时按上述任务查询说明获取结果。
Python
先按 Python 签名 SDK 接入文档引入 SDK。
import json
import requests
from sign_sdk import sign
def main():
key = "YOUR_ACCESS_KEY"
secret = "YOUR_SECRET_KEY"
url = "https://openapi.meitu.com/api/v1/sdk/sync/push"
inner_params = {
"parameter": {
"prompt": {
"image_ratio": "1:1",
"文字新增": "新增文字“多材质可选 蓝光款抗黄”,字体为现代粗黑体,位于画面顶部左侧留白区域;新增文字“极速退款 | 现货速发 | 厂家直营”,字体为细黑体,位于画面底部边缘留白区域",
"画面不变内容": "保留两款手机壳的结构、证书布局、原图抗黄对比和证书背景结构",
"画面修改内容": "将 UI 气泡从直角改为圆角,强化新手机壳的通透感与光照亮度,突出与旧款的质感差异",
"画面禁止内容": "禁止改变两款手机壳的颜色和对比结构;禁止新手机壳出现发黄质感;禁止文字遮挡手机壳和证书主体;禁止使用廉价色块"
},
"seed": -1,
"rsp_media_type": "url"
},
"extra": {}
}
payload = {
"task": "/v1/Encompassing_Image/495717",
"task_type": "formula",
"init_images": [
{
"url": "https://example.com/input.png",
"profile": {
"media_profiles": {
"media_data_type": "url"
},
"media_extra": {},
"version": "v1"
}
}
],
"params": json.dumps(inner_params, ensure_ascii=True),
"sync_timeout": 30
}
body = json.dumps(payload, ensure_ascii=True)
headers = {
"Content-Type": "application/json",
sign.HeaderHost: "openapi.meitu.com",
}
signer = sign.Signer(key, secret)
signed_request = signer.sign(url, "POST", headers, body)
with requests.Session() as session:
response = session.send(
signed_request, timeout=(10, 60), verify=True, allow_redirects=False
)
print("Status:", response.status_code)
print("Response:", response.text)
if __name__ == "__main__":
main()Go
先按 Go 签名 SDK 接入文档引入 SDK。
package main
import (
"fmt"
"io"
"net/http"
"time"
"github.com/mtlab/api/signer"
)
func main() {
body := `{
"task": "/v1/Encompassing_Image/495717",
"task_type": "formula",
"init_images": [
{
"url": "https://example.com/input.png",
"profile": {
"media_profiles": {
"media_data_type": "url"
},
"media_extra": {},
"version": "v1"
}
}
],
"params": "{\"parameter\":{\"prompt\":{\"image_ratio\":\"1:1\",\"文字新增\":\"新增文字“多材质可选 蓝光款抗黄”,字体为现代粗黑体,位于画面顶部左侧留白区域;新增文字“极速退款 | 现货速发 | 厂家直营”,字体为细黑体,位于画面底部边缘留白区域\",\"画面不变内容\":\"保留两款手机壳的结构、证书布局、原图抗黄对比和证书背景结构\",\"画面修改内容\":\"将 UI 气泡从直角改为圆角,强化新手机壳的通透感与光照亮度,突出与旧款的质感差异\",\"画面禁止内容\":\"禁止改变两款手机壳的颜色和对比结构;禁止新手机壳出现发黄质感;禁止文字遮挡手机壳和证书主体;禁止使用廉价色块\"},\"seed\":-1,\"rsp_media_type\":\"url\"},\"extra\":{}}",
"sync_timeout": 30
}`
url := "https://openapi.meitu.com/api/v1/sdk/sync/push"
headers := make(http.Header)
headers.Set("Content-Type", "application/json")
headers.Set(signer.HeaderHost, "openapi.meitu.com")
signObj := signer.NewSigner("YOUR_ACCESS_KEY", "YOUR_SECRET_KEY")
req, err := signObj.Sign(url, http.MethodPost, headers, body)
if err != nil {
fmt.Println("Sign request failed:", err)
return
}
client := &http.Client{
Timeout: 60 * time.Second,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Send request failed:", err)
return
}
defer resp.Body.Close()
responseBody, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Read response failed:", err)
return
}
fmt.Println("Status:", resp.StatusCode)
fmt.Println("Response:", string(responseBody))
}PHP
先按 PHP 签名 SDK 接入文档引入 SDK。
<?php
require_once __DIR__ . '/signer.php';
$innerParams = json_encode([
'parameter' => [
'prompt' => [
'image_ratio' => '1:1',
'文字新增' => '新增文字“多材质可选 蓝光款抗黄”,字体为现代粗黑体,位于画面顶部左侧留白区域;新增文字“极速退款 | 现货速发 | 厂家直营”,字体为细黑体,位于画面底部边缘留白区域',
'画面不变内容' => '保留两款手机壳的结构、证书布局、原图抗黄对比和证书背景结构',
'画面修改内容' => '将 UI 气泡从直角改为圆角,强化新手机壳的通透感与光照亮度,突出与旧款的质感差异',
'画面禁止内容' => '禁止改变两款手机壳的颜色和对比结构;禁止新手机壳出现发黄质感;禁止文字遮挡手机壳和证书主体;禁止使用廉价色块'
],
'seed' => -1,
'rsp_media_type' => 'url'
],
'extra' => (object) []
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$body = json_encode([
'task' => '/v1/Encompassing_Image/495717',
'task_type' => 'formula',
'init_images' => [
[
'url' => 'https://example.com/input.png',
'profile' => [
'media_profiles' => [
'media_data_type' => 'url'
],
'media_extra' => (object) [],
'version' => 'v1'
]
]
],
'params' => $innerParams,
'sync_timeout' => 30
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$url = 'https://openapi.meitu.com/api/v1/sdk/sync/push';
$headers = [
'Content-Type' => 'application/json',
'Host' => 'openapi.meitu.com',
];
$signer = new Signer('YOUR_ACCESS_KEY', 'YOUR_SECRET_KEY');
$curl = $signer->sign($url, 'POST', $headers, $body);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($curl, CURLOPT_TIMEOUT, 60);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 2);
$response = curl_exec($curl);
if ($response === false) {
echo 'Error: ' . curl_error($curl) . PHP_EOL;
} else {
echo 'Status: ' . curl_getinfo($curl, CURLINFO_HTTP_CODE) . PHP_EOL;
echo 'Response: ' . $response . PHP_EOL;
}
curl_close($curl);Java
先按 Java 签名 SDK 接入文档引入 SDK。
import com.meitu.openai.common.Signer;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) throws Exception {
String body =
"{\n" +
" \"task\": \"/v1/Encompassing_Image/495717\",\n" +
" \"task_type\": \"formula\",\n" +
" \"init_images\": [\n" +
" {\n" +
" \"url\": \"https://example.com/input.png\",\n" +
" \"profile\": {\n" +
" \"media_profiles\": {\n" +
" \"media_data_type\": \"url\"\n" +
" },\n" +
" \"media_extra\": {},\n" +
" \"version\": \"v1\"\n" +
" }\n" +
" }\n" +
" ],\n" +
" \"params\": \"{\\\"parameter\\\":{\\\"prompt\\\":{\\\"image_ratio\\\":\\\"1:1\\\",\\\"文字新增\\\":\\\"新增文字“多材质可选 蓝光款抗黄”,字体为现代粗黑体,位于画面顶部左侧留白区域;新增文字“极速退款 | 现货速发 | 厂家直营”,字体为细黑体,位于画面底部边缘留白区域\\\",\\\"画面不变内容\\\":\\\"保留两款手机壳的结构、证书布局、原图抗黄对比和证书背景结构\\\",\\\"画面修改内容\\\":\\\"将 UI 气泡从直角改为圆角,强化新手机壳的通透感与光照亮度,突出与旧款的质感差异\\\",\\\"画面禁止内容\\\":\\\"禁止改变两款手机壳的颜色和对比结构;禁止新手机壳出现发黄质感;禁止文字遮挡手机壳和证书主体;禁止使用廉价色块\\\"},\\\"seed\\\":-1,\\\"rsp_media_type\\\":\\\"url\\\"},\\\"extra\\\":{}}\",\n" +
" \"sync_timeout\": 30\n" +
"}";
String url = "https://openapi.meitu.com/api/v1/sdk/sync/push";
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/json");
headers.put(Signer.HeaderHost, "openapi.meitu.com");
Signer signer = new Signer("YOUR_ACCESS_KEY", "YOUR_SECRET_KEY");
Map<String, String> signedHeaders = signer.sign(url, "POST", headers, body);
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
try {
connection.setRequestMethod("POST");
connection.setConnectTimeout(10000);
connection.setReadTimeout(60000);
connection.setInstanceFollowRedirects(false);
connection.setDoOutput(true);
for (Map.Entry<String, String> entry : signedHeaders.entrySet()) {
connection.setRequestProperty(entry.getKey(), entry.getValue());
}
byte[] bodyBytes = body.getBytes(StandardCharsets.UTF_8);
connection.setFixedLengthStreamingMode(bodyBytes.length);
try (OutputStream output = connection.getOutputStream()) {
output.write(bodyBytes);
}
int status = connection.getResponseCode();
System.out.println("Status: " + status);
InputStream responseStream = status >= 400
? connection.getErrorStream() : connection.getInputStream();
if (responseStream != null) {
try (InputStream input = responseStream;
ByteArrayOutputStream output = new ByteArrayOutputStream()) {
byte[] buffer = new byte[4096];
int length;
while ((length = input.read(buffer)) != -1) {
output.write(buffer, 0, length);
}
System.out.println("Response: " + output.toString("UTF-8"));
}
}
} finally {
connection.disconnect();
}
}
}