Facial Landmark Detection

Description

The caller provides an image file or image URL for face detection and facial landmark detection.

Version

2.0

Image Requirements

Image formats: JPG (JPEG), PNG
Image dimensions: minimum 48 × 48 pixels; maximum 4096 × 4096 pixels
Image file size: 10 MB
Minimum face dimensions: The system detects a face using a square bounding box. The minimum side length of this square is 1/48 of the image's shorter side and must be at least 48 pixels. For example, for a 4096 × 3200-pixel image, the minimum face dimensions are 66 × 66 pixels.
Face count limit: up to 10 faces

Facial landmark reference diagrams:

  1. Reference diagram for 118 facial landmarks.

image

  1. Reference diagram for 171 facial landmarks.

image

API URL

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

Method

POST

Content-Type: application/json

Authentication

Open Platform API Signature

Request Parameters

RequiredParameterTypeDescription
YesparamsstringAlgorithm parameters (JSON string)
Yesinit_imagesobject[]List of media files
YestaskstringFixed value: /v1/Facial_Keypoint_Detection/487161
Yestask_typestringFixed value: formula
Nosync_timeoutintDefaults to 30; synchronous timeout duration

The init_images media file parameters are described below.

RequiredParameterTypeDescription
YesurlstringImage URL or base64-encoded data
YesprofileobjectAttribute information

The profile attribute information is described below.

RequiredParameterTypeDescription
Yesmedia_profilesobjectMedia attribute information
YesversionstringFixed value: v1

The media_profiles media attribute information is described below.

RequiredParameterTypeDescription
Yesmedia_data_typestringurl indicates a URL; types such as jpg indicate base64-encoded data

The params inference parameter is a JSON string with the following structure.

{
  "rsp_media_type": "url",
  "parameter": {
    "return_landmark": 3
  }
}
RequiredFieldTypeDescription
Norsp_media_typestringDefaults to url; jpg indicates base64-encoded data
YesparameterobjectCore algorithm parameter object

The algorithm parameters in parameter are described below.

RequiredParameterTypeDescription
Noreturn_landmarkintWhether to detect and return landmarks for facial features and contours

Valid values for return_landmark are described below.

ValueMeaningOutput Format
0Do not return landmarksNone
1Return 106 landmarks as percentagesPercentage coordinates
2Return 106 landmarks as absolute valuesActual pixel coordinates
3Return 118 landmarks as percentagesPercentage coordinates
4Return 118 landmarks as absolute valuesActual pixel coordinates
5Return 171 landmarks as percentagesPercentage coordinates
6Return 171 landmarks as absolute valuesActual pixel coordinates

Request Example

{
  "task": "/v1/Facial_Keypoint_Detection/487161",
  "task_type": "formula",
  "init_images": [
    {
      "url": "https://obs.mtlab.meitu.com/public/EtRGxLI8ulQ43pGhuZfym9aYUD2PUbMP/MTcwOTE4MjgwMA==/58fdb25d-8e6f-4026-4a5e-45d9894190a4.png",
      "profile": {
        "media_profiles": {
          "media_data_type": "url"
        },
        "version": "v1"
      }
    }
  ],
  "params": "{\"rsp_media_type\":\"url\",\"parameter\":{\"return_landmark\":3}}",
  "sync_timeout": 30
}

Response Fields

Generated results are periodically deleted. Download and save them promptly.
FieldTypeDescription
request_idstringRequest identifier
trace_idstringTrace identifier
codeintBusiness status code; 0 indicates that the request was accepted successfully
error_codeintError code; 0 on success
messagestringBusiness message
tipsanyAdditional information; may be null
dataobjectTask status and algorithm result

Fields in the data object are described below.

FieldTypeDescription
statusNumber-1: task not found; 0: created successfully; 1: processing; 2: failed; 9: timed out—use the Query API; 10: succeeded
resultObjectTask processing result
progressNumberTask progress, from 0 to 1; 1 indicates completion
predict_elapsedNumberEstimated processing time, in milliseconds
create_timeNumberTask creation timestamp, in milliseconds
task_idStringTask ID
custom_task_idStringCustom task ID
trace_idStringTrace ID
client_infoStringClient information
init_imagesArrayInitial input media information

Fields in the result object are described below.

FieldTypeDescription
idStringTask result ID
urlsArrayList of generated media file URLs
parametersObjectTask parameter information
dataObjectDetailed task processing data
imagesArrayList of generated image URLs
mtlab_resObjectMeitu Lab internal processing result
media_info_listArrayMedia information list; each item contains media_data, media_extra, and media_profiles
error_codeIntAlgorithm error code; 20001 when processing fails
error_msgStringAlgorithm error message; PROCESS_ERROR when processing fails

Fields in parameters are described below.

FieldTypeDescription
landmark_typeIntFacial landmark type; see the valid values for return_landmark
versionStringAPI version number

Fields in media_info_list are described below.

FieldTypeDescription
media_dataStringMedia file URL
media_extraObjectExtended media information, including detected face information in faces
media_profilesObjectMedia attribute information, such as resolution and frame rate (null for this API)

Fields in faces are described below.

FieldTypeDescription
face_landmarkArrayFacial landmark coordinate array; coordinates are percentages in [x, y] format
face_rectangleObjectFace bounding rectangle containing left, top, width, and height as percentages

Fields in face_rectangle are described below.

FieldTypeDescription
leftFloatX-coordinate of the face box's top-left corner as a percentage of image width
topFloatY-coordinate of the face box's top-left corner as a percentage of image height
widthFloatFace box width as a percentage of image width
heightFloatFace box height as a percentage of image height

Response Examples

Successful Response Example

Response Status: 200

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

{
  "request_id": "",
  "trace_id": "",
  "code": 0,
  "error_code": 0,
  "message": "success",
  "tips": null,
  "data": {
    "status": 10,
    "result": {
      "id": "t_mt1a3i5n7b95923c6b-xxxx-4045-8f0e-9e309efdc7ae",
      "urls": [
        ""
      ],
      "parameters": {
        "landmark_type": 3,
        "version": "2.0.0"
      },
      "data": {
        "ErrorCode": 0,
        "ErrorMsg": "",
        "error_code": 0,
        "error_msg": "",
        "media_info_list": [
          {
            "media_data": "",
            "media_extra": {
              "faces": [
                {
                  "face_landmark": [
                    [
                      0.35435236,
                      0.2828878
                    ],
                    [
                      0.35482758,
                      0.28858864
                    ]
                  ],
                  "face_rectangle": {
                    "height": 0.052405804,
                    "left": 0.37931174,
                    "top": 0.26455134,
                    "width": 0.069874406
                  }
                }
              ]
            },
            "media_profiles": null
          }
        ],
        "msg_id": "",
        "parameter": {
          "landmark_type": 3,
          "version": "2.0.0"
        }
      },
      "images": [
        ""
      ],
      "mtlab_res": {
        "ErrorCode": 0,
        "ErrorMsg": "",
        "error_code": 0,
        "error_msg": "",
        "media_info_list": [
          {
            "media_data": "",
            "media_extra": {
              "faces": [
                {
                  "face_landmark": [
                    [
                      0.35435236,
                      0.2828878
                    ],
                    [
                      0.35482758,
                      0.28858864
                    ]
                  ],
                  "face_rectangle": {
                    "height": 0.052405804,
                    "left": 0.37931174,
                    "top": 0.26455134,
                    "width": 0.069874406
                  }
                }
              ]
            },
            "media_profiles": null
          }
        ],
        "msg_id": "",
        "parameter": {
          "landmark_type": 3,
          "version": "2.0.0"
        }
      },
      "media_info_list": [
        {
          "media_data": "",
          "media_extra": {
            "faces": [
              {
                "face_landmark": [
                  [
                    0.35435236,
                    0.2828878
                  ],
                  [
                    0.35482758,
                    0.28858864
                  ]
                ],
                "face_rectangle": {
                  "height": 0.052405804,
                  "left": 0.37931174,
                  "top": 0.26455134,
                  "width": 0.069874406
                }
              }
            ]
          },
          "media_profiles": null
        }
      ]
    },
    "progress": 0,
    "predict_elapsed": 10000,
    "create_time": 1770285187417,
    "task_id": "t_mt1a3i5n7b95923c6b-xxxx-4045-8f0e-9e309efdc7ae",
    "custom_task_id": "",
    "trace_id": "",
    "client_info": "",
    "init_images": null
  }
}

Pending Response Example

Use the Query API to query the task.

Response Status: 200

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

{
  "request_id": "",
  "trace_id": "",
  "code": 0,
  "error_code": 0,
  "message": "success",
  "tips": null,
  "data": {
    "status": 9,
    "result": {
      "id": "t_mt1a3i5n7b95923c6b-xxxx-4045-8f0e-9e309efdc7ae"
    },
    "progress": 0,
    "predict_elapsed": 10000,
    "create_time": 1770285187417,
    "task_id": "t_mt1a3i5n7b95923c6b-xxxx-4045-8f0e-9e309efdc7ae",
    "custom_task_id": "",
    "trace_id": "ace75fe5-xxxx-46ad-a1db-cbcfde308f8a",
    "client_info": "",
    "init_images": null
  }
}

Failed Response Example

Response Status: 400

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

{
  "request_id": "",
  "trace_id": "",
  "code": 0,
  "error_code": 20001,
  "message": "PROCESS_ERROR",
  "tips": null,
  "data": {
    "status": 2,
    "result": {
      "error_code": 20001,
      "error_msg": "PROCESS_ERROR"
    },
    "progress": 0,
    "predict_elapsed": 0,
    "create_time": 1770285187417,
    "task_id": "t_mt1a3i5n7b95923c6b-xxxx-4045-8f0e-9e309efdc7ae",
    "custom_task_id": "",
    "trace_id": "",
    "client_info": "",
    "init_images": null
  }
}

General Error Codes and Messages

See API Error Codes.

API-specific error: 20001 PROCESS_ERROR, indicating a processing error.

SDK Examples

Python

import json

import requests
from sign_sdk import sign

def api_call_example():
    key = "your_api_key"
    secret = "your_api_secret"
    url = "https://openapi.meitu.com/api/v1/sdk/sync/push"
    method = "POST"

    headers = {
        "Content-Type": "application/json",
        sign.HeaderHost: "openapi.meitu.com",
    }
    inner_params = {
        "rsp_media_type": "url",
        "parameter": {"return_landmark": 3},
    }
    payload = {
        "task": "/v1/Facial_Keypoint_Detection/487161",
        "task_type": "formula",
        "init_images": [
            {
                "url": "https://obs.mtlab.meitu.com/public/EtRGxLI8ulQ43pGhuZfym9aYUD2PUbMP/MTcwOTE4MjgwMA==/58fdb25d-8e6f-4026-4a5e-45d9894190a4.png",
                "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, method, headers, body)
    response = requests.Session().send(signed_request)
    print(f"Status: {response.status_code}")
    print(f"Response: {response.text}")

if __name__ == "__main__":
    api_call_example()

Go

package main

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

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

func main() {
	key := "your_api_key"
	secret := "your_api_secret"
	signObj := signer.NewSigner(key, secret)

	url := "https://openapi.meitu.com/api/v1/sdk/sync/push"
	method := http.MethodPost
	headers := make(http.Header)
	headers.Set(signer.HeaderHost, "openapi.meitu.com")
	headers.Set("Content-Type", "application/json")

	body := `{
  "task": "/v1/Facial_Keypoint_Detection/487161",
  "task_type": "formula",
  "init_images": [{
    "url": "https://obs.mtlab.meitu.com/public/EtRGxLI8ulQ43pGhuZfym9aYUD2PUbMP/MTcwOTE4MjgwMA==/58fdb25d-8e6f-4026-4a5e-45d9894190a4.png",
    "profile": {
      "media_profiles": {"media_data_type": "url"},
      "version": "v1"
    }
  }],
  "params": "{\"rsp_media_type\":\"url\",\"parameter\":{\"return_landmark\":3}}",
  "sync_timeout": 30
}`

	req, err := signObj.Sign(url, method, headers, body)
	if err != nil {
		fmt.Println("Failed to sign request:", err)
		return
	}

	resp, err := http.DefaultClient.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("Read response failed:", err)
		return
	}
	fmt.Println("Response:", resp.StatusCode, string(responseBody))
}

PHP

<?php
require 'signer.php';

$key = 'your_api_key';
$secret = 'your_api_secret';
$signer = new Signer($key, $secret);

$url = 'https://openapi.meitu.com/api/v1/sdk/sync/push';
$method = 'POST';
$headers = ['Content-Type' => 'application/json'];

$innerParams = json_encode([
    'rsp_media_type' => 'url',
    'parameter' => ['return_landmark' => 3],
]);

$body = json_encode([
    'task' => '/v1/Facial_Keypoint_Detection/487161',
    'task_type' => 'formula',
    'init_images' => [
        [
            'url' => 'https://obs.mtlab.meitu.com/public/EtRGxLI8ulQ43pGhuZfym9aYUD2PUbMP/MTcwOTE4MjgwMA==/58fdb25d-8e6f-4026-4a5e-45d9894190a4.png',
            'profile' => [
                'media_profiles' => ['media_data_type' => 'url'],
                'version' => 'v1',
            ],
        ],
    ],
    'params' => $innerParams,
    'sync_timeout' => 30,
]);

$curl = $signer->sign($url, $method, $headers, $body);
$response = curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);

if ($status === 0) {
    echo 'Error: ' . curl_error($curl);
} else {
    echo "Status: {$status}\n";
    echo "Response: {$response}\n";
}
curl_close($curl);
?>

Java

package com.meitu.openai.common;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
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_api_key", "your_api_secret");
        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/Facial_Keypoint_Detection/487161\",\n" +
                "  \"task_type\": \"formula\",\n" +
                "  \"init_images\": [{\n" +
                "    \"url\": \"https://obs.mtlab.meitu.com/public/EtRGxLI8ulQ43pGhuZfym9aYUD2PUbMP/MTcwOTE4MjgwMA==/58fdb25d-8e6f-4026-4a5e-45d9894190a4.png\",\n" +
                "    \"profile\": {\n" +
                "      \"media_profiles\": {\"media_data_type\": \"url\"},\n" +
                "      \"version\": \"v1\"\n" +
                "    }\n" +
                "  }],\n" +
                "  \"params\": \"{\\\"rsp_media_type\\\":\\\"url\\\",\\\"parameter\\\":{\\\"return_landmark\\\":3}}\",\n" +
                "  \"sync_timeout\": 30\n" +
                "}";

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

        int status = connection.getResponseCode();
        InputStream stream = status >= 400
                ? connection.getErrorStream()
                : connection.getInputStream();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) {
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            System.out.println("Response: " + status + " " + response);
        }
    }
}