Change expression
Change facial expressions with our advanced AI model
Google Colab
Github
Webapp
You will need an API token to send HTTP requests. See Authentication for instructions.
Quick start
Firstly, you'll need to upload the image you want to process. These parameters must be supplied as multipart form data:
| Parameter | Example | Description |
|---|---|---|
file | @file.png | The image file as binary data. |
options | "{\"flag_hair\":true,\"flag_sync\":true,\"mode\":\"keep\"}" | Extra options for the generation process. See Options. |
Please note that we only support the following formats: WEBP, JPEG and PNG. Note down these fields from the response:
{
"image_id": "67762577...", // IMAGE_ID
"face_description_list": [
{
"a": { "Age": 25, "Gender": "male" },
"f": 0 // FACE_ID
}
],
"faces": {
"number_of_faces": 2,
"coordinates_list": [
{
"id": 0 // FACE_ID
// ...
},
{
"id": 1 // FACE_ID
// ...
}
]
}
}For more information about the data contained in coordinates_list and how to process it, see Coordinates.
To understand how to use the image_id parameter, also see Image ID.
You can also find out how to use the objects in face_description_list in the Prompts section.
To change the expression on a face, you will have to call the endpoint once for each face you want to modify. This will start asynchronous processes that will be handled in the next step.
This is an example of request containing all required fields for the face with "id": 0 (zero):
{
"id_image": "67762577...", // IMAGE_ID
"id_face": "0", // FACE_ID as string
"prompt": "{\"Expression\":\"happy\"}"
}For more information on the usage of the prompt parameter, see Prompts.
Notification objects returned by this endpoint will inform you of the end of a generation on our servers. We recommend sending a request every 3 seconds at most, to not incur in rate limiting.
{
"name_list": ["new_generation"]
}After the process is complete, you might see a similar response:
{
"notifications_list": [
{
"data": {
"f": 0, // FACE_ID
"g": 0, // GENERATION_ID
"id_image": "67762577...", // IMAGE_ID
"link": "https://...",
},
"name": "new_generation",
},
]
}Where link points to a cropped thumbnail of the face with the expression you requested in the previous step.
The generation_id field is a unique identifier for the process that was started in the previous step and has now ended.
If you start another expression change, the result will have a different generation_id. This ID is needed in the next step.
When you're satisfied with a new expression from the previous step, you can request the server to place the newly generated expression on the original image you started with.
{
"id_image": "67762577...", // IMAGE_ID
"id_face": "0", // FACE_ID as string
"id_generation": "2" // GENERATION_ID
}Uploading an image
Be careful!
All target images are deleted automatically after 24 hours of being uploaded. This action cannot be undone.
You can upload any image containing up to 30 people. Their faces will be automatically detected by the API. Please note that we only support the following formats: WEBP, JPEG and PNG.
import json
import requests
api_url = "https://api.piktid.com/api"
access_token = "your_access_token"
target_path = "path_to_image"
options = {
"flag_hair": True,
"flag_sync": True,
"mode": "keep"
}
with open(target_path, "rb") as target:
response = requests.post(
api_url + "/upload_pro",
headers={"Authorization": "Bearer " + access_token},
files={"file": target},
data={"options": json.dumps(options)},
).json()
image_id = response.get("image_id")Options
The options parameter in the request body must be formatted as a JSON-encoded string. It can contain the following parameters:
| Parameter | Values | Description |
|---|---|---|
flag_hair | true or false | If false, the server will carefully avoid drawing over the subject's hair. |
flag_sync | true or false | If false, the server will not run the detection step but only upload the image and create an image_id. |
mode | "keep" or "random" | If set to "random", generates a new face instead of just changing the expression. See Anonymize for that. |
Changing an expression
Starting an expression change process is easy, but make sure to handle it right. Everything is done asynchronously and the endpoint will return a success code if the process was started successfully.
import json
import requests
api_url = "https://api.piktid.com/api"
access_token = "your_access_token"
image_id = "67762577..."
expressions = {
0: {"Expression": "happy"},
1: {"Eyes": "wink_right"}
}
for face_id, prompt in expressions.items():
requests.post(
api_url + "/ask_new_expression",
headers={"Authorization": "Bearer " + access_token},
json={
"id_image": image_id,
"id_face": face_id,
"flag_sync": False,
"prompt": json.dumps(prompt)
},
)For more information on the usage of the prompt parameter, see Prompts.
Prompts
Expression prompts are defined as a single key-value pair, where:
- the key is a facial feature
- the value is an expression or physical state of the key
To better explain this concepts, here are the currently defined prompts as programming language type definitions:
from dataclasses import dataclass
from typing import Literal
@dataclass
class Prompt:
Expression: Literal[
"happy",
"angry",
"astonished",
"cry",
"disgusted",
"fearful",
"pathetic",
"surprised",
"suspicious",
"neutral",
"sad"
]
Eyes: Literal[
"closed",
"open",
"wink_left",
"wink_right"
]
Gaze: Literal[
"left",
"right",
"up",
"down"
]type Prompt = Partial<{
Expression:
| "happy"
| "angry"
| "astonished"
| "cry"
| "disgusted"
| "fearful"
| "pathetic"
| "surprised"
| "suspicious"
| "neutral"
| "sad"
Eyes:
| "closed"
| "open"
| "wink_left"
| "wink_right"
Gaze:
| "left"
| "right"
| "up"
| "down"
}>In both languages, the following are valid prompt objects:
{
"Expression": "happy"
}{
"Eyes": "wink_left"
}Waiting for an expression change
Be careful!
Notifications are automatically deleted after exactly 10 minutes (600 seconds) from their creation.
Results are shared to clients via "notifications". Clients are expected to poll for notifications and handle them in the allowed time limit before they are automatically dismissed by the API.
import time
import requests
api_url = "https://api.piktid.com/api"
access_token = "your_access_token"
image_id = "67762577..."
def process_notifications(response, image_id):
for notification in response.get("notifications_list", []):
if notification["name"] != "new_generation":
return None, None
if notification["data"]["id_image"] != image_id:
return None, None
return notification["data"], notification["id"]
return None, None
def delete_notification(notification_id):
requests.delete(
api_url + "/notification/delete_json",
headers={"Authorization": "Bearer " + access_token},
json={"id": notification_id},
)
count = 0
# Loops for 300 seconds, even though changing expressions generally
# takes much less time.
for _ in range(300):
response = requests.post(
api_url + "/notification_by_name_json",
headers={"Authorization": "Bearer " + access_token},
json={"name_list": "new_generation,error"},
).json()
data, notification_id = process_notifications(response, image_id)
if data is not None:
# Delete the notification to avoid reading it again
delete_notification(notification_id)
print(f"Face with ID {data['f']} finished processing!")
print(data["link"])
count += 1
if count == len(expressions):
break
time.sleep(1)Remember that the results you just received are only relative to a single face and the response does not contain the final image!
Assembling the final image
Be careful!
All generated images are deleted automatically after 24 hours of being created. This action cannot be undone. Make sure to download them!
To assemble the final image, you need to "pick" faces from the ones you generated so far (remember generation_id?).
Each request picks a single face, so you will need multiple requests to fully assemble the final image.
The following examples assumes that:
- the face with
face_idequals to 0 has been regenerated twice and generation 2 is chosen for the final image - the face with
face_idequals to 1 has been regenerated once and generation 1 is chosen for the final image
import json
import requests
api_url = "https://api.piktid.com/api"
access_token = "your_access_token"
image_id = "67762577..."
# Keys are FACE_ID, values are GENERATION_ID
picked_generations = {
0: "2",
1: "1"
}
final_link = None
for face_id, generation_id in picked_generations.items():
response = requests.post(
api_url + "/pick_face2",
headers={"Authorization": "Bearer " + access_token},
json={
"id_image": image_id,
"id_face": face_id,
"id_generation": generation_id,
# "flag_watermark": 0
},
).json()
final_link = response["links"]["l"]
print("Finished processing: ", final_link)PRO users can also disable watermark application on the final image.