智能消除

描述

消除图片中指定区域的内容,并使用填充模型补全该区域。支持涂抹和框选两种方式:

  • 涂抹方式:依次传入原图、标记消除范围的 mask 图。
  • 框选方式:传入原图,并通过 userboxes 指定相对坐标区域。

采用涂抹方式时,userboxes 和 use_sod 不生效。

版本

1.0

图片要求

支持 JPG、PNG,通过图片 URL 传入。涂抹方式必须按“原图在前、mask 图在后”的顺序传入两张图片。

调用 URL

https://openapi.meitu.com/api/v1/sdk/sync/push

任务名称(task):/v1/smart_eraser/488142

任务类型(task_type):formula

调用方法

POST

Content-Type: application/json

权限

使用已开通该能力的 AK/SK,通过签名 SDK 生成请求签名。详见开放平台接口签名。

请求参数

是否必选参数名类型参数说明
是sync_timeoutinteger同步等待时间,单位为秒。本文示例为 10,原文未说明默认值。
是task_typestring固定为 formula。
是init_imagesobject[]输入图片列表。框选方式传原图;涂抹方式依次传原图、mask 图。
是taskstring固定为 /v1/smart_eraser/488142。
是paramsstring算法参数对象序列化后的 JSON 字符串。

init_images 元素

是否必选参数名类型参数说明
是urlstring可访问的图片 URL。

params

解码后的对象包含 parameter,其字段如下。HTTP 请求体为 JSON 对象,仅 params 的值是 JSON 字符串,不要把整个请求体再次编码为字符串。

parameter

是否必选参数名类型参数说明
否rsp_media_typestringurl:返回短期有效的结果图片 URL,请及时下载保存。
否userboxesstring框选区域的 JSON 字符串。最外层为框列表,每个框包含四个相对坐标点,按左上、右上、右下、左下顺序排列。涂抹方式下不生效。
否use_sodstring是否对框选区域使用显著性分割。原文示例使用字符串 "true",不是布尔值。涂抹方式下不生效。
否model_typestring填充模型:lama 或 fcf。原文未说明默认值。

单个框的 userboxes 字符串值:

[[[0.1,0.1],[0.4,0.1],[0.4,0.4],[0.1,0.4]]]

输入值示例

涂抹方式

替换原图和 mask 图 URL。由于 use_sod 在涂抹方式下不生效,示例中省略该参数。

{
  "sync_timeout": 10,
  "task_type": "formula",
  "init_images": [
    {
      "url": "https://example.com/input.png"
    },
    {
      "url": "https://example.com/mask.png"
    }
  ],
  "task": "/v1/smart_eraser/488142",
  "params": "{\"parameter\":{\"rsp_media_type\":\"url\",\"model_type\":\"lama\"}}"
}

框选方式

替换图片 URL 和区域坐标。params 解码后,userboxes 仍为字符串。

{
  "sync_timeout": 10,
  "task_type": "formula",
  "init_images": [
    {
      "url": "https://example.com/input.png"
    }
  ],
  "task": "/v1/smart_eraser/488142",
  "params": "{\"parameter\":{\"rsp_media_type\":\"url\",\"model_type\":\"lama\",\"userboxes\":\"[[[0.1,0.1],[0.4,0.1],[0.4,0.4],[0.1,0.4]]]\",\"use_sod\":\"true\"}}"
}

框选方式的 params 解码结果:

{
  "parameter": {
    "rsp_media_type": "url",
    "model_type": "lama",
    "userboxes": "[[[0.1,0.1],[0.4,0.1],[0.4,0.4],[0.1,0.4]]]",
    "use_sod": "true"
  }
}

返回值说明

code = 0 表示请求处理正常,仍需检查 data.status。以下字段沿用旧版接口资料,其中结果图片路径为 data.result.urls[0],新配方是否保持该路径尚待验证。

字段类型说明
codeinteger0 表示请求成功,非零表示业务错误。
messagestring响应消息,原文失败示例为 PROCESS_ERROR;成功示例未包含。
dataobject任务详情。
data.statusinteger原文明确 9 为同步等待超时、2 为失败、10 为成功。
data.resultobject任务结果。
data.progressnumber任务进度,原文示例为 1。
data.predict_elapsednumber预计耗时,原文未说明单位。

data.result

字段类型说明
idstring网关任务 ID。
urlsstring[]结果图片地址列表,索引 0 为处理结果图片。
codeinteger原文失败示例中的算法状态码。

返回值示例

以下结构保留自旧版接口文档,仅供参考。尚未验证 /v1/smart_eraser/488142 配方的实际响应,结果字段路径和查询 ID 位置需以该配方实际响应为准;示例不是本次调用结果。

请求成功示例

HTTP 200。

{
  "code": 0,
  "data": {
    "status": 10,
    "result": {
      "id": "example-task-id",
      "urls": [
        "https://example.com/output.jpg"
      ]
    },
    "progress": 1,
    "predict_elapsed": 10000
  }
}

需要查询时

返回 data.status = 9 表示同步等待超时,不代表任务失败。将原文给出的 data.result.id 作为任务 ID,按任务查询接口继续获取结果,不要重复提交。

通用查询状态还包括 -1(未找到)、0(已创建)、1(执行中),以查询接口说明为准。

请求失败示例

原文示例为 HTTP 200,业务错误由 code 和 data.status 表示,不能仅凭 HTTP 状态判断任务成功。

{
  "code": 20001,
  "message": "PROCESS_ERROR",
  "data": {
    "status": 2,
    "result": {
      "id": "example-task-id",
      "code": 20001
    },
    "progress": 1
  }
}

通用的错误代码与信息

20001 / PROCESS_ERROR 表示处理错误。完整通用错误码见 API 错误码。

SDK 调用示例

以下四种示例均使用上方涂抹请求,内嵌完整请求体,无需额外 JSON 文件。按对应文档引入签名 SDK 后,替换 AK、SK、原图 URL 和 mask 图 URL。

示例提交一次并打印响应。使用框选方式时,按上方框选 JSON 调整请求体;签名和发送必须使用相同的最终请求体。

Python

Python 签名 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": {
            "rsp_media_type": "url",
            "model_type": "lama"
        }
    }
    payload = {
        "sync_timeout": 10,
        "task_type": "formula",
        "init_images": [
            {
                "url": "https://example.com/input.png"
            },
            {
                "url": "https://example.com/mask.png"
            }
        ],
        "task": "/v1/smart_eraser/488142",
        "params": json.dumps(inner_params, ensure_ascii=True)
    }
    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 接入文档

package main

import (
    "fmt"
    "io"
    "net/http"
    "time"

    "github.com/formula/api/signer"
)

func main() {
    body := `{
  "sync_timeout": 10,
  "task_type": "formula",
  "init_images": [
    {
      "url": "https://example.com/input.png"
    },
    {
      "url": "https://example.com/mask.png"
    }
  ],
  "task": "/v1/smart_eraser/488142",
  "params": "{\"parameter\":{\"rsp_media_type\":\"url\",\"model_type\":\"lama\"}}"
}`
    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 接入文档

<?php
require_once __DIR__ . '/signer.php';

$innerParams = json_encode([
    'parameter' => [
        'rsp_media_type' => 'url',
        'model_type' => 'lama'
    ]
], JSON_UNESCAPED_SLASHES);
$body = json_encode([
    'sync_timeout' => 10,
    'task_type' => 'formula',
    'init_images' => [
        [
            'url' => 'https://example.com/input.png'
        ],
        [
            'url' => 'https://example.com/mask.png'
        ]
    ],
    'task' => '/v1/smart_eraser/488142',
    'params' => $innerParams
], 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 接入文档

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" +
                "  \"sync_timeout\": 10,\n" +
                "  \"task_type\": \"formula\",\n" +
                "  \"init_images\": [\n" +
                "    {\n" +
                "      \"url\": \"https://example.com/input.png\"\n" +
                "    },\n" +
                "    {\n" +
                "      \"url\": \"https://example.com/mask.png\"\n" +
                "    }\n" +
                "  ],\n" +
                "  \"task\": \"/v1/smart_eraser/488142\",\n" +
                "  \"params\": \"{\\\"parameter\\\":{\\\"rsp_media_type\\\":\\\"url\\\",\\\"model_type\\\":\\\"lama\\\"}}\"\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();
        }
    }
}