Process (inference api)
Inference API for CloudFlow (cloud-hosted EdgeFlow instances)
CloudFlow Inference API
CloudFlow systems support an inference API similar to on-premises Edgeflows. The major difference is that all transactions with CloudFlow systems use the HTTPS protocol.
POST /1/process/{subject_uid}
HOST: https://<cloudflow-id>.cloudflow.cogniac.io
The URL Prefix for inference requests is composed of the CloudFlow identifier and suffixed with cloudflow.cogniac.io
The URL must include the input subject_uid of the first application in a pipeline of applications. This would be an HTTP-Input application type whose configured output subject uid would be the input subject specified in the CloudFlow /1/process inference POST request. An example application flow is shown below.
In the example above, The output subject configured in the HTTP Input app would be the subject_uid specified in the /1/process request.
Additional arguments can be provided as part of the form data as shown in the table below.
The HTTPS response includes all detections created by applications that received the image specified in the form data along with the HTTPS status response code.
Argument | Description | Example |
---|---|---|
image | (required) encoded UTF-8 string representation of the image for inference. | |
media_timestamp float | (optional) actual timestamp of media creation/occurrence time | 1604007056.62 |
uploaded_by_user string | (optional) user originating request | [email protected] |
domain_unit string | (optional) domain id for set assignment grouping or allowing correlation between groups of images | "test-domain-1 |
external_media_id string | (optional) arbitrary external id for this media. | "test-external-tracking-993" |
custom_data string | (optional) a string of opaque data associated with the input media. | '{'abc': 123, 'test': 'xyz'}' |
Responses
Error code | Description |
---|---|
200 | Successful completion. Data returned as JSON object. |
4xx | 401 - Missing Bearer token in the Authorization header 403 - Invalid Bearer token in the Authorization header - refresh the token 404 - Subject ID not found - The output subject uid of the HTTP input application is not configured with the subject uid used in the /1/process call. |
5xx | 500 - Cannot parse input data - form data incorrect 504 - Backend response timeout - timeout from downstream application(s) - the request took more than 60seconds. 504 - Failed to parse detection - detection format from downstream applications |
API Usage Example
API Usage Example using version 2.0.9 and above of the Cogniac SDK.
The following steps are required when using the /1/process inference API:
- Authenticate with Cloudcore
- Acquire a token from Cloudcore
- use the token when POSTing a /1/process request to cloud core.
The following example uses the Cogniac Python SDK to simplify the task of authentication and token acquisition.
The SDK will simply the authentication, token request, and renewal process in addition to sending a POST request to CloudFlow instance.
from cogniac import CogniacConnection
from time import time
import simplejson as json
TENANT_ID = 'test-tenant-axy'
CLOUDFLOW_ID = 'testfufh7'
FILENAME = 'd1.jpg'
SUBJECT_UID = 'side_view_camera_9lr'
# authenticate with cloudcore and fetch token used in API call
cc = CogniacConnection(tenant_id=TENANT_ID)
ef = cc.get_edgeflow(CLOUDFLOW_ID)
# invoke inference api for subject and image
try:
resp = ef.process_media(SUBJECT_UID, FILENAME)
detections = resp['detections'] # list all detections
except Exception as ex:
pass
An example JSON formatted response is shown below. The primary key is 'detections' whose value is a list of all detections generated by applications downstream from HTTP Input application.
Each detection entry contains the application_id reporting the detection, the model used, output subject_id, probability, and specific application data associated with the application. For example, a box detection app will report app_data_type as 'box_set' and a list of boxes in the app_data dictionary.
{u'detections': [{u'app_data': None,
u'app_data_type': None,
u'app_id': u'z2PNlsAs',
u'assertion_prefix': u'EF:v2p8ckya:NDQIWWPIJI',
u'created_at': 1639010302.243763,
u'detection_id': u'EF:v2p8ckya:NDQIWWPIJI:1',
u'domain_unit': None,
u'external_media_id': u'drone_photos_6op-1639010267990000',
u'filename': u'LTECFY7OTIUOJFYWGAHKJZ62MQM7-1.jpg',
u'focus': None,
u'input_subject_association': {u'probability': 1.0,
u'subject_uid': u'drone_photos_6op'},
u'media_md5': u'213a2d9c8d5b53472b8dd555df27ad0b',
u'model_id': u'InitialModel_z2PNlsAs_KLHP0K.tgz',
u'other_subject_associations': [],
u'subject_uid': u'buildings_2sck',
u'uncal_prob': 0.0,
u'user_id': None},
{u'app_data': [{u'box': {u'x0': 826,
u'x1': 907,
u'y0': 653,
u'y1': 689},
u'probability': 0.91015625},
{u'box': {u'x0': 1039,
u'x1': 1083,
u'y0': 214,
u'y1': 324},
u'probability': 0.449462890625}],
u'app_data_type': u'box_set',
u'app_id': u'kHG1csvg',
u'assertion_prefix': u'EF:v2p8ckya:NUFJHOCZKE',
u'created_at': 1639010303.168401,
u'detection_id': u'EF:v2p8ckya:NUFJHOCZKE:1',
u'domain_unit': None,
u'external_media_id': u'drone_photos_6op-1639010267990000',
u'filename': u'LTECFY7OTIUOJFYWGAHKJZ62MQM7-1.jpg',
u'focus': None,
u'input_subject_association': {u'probability': 1.0,
u'subject_uid': u'drone_photos_6op'},
u'media_md5': u'213a2d9c8d5b53472b8dd555df27ad0b',
u'model_id': u'donor_IVAC_Hpo-d-8e49-DmDT9hhuJLSiFuWOge-YN-OW_mtsv1_INT_10000.tgz',
u'other_subject_associations': [],
u'subject_uid': u'vehicles_carbox_3gu',
u'uncal_prob': 0.91015625,
u'user_id': None},
{u'app_data': None,
u'app_data_type': None,
u'app_id': u'8H88vm4v',
u'assertion_prefix': u'EF:v2p8ckya:RITLRGGXGC',
u'created_at': 1639010303.297092,
u'detection_id': u'EF:v2p8ckya:RITLRGGXGC:11',
u'domain_unit': None,
u'external_media_id': u'drone_photos_6op-1639010267990000',
u'filename': u'LTECFY7OTIUOJFYWGAHKJZ62MQM7-1.jpg',
u'focus': {u'box': {u'x0': 1039,
u'x1': 1083,
u'y0': 214,
u'y1': 324}},
u'input_subject_association': {u'probability': 0.91015625,
u'subject_uid': u'vehicles_carbox_3gu'},
u'media_md5': u'213a2d9c8d5b53472b8dd555df27ad0b',
u'model_id': u'Hpo-m-2d25-KHkIx4sG43EYsU5PzZ-GN-rf_224_224_mtsv1_2.tgz',
u'other_subject_associations': [{u'probability': 1.0,
u'subject_uid': u'drone_photos_6op'}],
u'subject_uid': u'vehiclestestoutput_6jo',
u'uncal_prob': 3.634687664858485e-23,
u'user_id': None}]}
If not using the Cogniac Python SDK, an equivalent usage of the /1/process API is shown below.
The sample code uses a username, password to authenticate and retrieve an API token from api.cogniac.io
. The credentials should be the same as those used for Cloudcore.
This token is then used to send a /1/process POST request to the Cloudflow server.
import os
import sys
import requests
from requests.auth import HTTPBasicAuth
from requests.exceptions import ReadTimeout, ConnectionError
from retrying import retry
API_VERSION = 1
TIMEOUT= 60 # seconds for response
CLOUDFLOW_ID = 'testfufh7'
TENANT_ID = 'test-tenant-axy'
SUBJECT_UID = 'drone_photos_6op'
URL_PREFIX = 'https://%s.cloudflow.cogniac.io/%d/' % (CLOUDFLOW_ID, API_VERSION)
username = os.environ['COGNIAC_USER']
password = os.environ['COGNIAC_PASSWORD']
class ServerError(Exception):
"""Server timeouts"""
class ClientError(Exception):
"""Unknown local Error"""
class CredentialError(Exception):
"""Expired Credentials"""
def server_error(exception):
"""Return True if we should retry (in this case when it's an ServerError, False otherwise"""
#logger.info("Got an exception: {}".format(type(exception)))
return isinstance(exception, (ServerError, ReadTimeout, ConnectionError))
def raise_errors(response):
"""Raise ServerError or ClientError based on requests response as appropriate."""
if response.status_code >= 500:
msg = "ServerError (%d): %s" % (response.status_code, response.content)
raise ServerError(msg)
@retry(stop_max_attempt_number=8, wait_exponential_multiplier=50, retry_on_exception=server_error)
def get_header():
"""
retrieve access token from Public API service
"""
tenant_data = {'tenant_id': TENANT_ID}
token_resp = requests.get('https://api.cogniac.io/1' + "/token", params=tenant_data,
auth=HTTPBasicAuth(username,password),
timeout=TIMEOUT)
status = token_resp.status_code
content = token_resp.content
if status >= 500:
raise ServerError("ServerError (%d): %s" % (status, content))
elif status in [401, 403]:
msg = "Invalid username password credentials (%d): %s" % (status, content)
raise CredentialError(msg)
elif status >= 400:
msg = "ClientError (%d): %s" % (status, content)
raise ClientError(msg)
else:
token = token_resp.json()
headers = {"Authorization": "Bearer %s" % token['access_token']}
return headers
@retry(wait_exponential_multiplier=20, retry_on_exception=server_error)
def process_media(filename, subject_uid):
files = {'file': open(filename, 'rb')}
header = get_header()
data = {"media_timestamp": time(), 'uploaded_by_user': '[email protected]',
'domain_unit': 'test_domain_1',
'custom_data': json.dumps({'test': "123test", 'tick': time()}}
t0 = time()
try:
url = URL_PREFIX + 'process/%s' % subject_uid
resp = requests.post(url, data=data, files=files, headers=header, timeout=TIMEOUT, verify=True)
raise_errors(resp)
if resp.status_code != 200:
else:
detection = resp.json()
except:
inference_time = 0
return inference_time
if __name__ == '__main__':
imgfile = 'd1.jpg'
result = process_media(imgfile, SUBJECT_UID)
Updated over 1 year ago