Flat lay on model
Virtual try-on from flat lay images with a custom model
You will need an API token to send HTTP requests. See Authentication for instructions.
Quick start
Create a project to organize your images. The project_id will be used in subsequent requests. See Creating a project for details.
Upload one or more flat lay / SKU images to the project. This is a two-step process:
- Request a pre-signed upload URL
- PUT the image binary to that URL
Collect all file_id values for the next step. See Uploading images for details.
The upload_url is only valid for a limited time. Upload the image immediately after receiving the response.
Start a flat-to-model job by providing the identity (the model), the list of uploaded file IDs (all combined as inputs), and one or more instructions. Each instruction produces one output image. See Starting a job for details.
Poll for notifications to track job progress. Use the id_task parameter with your job ID. See Polling notifications for details.
Uploading identities
Before starting a flat-to-model job, you need an identity_code. You can either use an existing identity from your gallery or upload a new one.
import requests
api_url = "https://v2.api.piktid.com"
access_token = "your_access_token"
identity_image_path = "path/to/identity.jpg"
with open(identity_image_path, "rb") as f:
response = requests.post(
api_url + "/identity/upload",
headers={"Authorization": "Bearer " + access_token},
files={"image": f},
data={"name": "Model A"}, # Optional custom name
).json()
identity_code = response["identity_code"]
print(f"Identity uploaded: {identity_code}"){
"identity_code": "id_xyz...", // IDENTITY_CODE
"name": "Model A",
"face_detected": true,
"success": true,
// ...
}You can list existing identities using:
Creating a project
import requests
api_url = "https://v2.api.piktid.com"
access_token = "your_access_token"
response = requests.post(
api_url + "/project",
headers={"Authorization": "Bearer " + access_token},
json={"project_name": "my-flat-lay-project"},
).json()
project_id = response["project_id"]
project_name = response["project_name"]{
"project_id": "abc123...", // PROJECT_ID
"project_name": "my-flat-lay-project"
}Uploading images
Upload all flat lay / SKU images (e.g., shirt, pants, shoes) that will be combined for each output. You can repeat this process for each image.
The upload_url is only valid for a limited time. Upload the image immediately after receiving the response.
import requests
api_url = "https://v2.api.piktid.com"
access_token = "your_access_token"
project_name = "my-flat-lay-project"
image_path = "path/to/sku-image.jpg"
# Step 1: Get pre-signed upload URL
response = requests.post(
api_url + "/upload",
headers={"Authorization": "Bearer " + access_token},
json={
"project_name": project_name,
"filename": "sku-shirt.jpg",
},
).json()
upload_url = response["upload_url"]
content_type = response["content_type"]
file_id = response["file_id"]
# Step 2: Upload the image binary
with open(image_path, "rb") as f:
requests.put(
upload_url,
headers={"Content-Type": content_type},
data=f.read(),
)
print(f"Uploaded file ID: {file_id}"){
"upload_url": "https://s3...", // Pre-signed PUT URL
"download_url": "https://...",
"project_id": "abc123...",
"project_name": "my-flat-lay-project",
"file_id": "sku_001...", // FILE_ID
"filename": "sku-shirt.jpg",
"content_type": "image/jpeg"
}Starting a job
Unlike model swap (1 input = 1 output), flat-to-model combines all input images and generates outputs based on the instructions list:
Ninput SKU images +Minstructions =Moutput images- Each instruction produces one output image combining all inputs
- Instructions run in parallel
import requests
api_url = "https://v2.api.piktid.com"
access_token = "your_access_token"
project_id = "abc123..."
identity_code = "id_xyz..." # From identity upload or gallery
file_ids = ["sku_001...", "sku_002..."] # All SKU images to combine
# Define instructions - each instruction produces one output
instructions = [
{
"pose": "1",
"angle": "3",
"background": "white studio",
"lighting": "soft",
"options": {
"size": "2K",
"ar": "9:16",
"format": "jpg",
},
},
{
"pose": "15",
"angle": "7",
"background": "gym rooftop",
"lighting": "dramatic",
"options": {
"size": "2K",
"ar": "3:4",
"format": "png",
},
},
]
response = requests.post(
api_url + "/flat-2-model",
headers={"Authorization": "Bearer " + access_token},
json={
"identity_code": identity_code,
"project_id": project_id,
"images": file_ids,
"instructions": instructions,
"post_process": False, # Optional: enable post-processing
"options": {}, # Optional: global options applied to all instructions
},
).json()
job_id = response["job_id"]
total_outputs = response["total_outputs"]
print(f"Job started: {job_id} ({total_outputs} outputs)"){
"job_id": "job_abc123...", // JOB_ID
"status": "pending",
"message": "Job created successfully",
"total_outputs": 2 // Number of output images
}Instruction parameters
Each instruction can contain the following parameters:
| Parameter | Type | Description |
|---|---|---|
pose | string | Pose reference (e.g., "1", "15"). See pose reference. |
angle | string | Camera angle reference (e.g., "3", "7"). See angles. |
background | string | Background description (e.g., "white studio", "beach"). |
lighting | string | Lighting style (e.g., "soft", "dramatic"). |
options | object | Output options. |
Output options
The options object within each instruction can contain:
| Parameter | Values | Description |
|---|---|---|
size | "1K", "2K", "4K" | Output image resolution. |
ar | "1:1", "3:4", "4:3", "9:16", "16:9" | Aspect ratio. |
format | "jpg", "png" | Output file format. |
seed | integer | Random seed for reproducibility. |
Post-processing
Set post_process to true in the job request to enable automatic post-processing of results. The job status will include post_processing_status to track this additional step.
{
"identity_code": "id_xyz...",
"project_id": "abc123...",
"images": ["sku_001...", "sku_002..."],
"instructions": [/* ... */],
"post_process": true
}Polling notifications
Poll for notifications to track job progress. Use the id_task parameter with your job ID.
import time
import requests
api_url = "https://v2.api.piktid.com"
access_token = "your_access_token"
job_id = "job_abc123..."
# Poll for notifications (recommended: every 3-5 seconds)
for _ in range(120): # 10 minute timeout
response = requests.post(
api_url + "/notifications",
headers={"Authorization": "Bearer " + access_token},
json={
"id_task": job_id,
"name_list": ["batch_edit", "image_result", "error"],
},
).json()
for notification in response:
print(f"Notification: {notification['name']}")
print(f"Data: {notification['data']}")
if notification["name"] == "batch_edit":
status = notification["data"].get("status")
if status == "completed":
print("Job completed!")
break
elif status == "failed":
print(f"Error: {notification['data'].get('error_message')}")
break
else:
time.sleep(5)
continue
break[
{
"id": 12345,
"name": "batch_edit", // Notification type
"timestamp": 1702819200.0,
"data": { // Job-specific data
"id_task": "job_abc123...",
"status": "completed",
"total_images": 2,
"processed_images": 2
}
}
]Retrieving results
Once the job is complete, retrieve the processed images.
import requests
api_url = "https://v2.api.piktid.com"
access_token = "your_access_token"
job_id = "job_abc123..."
response = requests.get(
api_url + f"/jobs/{job_id}/results",
headers={"Authorization": "Bearer " + access_token},
).json()
for result in response["results"]:
print(f"Image {result['image_index']}: {result['output']['full_size']}"){
"job_id": "job_abc123...",
"job_type": "flat_2_model",
"status": "completed",
"results": [
{
"image_index": 0,
"group_index": 0,
"output": {
"full_size": "https://...", // Result image URL
"thumbnail": "https://..."
},
"status": "completed"
},
{
"image_index": 1,
"group_index": 0,
"output": {
"full_size": "https://...",
"thumbnail": "https://..."
},
"status": "completed"
}
],
"summary": {
// Job statistics
}
}Bulk download
For bulk downloads, generate a temporary download URL that packages all results into a ZIP file.
import requests
api_url = "https://v2.api.piktid.com"
access_token = "your_access_token"
job_id = "job_abc123..."
# Generate download URL
response = requests.post(
api_url + "/download",
headers={"Authorization": "Bearer " + access_token},
json={"job_id": job_id},
).json()
download_url = response["download_url"]
expires = response["expires"]
print(f"Download URL: {download_url}")
print(f"Expires: {expires}")
# Download the ZIP file (no auth required for the token URL)
zip_response = requests.get(download_url)
with open("results.zip", "wb") as f:
f.write(zip_response.content){
"download_url": "https://v2.api.piktid.com/download/token123...",
"expires": "2024-12-17T11:00:00Z" // URL expiration time
}Checking job status
You can also check the job status directly without waiting for notifications.
import requests
api_url = "https://v2.api.piktid.com"
access_token = "your_access_token"
job_id = "job_abc123..."
response = requests.get(
api_url + f"/jobs/{job_id}/status",
headers={"Authorization": "Bearer " + access_token},
).json()
print(f"Status: {response['status']}")
print(f"Progress: {response['progress']}%")
print(f"Processed: {response['processed_images']}/{response['total_images']}"){
"job_id": "job_abc123...",
"job_type": "flat_2_model",
"status": "processing",
"progress": 50.0,
"total_images": 2,
"processed_images": 1,
"should_post_process": false,
"post_processing_status": null,
"created_at": "2024-12-17T10:00:00Z",
"updated_at": "2024-12-17T10:05:00Z"
}Error handling
Jobs may fail due to various reasons. Check the error_message field in the job status or results.
import requests
api_url = "https://v2.api.piktid.com"
access_token = "your_access_token"
job_id = "job_abc123..."
response = requests.get(
api_url + f"/jobs/{job_id}/results",
headers={"Authorization": "Bearer " + access_token},
).json()
if response["status"] == "failed":
print(f"Job failed: {response['error_message']}")
else:
for result in response["results"]:
if result["status"] == "failed":
print(f"Image {result['image_index']} failed: {result['error_message']}")