Outpainting
Description
Generates content beyond the boundaries of an input image to expand its canvas. Supports uniform expansion, separate expansion ratios for each side, and separate pixel offsets for each side. English prompts can be used to guide the generated content.
Image Examples
| Input Image | Output Image |
|---|---|
![]() | ![]() |
Image Requirements
- Input formats: JPG, JPEG, PNG, BMP, and HEIF.
- Input file size: up to 30 MB.
- Input images: one.
- Output dimensions: neither width nor height exceeds 4096 pixels. If the expanded image exceeds this limit, it is resized before being returned. For pixel-based expansion, resizing also reduces the actual number of added pixels while preserving the expansion proportions.
Request URL
- Endpoint:
https://openapi.meitu.com/api/v1/sdk/sync/push - Task name (
task):/v1/OutPainting/468257 - Task type (
task_type):formula
HTTP Method
POST
Content-Type: application/json
Authentication
Sign requests using your Access Key (AK) and Secret Key (SK). See API Request Signing.
Request Parameters
| Required | Parameter | Type | Description |
|---|---|---|---|
| Yes | task | string | Fixed value: /v1/OutPainting/468257. |
| Yes | task_type | string | Fixed value: formula. |
| Yes | init_images | object[] | Input image list containing one source image. |
| Yes | params | string | A JSON-encoded string containing the algorithm parameters described below. |
| No | sync_timeout | int | Synchronous wait time in seconds. Default: 30. Set to -1 to return without waiting. If data.status = 9, use the Task Status API to retrieve the result. |
init_images Item
| Required | Parameter | Type | Description |
|---|---|---|---|
| Yes | url | string | Image URL or Base64-encoded image data. Specify the transmission type in profile.media_profiles.media_data_type. |
| No | profile | object | Image transmission information. |
profile
| Required | Parameter | Type | Description |
|---|---|---|---|
| No | media_extra | object | Additional media parameters. |
| No | media_profiles | object | Media transmission information. |
| No | version | string | The examples use v1. |
media_profiles
| Required | Parameter | Type | Description |
|---|---|---|---|
| No | media_data_type | string | url: provide an image URL. jpg: provide Base64-encoded image data. |
params
Construct the following parameter object, then encode it as a JSON string for the params field in the request body.
| Required | Parameter | Type | Description |
|---|---|---|---|
| Yes | parameter | object | Outpainting algorithm parameters. |
parameter
| Required | Parameter | Type | Description |
|---|---|---|---|
| No | rsp_media_type | string | Output transmission type: url for image URLs or jpg for Base64-encoded image data. Default: url. |
| No | expand_ratio | float | Uniform expansion ratio for all four sides. Range: [0, 1]. Default: 0.5. Each horizontal side expands by this fraction of the original width; each vertical side expands by this fraction of the original height. |
| No | free_expand_ratio | object | Separate expansion ratios for left, right, top, and bottom. Each value must be in [0, 1]. |
| No | free_expand_pixel | object | Separate pixel offsets for left, right, top, and bottom. Each value must be a nonnegative integer. |
| No | generate_num | int | Number of images to generate. Default: 1. Maximum: 20. |
| No | seed | int | Random seed in [-1, 65535]. Default: 0. Set to -1 for a random seed. A fixed seed can be used to reproduce results with the same input and parameters. |
| No | extra_prompt | string | Additional prompt that guides generated content. Only English is supported by default. Separate multiple prompts with commas ,. |
| No | high_quality_encode | bool | Whether to encode output images with higher quality and lower compression. Default: false. This improves encoding quality without changing the generated content, but increases processing time. |
rsp_media_type belongs to the parameter object inside the params string.
When specifying the expansion area, provide only one of expand_ratio, free_expand_ratio, or free_expand_pixel. If no expansion mode is specified, the default expand_ratio = 0.5 is used.
For an input width W and height H, setting expand_ratio = r gives target dimensions of W × (1 + 2r) and H × (1 + 2r) before resizing. For example, 0.1 gives 120% of the original width and height, while 0.5 gives 200%.
free_expand_ratio
When using separate expansion ratios, provide all four fields:
| Required | Parameter | Type | Description |
|---|---|---|---|
| Yes | left | float | Width added on the left as a fraction of the original width. For example, 0.1 adds 10% of the original width on the left. |
| Yes | right | float | Width added on the right as a fraction of the original width. |
| Yes | top | float | Height added at the top as a fraction of the original height. |
| Yes | bottom | float | Height added at the bottom as a fraction of the original height. |
All values must be in [0, 1]. A value of 0 means no expansion on that side. Before resizing, the target width is W × (1 + left + right) and the target height is H × (1 + top + bottom).
free_expand_pixel
When using pixel-based expansion, provide all four fields:
| Required | Parameter | Type | Description |
|---|---|---|---|
| Yes | left | int | Pixels added on the left. |
| Yes | right | int | Pixels added on the right. |
| Yes | top | int | Pixels added at the top. |
| Yes | bottom | int | Pixels added at the bottom. |
All values must be nonnegative integers. A value of 0 means no expansion on that side. Before resizing, the target width is W + left + right and the target height is H + top + bottom.
Request Examples
The following examples each demonstrate one expansion mode. Replace https://example.com/input.jpg with an accessible image URL. params must always be a JSON string.
Uniform Expansion
expand_ratio = 0.25 adds 25% of the original width on each horizontal side and 25% of the original height on each vertical side. A 1000 × 800 pixel input has target dimensions of 1500 × 1200 pixels before resizing.
{
"task": "/v1/OutPainting/468257",
"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\",\"expand_ratio\":0.25,\"generate_num\":1,\"seed\":-1,\"high_quality_encode\":false}}",
"sync_timeout": 30
}Separate Expansion Ratios
Adds 10% of the original width on the left, 20% of the original height at the top, and 5% of the original height at the bottom, with no expansion on the right. A 1000 × 800 pixel input has target dimensions of 1100 × 1000 pixels before resizing.
{
"task": "/v1/OutPainting/468257",
"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\",\"free_expand_ratio\":{\"left\":0.1,\"right\":0,\"top\":0.2,\"bottom\":0.05},\"generate_num\":1,\"seed\":-1,\"high_quality_encode\":false}}",
"sync_timeout": 30
}Pixel-Based Expansion
Adds 100 pixels on the left, 200 pixels at the top, and 50 pixels at the bottom, with no expansion on the right. A 1000 × 800 pixel input has target dimensions of 1100 × 1050 pixels before resizing.
{
"task": "/v1/OutPainting/468257",
"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\",\"free_expand_pixel\":{\"left\":100,\"right\":0,\"top\":200,\"bottom\":50},\"generate_num\":1,\"seed\":-1,\"high_quality_encode\":false}}",
"sync_timeout": 30
}Response Fields
The following descriptions and examples use URL output and show the task status and result returned by the synchronous task API. After the task succeeds, retrieve the output images from data.result.urls.
| Field | Type | Description |
|---|---|---|
| code | int | Request processing status. 0 indicates normal request processing; a nonzero value indicates failure. Also check data.status to determine whether the task has completed. |
| message | string | Response message or error information. |
| data | object/null | Task information. May be null if the request fails. |
data
| Field | Type | Description |
|---|---|---|
| status | int | Task status: -1 not found; 0 created; 1 processing; 2 failed; 9 result query required; 10 succeeded. |
| result | object | Task ID and processing result. |
| progress | number | Task progress, for example 0.1, 0.85, or 1. |
result
| Field | Type | Description |
|---|---|---|
| id | string | Task ID. Pass this value as task_id when querying the result. |
| urls | string[] | Result image URLs available after the task succeeds. |
When data.status = 9, use data.result.id to call the Task Status API. Send a signed GET request to https://openapi.meitu.com/api/v1/sdk/status?task_id=<TASK_ID>.
Query status 0 or 1 means the task is not yet complete; 10 means success; 2 means failure. Query and save results promptly. Tasks expire after 24 hours, and historical task queries are not supported.
Response Examples
Successful Response
{
"code": 0,
"message": "",
"data": {
"status": 10,
"result": {
"id": "50309bd5-a827-4125-bc96-62039c93770b",
"urls": [
"https://example.com/result.png"
]
},
"progress": 1
}
}Query-Required Response
Pass the returned data.result.id as the task_id parameter to the Task Status API.
{
"code": 0,
"message": "",
"data": {
"status": 9,
"result": {
"id": "50309bd5-a827-4125-bc96-62039c93770b"
},
"progress": 0
}
}Failed Response
{
"code": 20008,
"message": "UNSUITABLE_IMAGE",
"data": null
}API-Specific Error Codes
| Error Code | Error Message | Description |
|---|---|---|
| 20001 | PROCESS_ERROR | Processing error |
| 20003 | DETECT_NOT_FACE | No face detected |
| 20004 | MORE_THAN_ONE_FACE | More than one face detected |
| 20007 | MISSING_LANDMARK_ARGUMENTS | Face landmarks are missing |
| 20008 | UNSUITABLE_IMAGE | Image does not meet the requirements |
| 20009 | UNSUPPORT_TYPE | Unsupported type |
| 20010 | DETECT_NOT_FACE | No face detected in the second image |
| 20011 | UNSUITABLE_VERTICAL_IMAGE | Image height does not meet the requirements |
| 20012 | UNSUITABLE_HORIZONTAL_IMAGE | Image width does not meet the requirements |
| 20013 | RESOLUTION_TOO_LARGE_ERROR | Image resolution is too high |
| 20014 | NOT_FOUND | Image not found |
| 20015 | PICTURE_OVERRUN_ERROR | Image exceeds the limit |
| 20020 | DETECT_FACE_OUTOFIMAGE | Facial features are missing |
| 20021 | DETECT_FACE_PITCHANGLE_BIG | Face pitch angle is too large |
| 20022 | DETECT_FACE_YAWANGLE_BIG | Face yaw angle is too large |
| 20023 | DETECT_FACE_LOWAREA | Face area is too small or has insufficient pixels |
| 21001 | LOAD_MODEL_ERROR | Failed to load the model |
| 21002 | HAIR_MASK_LOSS | Hair mask is missing |
| 21003 | FACE_NUM_ERROR | Invalid number of faces |
| 21004 | AR_PARSE_FAULT | Failed to parse the AR plist |
| 21005 | AR_EEEOR_COUNT | AR face error |
| 21006 | AR_FACE_OUT | AR extends beyond the face region |
| 21007 | JSON_ERROR | Invalid JSON content |
| 21008 | BACKGROUND_IMAGE_LOSS | Background image is missing |
| 21009 | BODY_MASK_LOSS | Body mask is missing |
| 21010 | FACE_ANGLE_ERROR | Invalid face angle |
| 21011 | SKIN_MASK_LOSS | Skin mask is missing |
| 21012 | BODY_INFO_LOSS | Body keypoints or contour points are missing |
| 21013 | RECT_OUT_IMAGE | Region extends beyond the image |
| 30001 | GEN_ERROR | Generation error |
Common Error Codes
See API Error Codes.
SDK Examples
The following examples submit an outpainting task with expand_ratio = 0.25. Replace the AK, SK, and input image URL before running the code.
For separate ratios or pixel offsets, replace expand_ratio with free_expand_ratio or free_expand_pixel. Do not provide multiple expansion modes in one request. The examples print the API response. If data.status = 9, query the task result as described above.
Python
Set up the SDK as described in the Python Signing SDK documentation.
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",
"expand_ratio": 0.25,
"generate_num": 1,
"seed": -1,
"high_quality_encode": False
}
}
payload = {
"task": "/v1/OutPainting/468257",
"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
Set up the SDK as described in the Go Signing SDK documentation.
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/OutPainting/468257",
"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\",\"expand_ratio\":0.25,\"generate_num\":1,\"seed\":-1,\"high_quality_encode\":false}}",
"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
Set up the SDK as described in the PHP Signing SDK documentation.
<?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',
'expand_ratio' => 0.25,
'generate_num' => 1,
'seed' => -1,
'high_quality_encode' => false,
],
], JSON_UNESCAPED_SLASHES);
$body = json_encode([
'task' => '/v1/OutPainting/468257',
'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
Set up the SDK as described in the Java Signing SDK documentation.
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/OutPainting/468257\",\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\\\",\\\"expand_ratio\\\":0.25,\\\"generate_num\\\":1,\\\"seed\\\":-1,\\\"high_quality_encode\\\":false}}\",\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();
}
}
}
