Hairstyle Transfer

Description

Replaces the hairstyle in a portrait image using a specified hairstyle identifier and returns the processed image URLs.

Version

1.0

Image Requirements

Provide a portrait image URL. The example uses one image with media_data_type set to url.

Request URL

    Production environment: https://openapi.meitu.com
    Task submission endpoint: https://openapi.meitu.com/api/v1/sdk/sync/push
    Task name (task): /v1/hairtransfer/495184
    Task type (task_type): formula

HTTP Method

POST

Content-Type: application/json

X-Aigcp-Way: handler

Authentication

Obtain an Access Key and Secret Key, then sign the request as described in Open Platform API Signature. The signing SDK generates Authorization and X-Sdk-Date.

Request Parameters

RequiredParameterTypeDescription
YestaskstringFixed value: /v1/hairtransfer/495184
Yestask_typestringFixed value: formula
Yesinit_imagesobject[]Input portrait image list
YesparamsstringAlgorithm parameters encoded as a JSON string containing a parameter object
Nosync_timeoutintSynchronous wait time in seconds. The example uses 30; -1 returns immediately without waiting. Use the returned task ID to query the result if processing is not complete

Structure of each init_images item:

RequiredParameterTypeDescription
YesurlstringInput portrait image URL
NoprofileobjectImage metadata

Structure of profile:

RequiredParameterTypeDescription
Yesmedia_profilesobjectImage properties; required when profile is provided
NoversionstringVersion. Default: v1

Structure of media_profiles:

RequiredParameterTypeDescription
Nomedia_data_typestringMedia data type. Default: url, indicating an image URL

params is a JSON string. Its decoded structure is:

{
  "parameter": {
    "rsp_media_type": "url",
    "hair_type": "1"
  }
}
RequiredParameterTypeDescription
YesparameterobjectHairstyle processing parameters

Structure of parameter:

RequiredParameterTypeDescription
Norsp_media_typestringOutput media type. url returns image URLs
Nohair_typestringHairstyle identifier. 0 keeps the original hairstyle. For other identifiers, see Reference Hairstyles. Pass a string, such as "1" or "1_II"

Request Example

This example uses hairstyle identifier "1". Replace the image URL and save the request body as request.json.

{
  "task": "/v1/hairtransfer/495184",
  "task_type": "formula",
  "init_images": [
    {
      "url": "https://example.com/portrait.jpg",
      "profile": {
        "media_profiles": {
          "media_data_type": "url"
        },
        "version": "v1"
      }
    }
  ],
  "params": "{\"parameter\":{\"rsp_media_type\":\"url\",\"hair_type\":\"1\"}}",
  "sync_timeout": 30
}

Response Fields

Success Response Fields

FieldTypeDescription
request_idstringRequest ID
trace_idstringTrace ID
codeintRequest status code. 0 indicates request success; use data.status to determine task completion
error_codeintBusiness error code. 0 indicates no business error
messagestringResponse message
dataobjectTask status and processing result

Fields in data:

FieldTypeDescription
statusintTask status. 10 indicates success and 2 indicates failure. While processing is incomplete, retrieve updates with the task query API
resultobjectProcessing result
progressnumberTask progress from 0 to 1; 1 indicates completion
predict_elapsednumberEstimated processing time in milliseconds
create_timeint64Task creation timestamp in milliseconds
task_idstringTask ID used to query the task
custom_task_idstringCustom task ID
trace_idstringTrace ID
client_infostringClient information
init_imagesobject[]/nullInput image information

Fields in data.result:

FieldTypeDescription
idstringTask ID
urlsstring[]Result image URLs
imagesstring[]Result image URLs, equivalent to urls
parametersobjectResult parameters
dataobjectDetailed algorithm result
msgstringResult message
msg_idstringMessage ID
mtlab_resobjectAlgorithm response status
media_info_listobject[]Result image metadata

Fields in data.result.data:

FieldTypeDescription
durationobjectTiming information for processing stages
error_codeintAlgorithm error code; 0 indicates success
error_msgstringAlgorithm response message
extraobjectAdditional image conversion information
media_info_listobject[]Result image metadata
msg_idstringMessage ID
parameterobjectAlgorithm result parameters

Fields in data.result.data.duration:

FieldTypeDescription
alg_process_timenumberAlgorithm processing time in milliseconds
created_timestampnumberTask creation timestamp in seconds
pull_timestampnumberResource retrieval timestamp in seconds
repost_timenumberRetry duration
upload_timenumberUpload time in milliseconds
waiting_timenumberQueue wait time in milliseconds

Fields in data.result.data.extra:

FieldTypeDescription
trans_metaobjectConversion metadata
trans_meta.rsp_metaobject[]Result image metadata list

Fields in each rsp_meta item:

FieldTypeDescription
heightnumberImage height in pixels
widthnumberImage width in pixels
media_typestringMedia type, such as image
sizenumberFile size in KB

Fields in each media_info_list item in the result:

FieldTypeDescription
media_datastringResult image URL
media_extraobject/nullAdditional image information
media_profilesobjectResult image properties

Fields in result media_profiles:

FieldTypeDescription
media_data_sizenumber[]Image dimensions in [height, width] order
media_data_typestringMedia data type; url indicates a URL

Fields in data.result.parameters and data.result.data.parameter:

FieldTypeDescription
rsp_media_typestringOutput media type
versionstringAlgorithm version

Fields in data.result.mtlab_res:

FieldTypeDescription
ErrorCodeintAlgorithm error code
ErrorMsgstringAlgorithm error message
error_codeintCompatibility error code field
error_msgstringCompatibility error message field
media_info_listobject[]/nullMedia information list
msg_idstringMessage ID
parameterobject/nullAlgorithm parameter information

Error Response Fields

FieldTypeDescription
error_codeintError code
messagestringError message
datastring/nullAdditional error information; may be null

Response Examples

Success Response Example

Response Status: 200

Content-Type: application/json; charset=utf-8

The example shows task information and output image URLs; additional result details are omitted.

{
  "request_id": "req_example",
  "trace_id": "trace_example",
  "code": 0,
  "error_code": 0,
  "message": "success",
  "data": {
    "status": 10,
    "result": {
      "id": "task_example",
      "urls": [
        "https://example.com/result_1.jpeg",
        "https://example.com/result_2.jpeg"
      ],
      "parameters": {
        "rsp_media_type": "url",
        "version": "1.0.32"
      }
    },
    "progress": 1,
    "predict_elapsed": 10000,
    "create_time": 1776137629570,
    "task_id": "task_example",
    "custom_task_id": "",
    "trace_id": "trace_example",
    "client_info": "",
    "init_images": null
  }
}

Error Response Example

Response Status: 400

Content-Type: application/json; charset=utf-8

{
  "error_code": 20001,
  "message": "PROCESS_ERROR",
  "data": null
}

Task Query

When sync_timeout=-1, or the submission response does not contain the final result, use the returned data.task_id with the task query API.

GET https://openapi.meitu.com/api/v1/sdk/status?task_id=YOUR_TASK_ID

Query requests also require AK/SK signing. Continue querying while the query status is 0 (created) or 1 (processing). 10 means success, 2 means failure, and -1 means the task was not found. Tasks expire after 24 hours and cannot be queried after expiration.

API-Specific Error Codes

ErrorCodeMessageDescription
20001PROCESS_ERRORProcessing failed

Common Error Codes

See API Error Codes.

Calling Examples

Signing SDK integration guides: Go, Java, Python, JavaScript, PHP.

cURL

SDK_DATE is the UTC timestamp used for signing. SIGNATURE is the generated authorization value without the Bearer prefix. Both must correspond to the current request.json and the headers below. See Open Platform API Signature for generation instructions.

curl --request POST \
  --url 'https://openapi.meitu.com/api/v1/sdk/sync/push' \
  --header 'Host: openapi.meitu.com' \
  --header 'Content-Type: application/json' \
  --header 'X-Aigcp-Way: handler' \
  --header "X-Sdk-Date: ${SDK_DATE}" \
  --header "Authorization: Bearer ${SIGNATURE}" \
  --data-binary @request.json

Python

Download Python SDK 1.0.2, add the sign_sdk directory to the project, and install requests. Replace the AK, SK, and image URL before running.

import json

import requests
from sign_sdk import sign

access_key = "your_access_key"
secret_key = "your_secret_key"
url = "https://openapi.meitu.com/api/v1/sdk/sync/push"

params = {
    "parameter": {
        "rsp_media_type": "url",
        "hair_type": "1",
    }
}
payload = {
    "task": "/v1/hairtransfer/495184",
    "task_type": "formula",
    "init_images": [
        {
            "url": "https://example.com/portrait.jpg",
            "profile": {
                "media_profiles": {"media_data_type": "url"},
                "version": "v1",
            },
        }
    ],
    "params": json.dumps(params, separators=(",", ":")),
    "sync_timeout": 30,
}
body = json.dumps(payload, separators=(",", ":"))
headers = {
    "Host": "openapi.meitu.com",
    "Content-Type": "application/json",
    "X-Aigcp-Way": "handler",
}

signer = sign.Signer(access_key, secret_key)
request = signer.sign(url, "POST", headers, body)

with requests.Session() as session:
    response = session.send(request, timeout=(10, 60))
    print("Status:", response.status_code)
    print("Response:", response.text)

Go

Download Go SDK 1.0.3 and use Go 1.20 or later. Save the code below as demo.go in the extracted directory, replace the AK, SK, and image URL, then run go run .

package main

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

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

func main() {
	accessKey := "your_access_key"
	secretKey := "your_secret_key"
	endpoint := "https://openapi.meitu.com/api/v1/sdk/sync/push"
	body := `{
  "task": "/v1/hairtransfer/495184",
  "task_type": "formula",
  "init_images": [
    {
      "url": "https://example.com/portrait.jpg",
      "profile": {
        "media_profiles": {
          "media_data_type": "url"
        },
        "version": "v1"
      }
    }
  ],
  "params": "{\"parameter\":{\"rsp_media_type\":\"url\",\"hair_type\":\"1\"}}",
  "sync_timeout": 30
}`

	headers := make(http.Header)
	headers.Set("Host", "openapi.meitu.com")
	headers.Set("Content-Type", "application/json")
	headers.Set("X-Aigcp-Way", "handler")

	sign := signer.NewSigner(accessKey, secretKey)
	request, err := sign.Sign(endpoint, http.MethodPost, headers, body)
	if err != nil {
		fmt.Println("Sign request failed:", err)
		return
	}

	client := &http.Client{Timeout: 60 * time.Second}
	response, err := client.Do(request)
	if err != nil {
		fmt.Println("Send request failed:", err)
		return
	}
	defer response.Body.Close()

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

PHP

Download PHP SDK 1.0.9, place signer.php in the same directory as the example, and enable the cURL extension.

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

$accessKey = 'your_access_key';
$secretKey = 'your_secret_key';
$url = 'https://openapi.meitu.com/api/v1/sdk/sync/push';

$params = json_encode([
    'parameter' => [
        'rsp_media_type' => 'url',
        'hair_type' => '1',
    ],
]);
$body = json_encode([
    'task' => '/v1/hairtransfer/495184',
    'task_type' => 'formula',
    'init_images' => [
        [
            'url' => 'https://example.com/portrait.jpg',
            'profile' => [
                'media_profiles' => ['media_data_type' => 'url'],
                'version' => 'v1',
            ],
        ],
    ],
    'params' => $params,
    'sync_timeout' => 30,
]);
$headers = [
    'Host' => 'openapi.meitu.com',
    'Content-Type' => 'application/json',
    'X-Aigcp-Way' => 'handler',
];

$signer = new Signer($accessKey, $secretKey);
$curl = $signer->sign($url, 'POST', $headers, $body);
curl_setopt_array($curl, [
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 60,
    CURLOPT_HEADER => false,
]);

$response = curl_exec($curl);
if ($response === false) {
    echo 'Error: ' . curl_error($curl);
} else {
    echo 'Status: ' . curl_getinfo($curl, CURLINFO_HTTP_CODE) . PHP_EOL;
    echo 'Response: ' . $response . PHP_EOL;
}
curl_close($curl);

Java

Download Java SDK 1.0.3 and use JDK 8 or later. Add src/main/java/com/meitu/openai/common/Signer.java from the package to the project and save the example as Main.java.

import com.meitu.openai.common.Signer;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
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 accessKey = "your_access_key";
        String secretKey = "your_secret_key";
        String url = "https://openapi.meitu.com/api/v1/sdk/sync/push";
        String body = "{\n"
                + "  \"task\": \"/v1/hairtransfer/495184\",\n"
                + "  \"task_type\": \"formula\",\n"
                + "  \"init_images\": [\n"
                + "    {\n"
                + "      \"url\": \"https://example.com/portrait.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\\\",\\\"hair_type\\\":\\\"1\\\"}}\",\n"
                + "  \"sync_timeout\": 30\n"
                + "}";

        Map<String, String> headers = new HashMap<>();
        headers.put("Host", "openapi.meitu.com");
        headers.put("Content-Type", "application/json");
        headers.put("X-Aigcp-Way", "handler");

        Signer signer = new Signer(accessKey, secretKey);
        Map<String, String> signedHeaders = signer.sign(url, "POST", headers, body);

        HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
        connection.setRequestMethod("POST");
        connection.setConnectTimeout(10_000);
        connection.setReadTimeout(60_000);
        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();
        InputStream input = status >= 400
                ? connection.getErrorStream()
                : connection.getInputStream();
        System.out.println("Status: " + status);
        if (input != null) {
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(input, StandardCharsets.UTF_8))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    System.out.println(line);
                }
            }
        }
        connection.disconnect();
    }
}