双人合影
描述
传入两张图片,调用双人合影算法,返回处理后的合影图片。接口默认异步处理,提交后使用任务 ID 查询结果。
版本
1.0
图片要求
每次请求传入两张图片,通过 URL 提供。
调用 URL
正式环境:https://openapi.meitu.com
任务提交接口:
https://openapi.meitu.com/api/v1/sdk/sync/push任务名称(task):/v1/couples_photo/default/488650
任务类型(task_type):formula
调用方法
POST
Content-Type: application/json
权限
使用已开通该能力的 AK/SK,通过签名 SDK 生成请求签名。详见开放平台接口签名。
请求参数
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 是 | task | string | 固定为 /v1/couples_photo/default/488650。 |
| 是 | task_type | string | 固定为 formula。 |
| 是 | init_images | object[] | 两张输入图片。 |
| 是 | params | string | 推理参数;传入空字符串 "" 使用默认参数。 |
| 否 | sync_timeout | integer | 默认 -1,不等待生成完成,立即返回任务信息,后续通过任务查询接口获取结果。 |
init_images 元素
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 是 | url | string | 可访问的图片 URL。 |
| 否 | profile | object | 图片属性配置。 |
profile
传入 profile 时,包含以下字段:
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 是 | media_profiles | object | 媒体属性。 |
media_profiles
| 是否必选 | 参数名 | 类型 | 参数说明 |
|---|---|---|---|
| 否 | media_data_type | string | 默认 url,表示通过 URL 传入图片。 |
params
本接口支持 params: "" 表示使用默认参数。保持空字符串,不替换为对象、null 或字符串 "{}"。原文未列出可配置的推理参数。
输入值示例
将两张图片的 URL 替换为实际地址:
{
"task": "/v1/couples_photo/default/488650",
"task_type": "formula",
"init_images": [
{
"url": "https://example.com/person-1.png",
"profile": {
"media_profiles": {
"media_data_type": "url"
}
}
},
{
"url": "https://example.com/person-2.png",
"profile": {
"media_profiles": {
"media_data_type": "url"
}
}
}
],
"params": "",
"sync_timeout": -1
}返回值说明
code=0、error_code=0 表示请求处理正常,不代表任务已经完成。检查 data.status:为 10 时读取 data.result.urls;为 9 时继续查询任务。请及时下载保存结果图片。
| 字段 | 类型 | 说明 |
|---|---|---|
| request_id / trace_id | string | 请求 ID、链路追踪 ID。 |
| code | integer | 响应中的状态码,0 表示请求处理正常;不是 HTTP 状态码。 |
| error_code | integer | 错误码,0 表示无错误。 |
| message | string | 响应消息,成功示例为 success。 |
| data | object / null | 任务详情,失败时可能为 null。 |
data
| 字段 | 类型 | 说明 |
|---|---|---|
| status | integer | -1 未找到;0 已创建;1 执行中;2 失败;9 需要查询;10 成功。 |
| result | object | 任务处理结果。 |
| progress | number | 任务进度,范围 0–1,1 表示完成。 |
| predict_elapsed | number | 预计处理耗时,单位为毫秒。 |
| create_time | number | 任务创建时间戳,单位为毫秒。 |
| task_id | string | 查询使用的网关任务 ID。 |
| custom_task_id / trace_id / client_info | string | 自定义任务 ID、追踪 ID 和客户端信息。 |
| init_images | array / null | 输入媒体信息,成功示例中为 null。 |
result
| 字段 | 类型 | 说明 |
|---|---|---|
| id | string | 任务标识,与示例中的 data.task_id 一致。 |
| urls / images | string[] | 结果图片 URL 列表;示例中两者相同。 |
| parameters | object | 输出参数;可包含 rsp_media_type,url 表示返回 URL,示例为空对象。 |
| data | object | 算法返回的详细数据。 |
| msg / msg_id | string | 算法消息及消息 ID;msg_id 不是任务查询 ID。 |
| mtlab_res | object | 底层算法兼容结果。 |
| media_info_list | object[] | 输出媒体信息列表。 |
result.data
| 字段 | 类型 | 说明 |
|---|---|---|
| duration | object | 各阶段时间和耗时信息,见下表。 |
| error_code / error_msg | integer / string | 算法错误码和消息,error_code=0 表示成功。 |
| extra | object | 扩展信息。 |
| media_info_list | object[] | 结果媒体列表。 |
| msg_id | string | 算法消息 ID。 |
| parameter | object | 参数回显。 |
result.data.duration
| 字段 | 类型 | 说明 |
|---|---|---|
| alg_process_time | number | 算法处理耗时,毫秒。 |
| created_timestamp / pull_timestamp | number | 创建及资源拉取时间戳,秒。 |
| upload_time / waiting_time | number | 上传及排队等待耗时,毫秒。 |
| repost_time | number | 原文标为重试耗时,未说明单位。 |
media_info_list 元素
适用于 data.result.media_info_list 及 data.result.data.media_info_list。
| 字段 | 类型 | 说明 |
|---|---|---|
| media_data | string | 结果图片 URL。 |
| media_extra | object | 媒体扩展信息。 |
| media_profiles | object | 媒体属性。 |
| media_profiles.media_data_type | string | url 表示 media_data 为 URL 地址。 |
result.mtlab_res
| 字段 | 类型 | 说明 |
|---|---|---|
| ErrorCode / error_code | integer | 底层错误码及兼容字段。 |
| ErrorMsg / error_msg | string | 底层错误消息及兼容字段。 |
| media_info_list | array / null | 媒体信息,成功示例中为 null。 |
| msg_id | string | 算法消息 ID。 |
| parameter | object / null | 底层参数,成功示例中为 null。 |
返回值示例
成功及失败结构来自原文,任务 ID、算法消息 ID 和图片 URL 已替换为示意值,非本次实际调用结果。成功示例展示任务完成后的结果,不表示默认异步提交会立即返回成品图片。
请求成功返回示例
{
"request_id": "",
"trace_id": "",
"code": 0,
"error_code": 0,
"message": "success",
"data": {
"status": 10,
"result": {
"id": "example-task-id",
"urls": [
"https://example.com/couples-photo.png"
],
"parameters": {},
"data": {
"duration": {
"alg_process_time": 2396,
"created_timestamp": 1775548648,
"pull_timestamp": 1775548648,
"repost_time": 0,
"upload_time": 74,
"waiting_time": 58
},
"error_code": 0,
"error_msg": "success",
"extra": {},
"media_info_list": [
{
"media_data": "https://example.com/couples-photo.png",
"media_extra": {},
"media_profiles": {
"media_data_type": "url"
}
}
],
"msg_id": "example-algorithm-msg-id",
"parameter": {}
},
"msg": "success",
"msg_id": "example-algorithm-msg-id",
"images": [
"https://example.com/couples-photo.png"
],
"mtlab_res": {
"ErrorCode": 0,
"ErrorMsg": "",
"error_code": 0,
"error_msg": "",
"media_info_list": null,
"msg_id": "example-algorithm-msg-id",
"parameter": null
},
"media_info_list": [
{
"media_data": "https://example.com/couples-photo.png",
"media_extra": {},
"media_profiles": {
"media_data_type": "url"
}
}
]
},
"progress": 1,
"predict_elapsed": 10000,
"create_time": 1775548616543,
"task_id": "example-task-id",
"custom_task_id": "",
"trace_id": "",
"client_info": "",
"init_images": null
}
}需要查询返回示例
以下为待查询状态的精简结构示意。异步提交或同步等待未完成时,保存 data.task_id,按任务查询接口继续查询,不要重复提交,也不要使用 msg_id 查询。
{
"request_id": "",
"trace_id": "",
"code": 0,
"error_code": 0,
"message": "success",
"data": {
"status": 9,
"result": {
"id": "example-task-id"
},
"progress": 0,
"task_id": "example-task-id"
}
}请求失败返回示例
原文失败示例为 HTTP 400:
{
"error_code": 20001,
"message": "PROCESS_ERROR",
"data": null
}通用的错误代码与信息
20001 / PROCESS_ERROR 表示处理错误。其他错误详见 API 错误码。
SDK 调用示例
以下示例内嵌完整请求体,无需额外 JSON 文件。按对应文档引入签名 SDK 后,替换 AK、SK 和两张图片的 URL。所有示例保留 params="" 和 sync_timeout=-1。
示例提交一次并打印响应;任务未完成时通过任务查询接口获取结果。签名和发送使用相同的 URL、方法和请求体。
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"
payload = {
"task": "/v1/couples_photo/default/488650",
"task_type": "formula",
"init_images": [
{
"url": "https://example.com/person-1.png",
"profile": {
"media_profiles": {
"media_data_type": "url"
}
}
},
{
"url": "https://example.com/person-2.png",
"profile": {
"media_profiles": {
"media_data_type": "url"
}
}
}
],
"params": "",
"sync_timeout": -1
}
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 := `{
"task": "/v1/couples_photo/default/488650",
"task_type": "formula",
"init_images": [
{
"url": "https://example.com/person-1.png",
"profile": {
"media_profiles": {
"media_data_type": "url"
}
}
},
{
"url": "https://example.com/person-2.png",
"profile": {
"media_profiles": {
"media_data_type": "url"
}
}
}
],
"params": "",
"sync_timeout": -1
}`
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';
$body = json_encode([
'task' => '/v1/couples_photo/default/488650',
'task_type' => 'formula',
'init_images' => [
[
'url' => 'https://example.com/person-1.png',
'profile' => [
'media_profiles' => [
'media_data_type' => 'url'
]
]
],
[
'url' => 'https://example.com/person-2.png',
'profile' => [
'media_profiles' => [
'media_data_type' => 'url'
]
]
]
],
'params' => '',
'sync_timeout' => -1
], 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" +
" \"task\": \"/v1/couples_photo/default/488650\",\n" +
" \"task_type\": \"formula\",\n" +
" \"init_images\": [\n" +
" {\n" +
" \"url\": \"https://example.com/person-1.png\",\n" +
" \"profile\": {\n" +
" \"media_profiles\": {\n" +
" \"media_data_type\": \"url\"\n" +
" }\n" +
" }\n" +
" },\n" +
" {\n" +
" \"url\": \"https://example.com/person-2.png\",\n" +
" \"profile\": {\n" +
" \"media_profiles\": {\n" +
" \"media_data_type\": \"url\"\n" +
" }\n" +
" }\n" +
" }\n" +
" ],\n" +
" \"params\": \"\",\n" +
" \"sync_timeout\": -1\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();
}
}
}