智能消除
描述
消除图片中指定区域的内容,并使用填充模型补全该区域。支持涂抹和框选两种方式:
- 涂抹方式:依次传入原图、标记消除范围的 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_timeout | integer | 同步等待时间,单位为秒。本文示例为 10,原文未说明默认值。 |
| 是 | task_type | string | 固定为 formula。 |
| 是 | init_images | object[] | 输入图片列表。框选方式传原图;涂抹方式依次传原图、mask 图。 |
| 是 | task | string | 固定为 /v1/smart_eraser/488142。 |
| 是 | params | string | 算法参数对象序列化后的 JSON 字符串。 |
init_images 元素
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 是 | url | string | 可访问的图片 URL。 |
params
解码后的对象包含 parameter,其字段如下。HTTP 请求体为 JSON 对象,仅 params 的值是 JSON 字符串,不要把整个请求体再次编码为字符串。
parameter
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 否 | rsp_media_type | string | url:返回短期有效的结果图片 URL,请及时下载保存。 |
| 否 | userboxes | string | 框选区域的 JSON 字符串。最外层为框列表,每个框包含四个相对坐标点,按左上、右上、右下、左下顺序排列。涂抹方式下不生效。 |
| 否 | use_sod | string | 是否对框选区域使用显著性分割。原文示例使用字符串 "true",不是布尔值。涂抹方式下不生效。 |
| 否 | model_type | string | 填充模型: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],新配方是否保持该路径尚待验证。
| 字段 | 类型 | 说明 |
|---|---|---|
| code | integer | 0 表示请求成功,非零表示业务错误。 |
| message | string | 响应消息,原文失败示例为 PROCESS_ERROR;成功示例未包含。 |
| data | object | 任务详情。 |
| data.status | integer | 原文明确 9 为同步等待超时、2 为失败、10 为成功。 |
| data.result | object | 任务结果。 |
| data.progress | number | 任务进度,原文示例为 1。 |
| data.predict_elapsed | number | 预计耗时,原文未说明单位。 |
data.result
| 字段 | 类型 | 说明 |
|---|---|---|
| id | string | 网关任务 ID。 |
| urls | string[] | 结果图片地址列表,索引 0 为处理结果图片。 |
| code | integer | 原文失败示例中的算法状态码。 |
返回值示例
以下结构保留自旧版接口文档,仅供参考。尚未验证 /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
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
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
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
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();
}
}
}