万物套图

描述

根据输入商品图片、文字提示词和模板编号生成套图。通过 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_imagesobject[]输入图片列表。
必选paramsstringJSON 字符串,解码后直接包含 prompt 和 task_id。
-rsp_media_typestring结果返回方式。本例为 url,填写在请求顶层。
必选taskstring固定为 /v1/Encompassing_Image/495797。
必选task_typestring固定为 formula。

- 表示当前资料未注明必选性,不表示可省略。

init_images 元素

是否必选参数名类型参数说明
必选urlstring输入图片地址。
必选profileobject图片属性信息。

profile

是否必选参数名类型参数说明
可选media_extraobject图片附加信息,示例为 {}。
必选media_profilesobject图片传输信息。

media_profiles

是否必选参数名类型参数说明
必选media_data_typestring本例为 url,表示通过 URL 传入图片。

params

先构造包含以下字段的对象,再将整个对象序列化为字符串。这里没有 parameter 包装层,prompt 本身是字符串。

是否必选参数名类型参数说明
是promptstring套图生成要求,例如“生成一组亚马逊套图,一共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_idstring请求标识。
trace_idstring链路标识。
codeint业务状态码,0 表示正常,非 0 表示错误。
error_codeint错误码,示例中与 code 一致。
messagestring响应消息或错误信息。
dataobject任务状态与结果。
tipsnull附加提示,本文失败示例中为 null,可能不返回。

data

字段类型说明
statusint状态码:-1 未找到任务;0 创建成功;1 执行中;2 失败;9 任务超时(同步等待超时),使用任务查询接口继续查询;10 成功。
resultobject任务结果封装;尚未完成时可能仅包含 id。
task_idstring网关任务 ID,查询时作为 task_id 传入。
progressnumber任务进度。
predict_elapsednumber预计耗时。
create_timenumber任务创建时间。
custom_task_idstring自定义任务标识。
trace_idstring任务链路标识。
client_infostring客户端附加信息。
init_imagesarray/null输入图片信息,示例中为 null。

result

字段类型说明
idstring网关任务 ID,与 data.task_id 对应。
codeint算法状态码。
dataobject算法返回的原始结果,字段如下。
msgstring算法响应消息。
msg_idstring算法请求标识,不用于替代网关 task_id。

result.data

字段类型说明
error_codeint错误码,0 表示成功。
error_msgstring处理结果或错误说明。
msg_idstring算法请求的唯一标识。此标识及算法结果保留 24 小时后删除;网关查询使用 data.task_id。
media_info_listobject[]处理后的图片列表。
parameterobject/null结果返回方式及算法版本信息;失败时可能为 null。
durationobject处理过程的详细耗时。
extraobject附加信息;示例中包含 algo_msg。
callback_msgstring回调相关信息,可为空字符串。

result.data.media_info_list 元素

字段类型说明
media_datastring结果图片 URL 或 Base64 编码数据。
media_extraobject/null图片附加信息,可为 null。
media_profilesobject结果图片的传输信息。
media_profiles.media_data_typestringurl 表示 URL,jpg 表示 Base64 编码数据。

result.data.parameter

字段类型说明
rsp_media_typestring图片返回方式:url 或 jpg。
versionstring算法版本,返回示例为 1.0.0。

result.data.duration

字段类型说明
created_timestampint算法接收请求的时间戳,单位为秒。
pull_timestampint任务出队列的时间戳,单位为秒。
waiting_timeint队列等待耗时,单位为毫秒。
alg_process_timeint算法处理耗时,包含下载时间,单位为毫秒。
upload_timeint上传耗时,单位为毫秒。
repost_timeint异步回调接口耗时,单位为毫秒。

返回 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();
        }
    }
}