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 ImageOutput Image
Input imageOutput 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

RequiredParameterTypeDescription
YestaskstringFixed value: /v1/OutPainting/468257.
Yestask_typestringFixed value: formula.
Yesinit_imagesobject[]Input image list containing one source image.
YesparamsstringA JSON-encoded string containing the algorithm parameters described below.
Nosync_timeoutintSynchronous 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

RequiredParameterTypeDescription
YesurlstringImage URL or Base64-encoded image data. Specify the transmission type in profile.media_profiles.media_data_type.
NoprofileobjectImage transmission information.

profile

RequiredParameterTypeDescription
Nomedia_extraobjectAdditional media parameters.
Nomedia_profilesobjectMedia transmission information.
NoversionstringThe examples use v1.

media_profiles

RequiredParameterTypeDescription
Nomedia_data_typestringurl: 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.

RequiredParameterTypeDescription
YesparameterobjectOutpainting algorithm parameters.

parameter

RequiredParameterTypeDescription
Norsp_media_typestringOutput transmission type: url for image URLs or jpg for Base64-encoded image data. Default: url.
Noexpand_ratiofloatUniform 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.
Nofree_expand_ratioobjectSeparate expansion ratios for left, right, top, and bottom. Each value must be in [0, 1].
Nofree_expand_pixelobjectSeparate pixel offsets for left, right, top, and bottom. Each value must be a nonnegative integer.
Nogenerate_numintNumber of images to generate. Default: 1. Maximum: 20.
NoseedintRandom 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.
Noextra_promptstringAdditional prompt that guides generated content. Only English is supported by default. Separate multiple prompts with commas ,.
Nohigh_quality_encodeboolWhether 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:

RequiredParameterTypeDescription
YesleftfloatWidth added on the left as a fraction of the original width. For example, 0.1 adds 10% of the original width on the left.
YesrightfloatWidth added on the right as a fraction of the original width.
YestopfloatHeight added at the top as a fraction of the original height.
YesbottomfloatHeight 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:

RequiredParameterTypeDescription
YesleftintPixels added on the left.
YesrightintPixels added on the right.
YestopintPixels added at the top.
YesbottomintPixels 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.

FieldTypeDescription
codeintRequest processing status. 0 indicates normal request processing; a nonzero value indicates failure. Also check data.status to determine whether the task has completed.
messagestringResponse message or error information.
dataobject/nullTask information. May be null if the request fails.

data

FieldTypeDescription
statusintTask status: -1 not found; 0 created; 1 processing; 2 failed; 9 result query required; 10 succeeded.
resultobjectTask ID and processing result.
progressnumberTask progress, for example 0.1, 0.85, or 1.

result

FieldTypeDescription
idstringTask ID. Pass this value as task_id when querying the result.
urlsstring[]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 CodeError MessageDescription
20001PROCESS_ERRORProcessing error
20003DETECT_NOT_FACENo face detected
20004MORE_THAN_ONE_FACEMore than one face detected
20007MISSING_LANDMARK_ARGUMENTSFace landmarks are missing
20008UNSUITABLE_IMAGEImage does not meet the requirements
20009UNSUPPORT_TYPEUnsupported type
20010DETECT_NOT_FACENo face detected in the second image
20011UNSUITABLE_VERTICAL_IMAGEImage height does not meet the requirements
20012UNSUITABLE_HORIZONTAL_IMAGEImage width does not meet the requirements
20013RESOLUTION_TOO_LARGE_ERRORImage resolution is too high
20014NOT_FOUNDImage not found
20015PICTURE_OVERRUN_ERRORImage exceeds the limit
20020DETECT_FACE_OUTOFIMAGEFacial features are missing
20021DETECT_FACE_PITCHANGLE_BIGFace pitch angle is too large
20022DETECT_FACE_YAWANGLE_BIGFace yaw angle is too large
20023DETECT_FACE_LOWAREAFace area is too small or has insufficient pixels
21001LOAD_MODEL_ERRORFailed to load the model
21002HAIR_MASK_LOSSHair mask is missing
21003FACE_NUM_ERRORInvalid number of faces
21004AR_PARSE_FAULTFailed to parse the AR plist
21005AR_EEEOR_COUNTAR face error
21006AR_FACE_OUTAR extends beyond the face region
21007JSON_ERRORInvalid JSON content
21008BACKGROUND_IMAGE_LOSSBackground image is missing
21009BODY_MASK_LOSSBody mask is missing
21010FACE_ANGLE_ERRORInvalid face angle
21011SKIN_MASK_LOSSSkin mask is missing
21012BODY_INFO_LOSSBody keypoints or contour points are missing
21013RECT_OUT_IMAGERegion extends beyond the image
30001GEN_ERRORGeneration 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();
        }
    }
}