Quickstart
This page will help you get started with move. You'll be up and running in a jiffy!
Single-camera API overview
Our single camera API takes a video file in .mp4
format and returns animation data in the .fbx
.usdc
and .usdz
formats.
The best quality output is obtained when the following steps are all included in the video:
- A human is fully visible at the start of the video
- The camera is static for the duration of the capture
- The entire actor is completely visible in every frame of the capture
- There are no other people visible at any time during the capture
- The actor holds an “A pose” such as [ref fig1] for 1 second at the start of the video
- Clothing worn by the actor has good contrast to their background
- The actor is well lit
In theory #1 is the only hard requirement for your capture to be processed by the API. Failure to meet the other criteria will degrade the output quality to varying degrees.
Existing applications
We recommend using one of our existing application, built on the same APIs, to experiment with inputs and outputs.
Move One iOS app
Our Move One app is the best place to test the process of capturing some motion and getting output data back. It uses the same API and Swift SDK. Our knowledge base provides further details on how to get good quality single person capture outputs.
Move One Bot
For tests that use a pre-existing video, consider the Move One Bot available on our Discord server. This will take any video up to 10s in length at at least FHD resolution and return the output data in direct messages.
Authentication
All calls to our API require an API key to be provided in the Authorization
header of the request. At this stage, while we are in an early access period, individual API keys will be issued via a one-time secret link. If you require multiple API keys (e.g. for development environments) please request these from your Move AI contact.
Data model
All inputs and outputs from the system are files and have a guid.
A take is a video input file and the optional additional files that can be used by the system to improve the quality of the output.
Takes that are currently processing or have been processed. The take is referenced by the job. The output files are attached to the job. The state of the job tells the user what stage of the processing cycle the job is currently in.
Giving feedback
You will be provided with an API key by a member of staff at Move AI. Please feel free to reach out to them with any questions or feature requests. We can also set up shared slack channels to provide more support to early adopters of the API.
Developer notes
Job state
The lifecycle of a job is:
NOT_STARTED
- submitted but not startedSTARTED
- has been sent to a server for processingRUNNING
- is running on the serverFINISHED
- has produced some outputs (this has no relation to the quality of the output, just that some output was generated)FAILED
- we couldn’t process the output
Processing time
Three main factors drive the time it takes for a take to process:
- Duration of video
- Resolution/framerate
- Availability of processing servers
For 10s FHD at 60fps with a server immediately available the processing should be complete with 5 minutes. If there isn't a server available then the time may be as high as 30 minutes for the same video. We make efforts to ensure that this happens as rarely as possible but at certain times, especially as we release updates to the processing engine, these may be more common. This is part of the reason we advise you avoid polling in production and use webhooks.
Webhooks
We provide an interface to be able to subscribe a webhook to events associated with Jobs. Details about the events are documented here:
Webhook Events | move.ai · Svix
The GraphQL mutation required to specify your endpoints is:
mutation CreateWebhook {
webhook: upsertWebhookEndpoint(
url: "https://your-webhook-url.com",
description: "This webhook does awesome things.",
secret: "your-secret",
events: ["ugc.job.state.completed"],
uid: "your-unique-id-for-webhook",
metadata: "{\"key\": \"value\"}"
) {
uid
secret
description
url
events
metadata
}
}
Complete JavaScript example
A simple JavaScript example app is available on github here: https://github.com/move-ai/move_js_sample.
Complete Python example
This uses our Python SDK.
pip install move-ugc-python
import os
import time
from datetime import datetime
import requests
from move_ugc import MoveUgc
class MoveAPI:
def __init__(self, api_key, endpoint_url=None):
self.api_key = api_key
if endpoint_url is None:
endpoint_url = 'https://api.move.ai/ugc/graphql'
self.endpoint_url = endpoint_url
self.client = MoveUgc(api_key=api_key, endpoint_url=endpoint_url)
def create_files(self, video_path):
video_file = self.client.files.create(file_type="mp4")
with open(video_path, 'rb') as f:
requests.put(video_file.presigned_url, data=f.read())
return video_file.id
def create_take(self, video_file_id, metadata=None):
if metadata is None:
metadata = {"test": "test"}
take = self.client.takes.create(
video_file_id=video_file_id,
metadata=metadata,
)
return take
def get_take(self, take_id):
take = self.client.takes.retrieve(id=take_id)
return take
def create_job(self, take_id):
job = self.client.jobs.create(take_id=take_id, metadata={"test": "test_job"})
return job
def get_job(self, job_id, expand=False):
# Get a job using the Move One Public API
# Implement job retrieval logic using move_ugc_python SDK
if expand is False:
job = self.client.jobs.retrieve(id=job_id)
else:
job = self.client.jobs.retrieve(
id=job_id, expand=["take", "outputs", "client"]
)
return job
def download_outputs(self, job_id, output_dir, output_name):
# make output dir if it doesn't exist
if not os.path.exists(output_dir):
os.makedirs(output_dir)
# get job
job = self.get_job(job_id, expand=True)
# for each output download the file in the output_dir
output_paths = []
for output in job.outputs:
output_file_name = f"{output_name}{output.file.type}"
output_path = os.path.join(output_dir, output_file_name)
with open(output_path, 'wb') as f:
response = requests.get(output.file.presigned_url)
f.write(response.content)
output_paths.append(output_path)
return output_paths
if __name__ == '__main__':
move = MoveAPI(os.getenv("MOVE_API_KEY"))
filename = 'portrait.mp4'
video_file_id = move.create_files(filename)
take = move.create_take(video_file_id)
job = move.create_job(take.id)
attempts = 0
while attempts < 100:
job = move.get_job(job.id)
update_str = f"[{datetime.now().isoformat()} | {attempts}] Job {job.id} is {job.state}"
print(update_str)
if job.state == 'FINISHED':
outputs = move.download_outputs(job.id, 'outputs', filename.split('.')[0])
print(outputs)
break
else:
time.sleep(30)
attempts += 1
Updated 9 months ago