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.

2382

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.

ArgumentDescriptionExample
image(required) encoded UTF-8 string representation of the image for inference.
media_timestamp
float
(optional) actual timestamp of media creation/occurrence time1604007056.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 codeDescription
200Successful completion. Data returned as JSON object.
4xx401 - 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.
5xx500 - 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)