衣服祛皱

描述

对输入图片中的衣服进行去皱处理,减轻衣物褶皱。通过 alpha 设置去皱程度,并以图片 URL 或 Base64 编码数据获取处理结果。

图片要求

  • 输入格式:JPG、JPEG、PNG、BMP、HEIF。
  • 输入文件大小:不超过 30 MB。
  • 输入图片数量:1 张。

调用 URL

任务类型(task_type)固定为 formula。不同地区使用以下对应的请求地址和任务名称:

地区请求地址任务名称(task)签名 Host
中国https://openapi.meitu.com/api/v1/sdk/sync/push/v1/Clothes_wrinkle_removal/470438openapi.meitu.com
新加坡https://openapi.starii.com/api/v1/sdk/sync/push/v1/Clothes_wrinkle_removal/251001openapi.starii.com
法兰克福https://openapi-fra.starii.com/api/v1/sdk/sync/push/v1/Clothes_wrinkle_removal/47611openapi-fra.starii.com

本文请求和 SDK 示例均使用中国节点。切换地区时,同时修改请求地址、task 和签名请求头中的 Host,使用同一行配置。

调用方法

POST

Content-Type: application/json

权限

使用 Access Key(AK)和 Secret Key(SK)进行请求签名,详见开放平台接口签名

请求参数

是否必选参数名类型参数说明
必选taskstring使用所选地区对应的任务名称。中国节点为 /v1/Clothes_wrinkle_removal/470438
必选task_typestring固定为 formula
必选init_imagesobject[]输入图片列表,传入 1 张需要去皱的图片。
必选paramsstring算法参数序列化后的 JSON 字符串,结构见下文。
可选sync_timeoutint同步等待时间,单位为秒,默认 30-1 表示不等待。返回 data.status = 9 时,使用任务查询接口获取结果。

init_images 元素

是否必选参数名类型参数说明
必选urlstring图片 URL 或 Base64 编码数据;传输方式由 profile.media_profiles.media_data_type 指定。
可选profileobject图片传输信息。

profile

是否必选参数名类型参数说明
可选media_extraobject多媒体附加参数。
可选media_profilesobject多媒体传输信息。
可选versionstring示例使用 v1

media_profiles

是否必选参数名类型参数说明
可选media_data_typestringurl:通过 URL 传入图片;jpg:通过 Base64 编码传入图片。

params

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

是否必选参数名类型参数说明
必选parameterobject衣服祛皱算法参数。

parameter

是否必选参数名类型参数说明
可选rsp_media_typestring结果返回方式:url 为图片 URL,jpg 为 Base64 编码数据。默认 url
可选alphaint去皱程度,取值范围为 [0, 100]。本文示例使用 100

rsp_media_typealpha 均位于 params 字符串中的 parameter 对象内。

输入值示例

以下示例使用中国节点,通过 URL 传入原图,去皱程度设为 100,结果以 URL 返回。请将 https://example.com/input.jpg 替换为实际可访问的图片地址。

{
  "task": "/v1/Clothes_wrinkle_removal/470438",
  "task_type": "formula",
  "init_images": [
    {
      "url": "https://example.com/input.jpg",
      "profile": {
        "media_profiles": {
          "media_data_type": "url"
        },
        "version": "v1"
      }
    }
  ],
  "params": "{\"parameter\":{\"rsp_media_type\":\"url\",\"alpha\":100}}",
  "sync_timeout": 30
}

返回值说明

以下说明及示例采用 URL 返回方式,展示同步任务接口的任务状态和结果。任务成功后,从 data.result.urls 获取结果图片。

字段类型说明
codeint请求处理状态码,0 表示请求处理正常;非 0 表示请求失败。任务是否完成需同时检查 data.status
messagestring响应消息或错误信息。
dataobject/null任务信息;请求失败时可能为 null

data

字段类型说明
statusint任务状态:-1 未找到任务;0 已创建;1 执行中;2 失败;9 需要查询结果;10 成功。
resultobject任务 ID 和处理结果。
progressnumber任务进度,例如 0.10.851

result

字段类型说明
idstring任务 ID;查询结果时作为 task_id 参数传入。
urlsstring[]任务成功后的结果图片 URL 列表。

data.status = 9 时,使用 data.result.id 调用任务查询接口。中国节点的查询请求为 GET https://openapi.meitu.com/api/v1/sdk/status?task_id=<任务ID>,同样需要签名。查询与任务提交使用同一地区节点及对应的 Host

查询返回状态 01 时表示任务尚未完成;10 表示成功;2 表示失败。请及时查询并保存结果,任务在 24 小时后过期,不支持历史任务查询。

返回值示例

请求成功返回示例

{
  "code": 0,
  "message": "",
  "data": {
    "status": 10,
    "result": {
      "id": "50309bd5-a827-4125-bc96-62039c93770b",
      "urls": [
        "https://example.com/result.png"
      ]
    },
    "progress": 1
  }
}

需要查询返回示例

使用返回的 data.result.id 作为任务查询接口的 task_id

{
  "code": 0,
  "message": "",
  "data": {
    "status": 9,
    "result": {
      "id": "50309bd5-a827-4125-bc96-62039c93770b"
    },
    "progress": 0
  }
}

请求失败返回示例

{
  "code": 20008,
  "message": "UNSUITABLE_IMAGE",
  "data": null
}

当前 API 特有的错误代码与信息

错误代码错误信息说明
20001PROCESS_ERROR处理错误
20003DETECT_NOT_FACE未检测到人脸
20004MORE_THAN_ONE_FACE检测到多个人脸
20007MISSING_LANDMARK_ARGUMENTS未传入人脸关键点
20008UNSUITABLE_IMAGE图片不符合要求
20009UNSUPPORT_TYPE不支持的 type
20010DETECT_NOT_FACE未检测到第二张图片中的人脸
20011UNSUITABLE_VERTICAL_IMAGE图片高度不符合要求
20012UNSUITABLE_HORIZONTAL_IMAGE图片宽度不符合要求
20013RESOLUTION_TOO_LARGE_ERROR图片分辨率过大
20014NOT_FOUND未找到图片
20015PICTURE_OVERRUN_ERROR图片超出限制
20020DETECT_FACE_OUTOFIMAGE面部五官缺失
20021DETECT_FACE_PITCHANGLE_BIG人脸俯仰角过大
20022DETECT_FACE_YAWANGLE_BIG人脸偏航角过大
20023DETECT_FACE_LOWAREA人脸区域占比过小或像素不足
21001LOAD_MODEL_ERROR模型加载失败
21002HAIR_MASK_LOSS缺少头发蒙版
21003FACE_NUM_ERROR人脸数量错误
21004AR_PARSE_FAULTAR plist 解析失败
21005AR_EEEOR_COUNTAR 人脸错误
21006AR_FACE_OUTAR 超出人脸范围
21007JSON_ERRORJSON 内容错误
21008BACKGROUND_IMAGE_LOSS缺少背景图片
21009BODY_MASK_LOSS缺少身体蒙版
21010FACE_ANGLE_ERROR人脸角度错误
21011SKIN_MASK_LOSS缺少皮肤蒙版
21012BODY_INFO_LOSS缺少骨骼点或外轮廓点
21013RECT_OUT_IMAGE区域超出图片范围
30001GEN_ERROR生成错误

通用的错误代码与信息

详见 API 错误码

SDK 调用示例

以下示例均使用中国节点,设置 alpha = 100,并以 URL 返回处理结果。调用前替换 AK、SK 和输入图片 URL。

示例打印接口响应。返回 data.status = 9 时,按上述说明查询任务结果。切换地区时,按“调用 URL”中的对应关系同步修改请求地址、taskHost

Python

先按 Python 签名 SDK 接入文档引入 SDK。

import json

import requests
from sign_sdk import sign

def api_call_example():
    key = "your_access_key"
    secret = "your_secret_key"
    url = "https://openapi.meitu.com/api/v1/sdk/sync/push"
    headers = {
        "Content-Type": "application/json",
        sign.HeaderHost: "openapi.meitu.com",
    }
    inner_params = {
        "parameter": {
            "rsp_media_type": "url",
            "alpha": 100
        }
    }
    payload = {
        "task": "/v1/Clothes_wrinkle_removal/470438",
        "task_type": "formula",
        "init_images": [
            {
                "url": "https://example.com/input.jpg",
                "profile": {
                    "media_profiles": {
                        "media_data_type": "url"
                    },
                    "version": "v1"
                }
            }
        ],
        "params": json.dumps(inner_params, ensure_ascii=False),
        "sync_timeout": 30
    }
    body = json.dumps(payload, ensure_ascii=False)

    signer = sign.Signer(key, secret)
    signed_request = signer.sign(url, "POST", headers, body)
    with requests.Session() as session:
        response = session.send(signed_request, timeout=60)
        print("Status:", response.status_code)
        print("Response:", response.text)

if __name__ == "__main__":
    api_call_example()

Go

先按 Go 签名 SDK 接入文档引入 SDK。

package main

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

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

func main() {
	key := "your_access_key"
	secret := "your_secret_key"
	signObj := signer.NewSigner(key, secret)
	url := "https://openapi.meitu.com/api/v1/sdk/sync/push"
	headers := make(http.Header)
	headers.Set(signer.HeaderHost, "openapi.meitu.com")
	headers.Set("Content-Type", "application/json")

	body := `{
  "task": "/v1/Clothes_wrinkle_removal/470438",
  "task_type": "formula",
  "init_images": [
    {
      "url": "https://example.com/input.jpg",
      "profile": {
        "media_profiles": {
          "media_data_type": "url"
        },
        "version": "v1"
      }
    }
  ],
  "params": "{\"parameter\":{\"rsp_media_type\":\"url\",\"alpha\":100}}",
  "sync_timeout": 30
}`
	req, err := signObj.Sign(url, http.MethodPost, headers, body)
	if err != nil {
		fmt.Println("Failed to sign request:", err)
		return
	}
	client := &http.Client{Timeout: 60 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("Failed to send request:", err)
		return
	}
	defer resp.Body.Close()

	responseBody, err := io.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("Failed to read response:", err)
		return
	}
	fmt.Println("Status:", resp.StatusCode)
	fmt.Println("Response:", string(responseBody))
}

PHP

先按 PHP 签名 SDK 接入文档引入 SDK。

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

$key = 'your_access_key';
$secret = 'your_secret_key';
$url = 'https://openapi.meitu.com/api/v1/sdk/sync/push';
$headers = [
    'Content-Type' => 'application/json',
    'Host' => 'openapi.meitu.com',
];

$innerParams = json_encode([
    'parameter' => [
        'rsp_media_type' => 'url',
        'alpha' => 100,
    ],
], JSON_UNESCAPED_SLASHES);
$body = json_encode([
    'task' => '/v1/Clothes_wrinkle_removal/470438',
    'task_type' => 'formula',
    'init_images' => [
        [
            'url' => 'https://example.com/input.jpg',
            'profile' => [
                'media_profiles' => [
                    'media_data_type' => 'url',
                ],
                'version' => 'v1',
            ],
        ],
    ],
    'params' => $innerParams,
    'sync_timeout' => 30,
], JSON_UNESCAPED_SLASHES);

$signer = new Signer($key, $secret);
$curl = $signer->sign($url, 'POST', $headers, $body);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_TIMEOUT, 60);
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 {
        Signer signer = new Signer("your_access_key", "your_secret_key");
        String url = "https://openapi.meitu.com/api/v1/sdk/sync/push";
        String method = "POST";
        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        headers.put(Signer.HeaderHost, "openapi.meitu.com");

        String body = "{\n"
                + "  \"task\": \"/v1/Clothes_wrinkle_removal/470438\",\n"
                + "  \"task_type\": \"formula\",\n"
                + "  \"init_images\": [\n"
                + "    {\n"
                + "      \"url\": \"https://example.com/input.jpg\",\n"
                + "      \"profile\": {\n"
                + "        \"media_profiles\": {\n"
                + "          \"media_data_type\": \"url\"\n"
                + "        },\n"
                + "        \"version\": \"v1\"\n"
                + "      }\n"
                + "    }\n"
                + "  ],\n"
                + "  \"params\": \"{\\\"parameter\\\":{\\\"rsp_media_type\\\":\\\"url\\\",\\\"alpha\\\":100}}\",\n"
                + "  \"sync_timeout\": 30\n"
                + "}";

        Map<String, String> signedHeaders = signer.sign(url, method, headers, body);
        HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
        try {
            connection.setRequestMethod(method);
            connection.setConnectTimeout(10000);
            connection.setReadTimeout(60000);
            connection.setInstanceFollowRedirects(false);
            for (Map.Entry<String, String> entry : signedHeaders.entrySet()) {
                connection.setRequestProperty(entry.getKey(), entry.getValue());
            }
            connection.setDoOutput(true);
            try (OutputStream output = connection.getOutputStream()) {
                output.write(body.getBytes(StandardCharsets.UTF_8));
            }

            int status = connection.getResponseCode();
            System.out.println("Status: " + status);
            InputStream stream = status >= 400
                    ? connection.getErrorStream()
                    : connection.getInputStream();
            if (stream != null) {
                try (InputStream input = stream;
                     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: "
                            + new String(output.toByteArray(), StandardCharsets.UTF_8));
                }
            }
        } finally {
            connection.disconnect();
        }
    }
}