万物套图

描述

根据输入图片和结构化提示词重新生成图片。可在提示词中描述需要新增的文字、需要保留与修改的画面内容,以及禁止出现的内容,用于商品展示图等图片的改版与再创作。

版本

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)进行请求签名,详见开放平台接口签名。

请求参数

是否必选参数名类型参数说明
必选paramsstring算法参数序列化后的 JSON 字符串,结构见下文。
必选init_imagesobject[]输入图片列表。
必选taskstring固定为 /v1/Encompassing_Image/495717。
必选task_typestring固定为 formula。
可选sync_timeoutint同步等待超时时间,单位为秒,默认 30。返回 data.status = 9 时继续查询任务结果。

init_images 元素

是否必选参数名类型参数说明
必选urlstring图片 URL 或 Base64 编码数据;URL 地址不进行 Base64 编码。
必选profileobject图片属性信息。

profile

是否必选参数名类型参数说明
必选media_profilesobject媒体传输信息。
必选versionstring固定为 v1。
可选media_extraobject图片附加参数,无附加配置时可传 {}。

media_profiles

是否必选参数名类型参数说明
必选media_data_typestringurl 表示图片 URL,jpg 表示 Base64 编码数据。

params

先构造以下参数对象,再序列化为字符串,作为请求体的 params 值。

是否必选参数名类型参数说明
必选parameterobject算法核心参数。
可选extraobject算法请求的附加信息,无附加配置时可传 {}。

parameter

是否必选参数名类型参数说明
必选promptobject结构化提示词,字段说明和示例见下文。在 params 解码后的 parameter 中保持对象类型。
可选heightnumber输出图片高度,单位为像素。未传入时从 prompt 中解析。
可选widthnumber输出图片宽度,单位为像素。未传入时从 prompt 中解析。
可选rsp_media_typestring结果返回方式:url 为图片 URL,jpg 为 Base64 编码数据。默认 url。
可选seednumber随机种子,默认 42;-1 表示使用随机种子。

prompt

以下字段展示结构化提示词的组织方式。字段名按示例保留,字段值填写具体的图片编辑要求。

字段名类型说明
image_ratiostring输出图片比例,示例值为 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_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 = {
        "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();
        }
    }
}