万物套图
描述
根据输入商品图片、文字提示词和模板编号生成套图。通过 prompt 描述套图需求,通过 params.task_id 指定模板。
版本
1.0
图片要求
支持 JPG、PNG 格式。图片可通过 URL 或 Base64 编码传入。以下示例使用一张原图。
调用 URL
- 正式环境:`https://openapi.meitu.com`
- 任务提交接口:`https://openapi.meitu.com/api/v1/sdk/sync/push`
- 任务名称(`task`):`/v1/Encompassing_Image/495797`
- 任务类型(`task_type`):`formula`调用方法
POST
Content-Type: application/json
权限
使用 Access Key(AK)和 Secret Key(SK)进行请求签名,详见开放平台接口签名。
Credits 不能用于 API 接口扣费。开通功能及资源包购买与管理请前往 MV 控制台。
请求参数
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 必选 | init_images | object[] | 输入图片列表。 |
| 必选 | params | string | JSON 字符串,解码后直接包含 prompt 和 task_id。 |
| - | rsp_media_type | string | 结果返回方式。本例为 url,填写在请求顶层。 |
| 必选 | task | string | 固定为 /v1/Encompassing_Image/495797。 |
| 必选 | task_type | string | 固定为 formula。 |
- 表示当前资料未注明必选性,不表示可省略。
init_images 元素
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 必选 | url | string | 输入图片地址。 |
| 必选 | profile | object | 图片属性信息。 |
profile
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 可选 | media_extra | object | 图片附加信息,示例为 {}。 |
| 必选 | media_profiles | object | 图片传输信息。 |
media_profiles
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 必选 | media_data_type | string | 本例为 url,表示通过 URL 传入图片。 |
params
先构造包含以下字段的对象,再将整个对象序列化为字符串。这里没有 parameter 包装层,prompt 本身是字符串。
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 是 | prompt | string | 套图生成要求,例如“生成一组亚马逊套图,一共4张”。 最多生成 7 张图,超出 7 张无法生效 |
params.task_id 是数字类型的模板编号,不是响应中用于查询任务的网关任务 ID。
输入值示例
以下使用商品套图模板(task_id: -1),通过提示词要求生成 4 张亚马逊套图。请按需替换图片地址与提示词。
{
"init_images": [
{
"profile": {
"media_extra": {},
"media_profiles": {
"media_data_type": "url"
}
},
"url": "https://obs.mtlab.meitu.com/public/EtRGxLI8ulQ43pGhuZfym9aYUD2PUbMP/MTc4NTM5MTIwMA==/78af5a5d-a5c8-4c9a-53ce-bd9d2e883a56.jpeg"
}
],
"params": "{\"prompt\":\"生成一组亚马逊套图,一共4张\"}",
"rsp_media_type": "url",
"task": "/v1/Encompassing_Image/495797",
"task_type": "formula"
}返回值说明
注意,生成的结果会定期清理,请及时下载保存。以下返回说明和示例沿用旧版资料,尚未核实新任务
/v1/Encompassing_Image/495797的实际返回结构。
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 = {
"prompt": "生成一组亚马逊套图,一共4张"
}
payload = {
"init_images": [
{
"profile": {
"media_extra": {},
"media_profiles": {
"media_data_type": "url"
}
},
"url": "https://obs.mtlab.meitu.com/public/EtRGxLI8ulQ43pGhuZfym9aYUD2PUbMP/MTc4NTM5MTIwMA==/78af5a5d-a5c8-4c9a-53ce-bd9d2e883a56.jpeg"
}
],
"params": json.dumps(inner_params, ensure_ascii=True),
"rsp_media_type": "url",
"task": "/v1/Encompassing_Image/495797",
"task_type": "formula"
}
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 := `{
"init_images": [
{
"profile": {
"media_extra": {},
"media_profiles": {
"media_data_type": "url"
}
},
"url": "https://obs.mtlab.meitu.com/public/EtRGxLI8ulQ43pGhuZfym9aYUD2PUbMP/MTc4NTM5MTIwMA==/78af5a5d-a5c8-4c9a-53ce-bd9d2e883a56.jpeg"
}
],
"params": "{\"prompt\":\"生成一组亚马逊套图,一共4张\"}",
"rsp_media_type": "url",
"task": "/v1/Encompassing_Image/495797",
"task_type": "formula"
}`
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([
'prompt' => '生成一组亚马逊套图,一共4张'
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$body = json_encode([
'init_images' => [
[
'profile' => [
'media_extra' => (object) [],
'media_profiles' => [
'media_data_type' => 'url'
]
],
'url' => 'https://obs.mtlab.meitu.com/public/EtRGxLI8ulQ43pGhuZfym9aYUD2PUbMP/MTc4NTM5MTIwMA==/78af5a5d-a5c8-4c9a-53ce-bd9d2e883a56.jpeg'
]
],
'params' => $innerParams,
'rsp_media_type' => 'url',
'task' => '/v1/Encompassing_Image/495797',
'task_type' => 'formula'
], 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" +
" \"init_images\": [\n" +
" {\n" +
" \"profile\": {\n" +
" \"media_extra\": {},\n" +
" \"media_profiles\": {\n" +
" \"media_data_type\": \"url\"\n" +
" }\n" +
" },\n" +
" \"url\": \"https://obs.mtlab.meitu.com/public/EtRGxLI8ulQ43pGhuZfym9aYUD2PUbMP/MTc4NTM5MTIwMA==/78af5a5d-a5c8-4c9a-53ce-bd9d2e883a56.jpeg\"\n" +
" }\n" +
" ],\n" +
" \"params\": \"{\\\"prompt\\\":\\\"生成一组亚马逊套图,一共4张\\\"}\",\n" +
" \"rsp_media_type\": \"url\",\n" +
" \"task\": \"/v1/Encompassing_Image/495797\",\n" +
" \"task_type\": \"formula\"\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();
}
}
}