Perceptor API specifications

Application Version: 2.1


QBOID Confidential and Proprietary Information


QBOID Perceptor products are 3D computer vision based products to speed up or automate typical human perception tasks that are related to perceiving shape and dimension. The Perceptor series algorithmically generates a set of object metadata, for every scan event that is to be saved, quantifying shape and dimension, annotated image data, as well as a variety of other data manually input by the operator, such as barcode scanner reads (either onboard the QBOID M2 product, or third party scanners, as supported by the QBOID S1 Perceptor), weight of an object, and other future data types.

Once the scan event is completed, either manually by an operator specific key press, or automatically by QBOID software, the 'sizing record' is immediately queued up for offloading to a remote resource, e.g. a WMS system or manifesting software. QBOID is making available the following documentation for independent software developers to transfer the Perceptor system sizing records to their software platforms .

In general, the main Perceptor API is a REST-like API where a JSON message is sent to a local or internet HTTP endpoint, and is designed to be familiar to developers doing client server web development.

Table of content



Definitions

Device

The QBOID equipment that is generating the sizing record. Can be a Perceptor M2 or S1.

Server

The machine that is receiving and consuming the sizing record.


Basic info

The Device will send a POST request with a JSON body to the HTTP endpoint defined in its configuration expecting a 200-299 HTTP response code.

The typical JSON body is like the following:

{
  "timestamp": "2021\/02\/14 07:19:38",
  "l": 195,
  "w": 165,
  "h": 72,
  "weight": 7709,
  "barcode": "845973030506",
  "shape": "Cuboidal",
  "device": "639b707f",
  "note": "",
  "attributes": {
    "hazmat": "true"
  }
}

Message structure

field format Description
timestamp string yyyy/MM/dd HH:mm:ss UTC timestamp using the Device time. Hour format is (0-23).
l int Length of the item in mm. Length is defined as the biggest among the 2 measurements parallel to the ground plane.
w int Width of the item in mm. Width is defined as the smallest among the 2 measurements parallel to the ground plane.
h int Height of the item in mm. Height is defined as the measurement orthogonal to the ground plane.
weight int
(Optional)
Weight of the item in g (grams).
barcode string
shape string Currently supported values are Irregular and Cuboidal. Additional shapes will be supported in the future.
device string Device serial number. Same as shown in Settings->System->About Phone->Status->Serial number.
note string Generic alphanumeric note screen as added by the user.
attributes map(string->string)
{} when empty
Contains the values assigned using the attributes buttons. Attribute buttons are fully customizable any arbitrary combination of key/value may be present.
image string
PNG/JPEG base64 image
(optional)
Full resolution color image with bounding box drawn on top.
Can be enabled/disabled in settings. The default image format is PNG, while JPEG can be selected in settings.
imagecolor string
PNG/JPEG base64 image
(optional)
Full resolution color image without bounding box drawn on top.
Can be enabled/disabled in settings. The default image format is PNG, while JPEG can be selected in settings.
imageseg string
PNG base64 image
(optional)
Object Image with background subtracted. Pixel that belong to the background have the alpha channel set to 0.
JPEG format is not supported because of the alpha channel requirement.

M2 API settings

Optional fields, image format and endpoint can be configured in the M2 app in the section: Settings > Data Settings > WiFi API.

Entry Description
API endpoint Define the URL for the HTTP/HTTPS endpoint.
Upload result image Enable/Disable the image field.
Upload color image Enable/Disable the imagecolor field.
Upload segmented image Enable/Disable the imageseg field.
Use JPEG format Select JPEG format for image and imagecolor fields.

Advanced Info

Escaping

String fields would be escaped as per JSON standard, more specifically:

input output
New Line \n
\ \\
/ \/
Carriage return \r
Tab \t

Image encoding

Images are encoded in the JSON request as a Data URI scheme with a base64 encoding adhering RFC-2045.

Following is an example of an encoded image:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAHklEQVR42mP8z8AARNQDjKMGjho4auCogaMGjlQDAUwCJ+0NBcXlAAAAAElFTkSuQmCC

Note: Previous string can be copied and pasted to browser address bar to visualize the image.

HTTP request

HTTP request is configured as following

Property Value
Request Method POST
Content-Type application/json; utf-8
Accept application/json

A response code in the range 200-299 is expected as a response.

Sample RAW TCP Message:

POST /newEntry HTTP/1.1
Content-Type: application/json; utf-8
Accept: application/json
User-Agent: Dalvik/2.1.0 (Linux; U; Android 8.1.0; Scorpio Build/V008R001)
Host: 192.168.7.103:5000
Connection: Keep-Alive
Accept-Encoding: gzip
Content-Length: 199

{"timestamp":"2021\/02\/25 02:16:25","l":266,"w":203,"h":72,"weight":6807,"barcode":"X001SS387V","shape":"Irregular","device":"639b707f","note":"QWERTY","attributes":{"batt":"false","hazmat":"true"}}

HTTPS support

Added in 2.1.0
HTTP over TLS can be used simply defining https as protocol in the URL of the endpoint.
If the server's SSL certificate was issued by a well known CA, no other operation is required.

Self-signed server certificate

A self-signed certificate is one that is signed with the servers own private key instead of being signed by a CA.
If the server is using a self-signed certificate, adding the certificate to the Perceptor M2 (see link) will be necessary. Failure to do so will throw the following error when the M2 is attempting to communicate with the server:

javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
Generate the self-signed certificate

Following is an example on how to create a self-signed certificate using OpenSSL command line tool

openssl req -x509 -newkey rsa:4096 -keyout self_key.pem -out self_cert.pem -sha256 -days 365 -nodes \
 -subj "/C=US/ST=California/L=San Jose/O=Qboid/OU=TestingWebService/CN=testing.qboid.ai/emailAddress=info@qboid.ai" \
-addext "subjectAltName=DNS:testing.qboid.ai"
Add the self-signed certificate to Perceptor M2


Testing Server

QBOID provides a testing www server that can be used for experimenting with the different API configurations/protocols.
The test server can be reached through a reverse proxy that's exposing the same service with different combinations of protocols and certificates. All endpoints refers to the same service, so data pushed over https will be visible on the http page and vice versa.

Disclaimer

Before using the server, please review the following:

List of endpoints

Protocol Web Visualization URL M2 Endpoint
HTTP http://testing.qboid.ai:5001/ http://testing.qboid.ai:5001/newEntry
HTTPS (self signed) https://testing.qboid.ai:5002/ https://testing.qboid.ai:5002/newEntry
HTTPS (public CA) https://testing.qboid.ai:5003/ https://testing.qboid.ai:5003/newEntry

Web Visualization URLs can be opened with any PC or Phone browser. The page will wait for 120s for incoming data sent by any M2 device that's currently using a Testing Server Endpoint.

Device configuration

Testing server endpoints can be configured on the M2 settings in Data Settings->API Endpoint.
Click on Use Testing Server and select the desired combination of Protocol and certificate.

For using the self signed certificate:


Code examples

Minimal Python app for receiving/parsing a message

Code

from flask import Flask
from flask import request
import json
app = Flask(__name__)

@app.route("/newEntry", methods=['POST'])
def newEntry():
    data = json.loads(request.data)
    print("Barcode: %s Size: %ix%ix%i" % (data["barcode"], data["l"], data["w"], data["h"]))
    return ""

if __name__ == "__main__":
    app.run(host = '0.0.0.0')

Output (2 sizing events of the same object)

Barcode: X001SS387V Size: 266x201x72
192.168.7.108 - - [24/Feb/2021 19:34:15] "POST /newEntry HTTP/1.1" 200 -
Barcode: X001SS387V Size: 267x200x72
192.168.7.108 - - [24/Feb/2021 19:36:00] "POST /newEntry HTTP/1.1" 200 -

For testing the previous code set the device end point to:

http://[HOST-IP]:5000/newEntry
where [HOST-IP] is the IP address of the PC running the code. E.g.: `192.168.7.100`

Python for generating sample messages

Code

import requests
import json
import random
import string
from datetime import datetime

# To be changed according to the setup
ENDPOINT = 'http://127.0.0.1:5000/newEntry'

def rndString(minsize, maxSize):
    return ''.join(random.choices(string.ascii_uppercase + string.digits, k = random.randint(minsize, maxSize))) 

def createJsonEntry():
    data = {}
    data['timestamp'] = datetime.utcnow().strftime('%Y/%m/%d %H:%M:%S')
    data['l'] = random.randint(10,500)
    data['w'] = random.randint(10,500)
    data['h'] = random.randint(10,500)
    data['weight'] = random.randint(10,50000)
    data['barcode'] = rndString(2,20)
    data['shape'] = 'Irregular' 
    data['device'] = rndString(8,8)
    data['note'] = rndString(0,20)
    attributes = {}
    for i in range(random.randint(0, 4)):
        attributes[rndString(3,5)] = rndString(3,5)
    data['attributes'] = attributes
    strjson = json.dumps(data)
    # Explicitly escaping "/" since python json library is not doing it by default
    strjson = strjson.replace('/', '\/')
    return strjson

if __name__ == "__main__":
    jsonMsg = createJsonEntry()
    print(jsonMsg)
    headers = {'content-type': 'application/json; utf-8', 'Accept' : 'application/json'}
    try:
        response = requests.post(ENDPOINT, data=jsonMsg, headers=headers)
        if (response.ok):
            print('Success')
        else:
            print('Error. Endpoint %s returned code: %i' % (ENDPOINT, response.status_code))    
    except Exception as e:
        print('Error while sending request to endpoint: %s' % ENDPOINT)

Output (Success)

{"timestamp": "2021\/02\/25 05:02:08", "l": 56, "w": 419, "h": 68, "weight": 46405, "barcode": "KPGVETAS", "shape": "Irregular", "device": "Y3S9JH1D", "note": "3RI9CKE2N", "attributes": {"FJ4": "U1HC6"}}
Success

Output (Endpoint returned an error)

{"timestamp": "2021\/02\/25 05:04:52", "l": 494, "w": 430, "h": 290, "weight": 31464, "barcode": "GE7WJ83B3PNJBN", "shape": "Irregular", "device": "DO1KF5ZI", "note": "A3KYJ", "attributes": {"SVRAJ": "HMXK"}}
Error. Endpoint http://127.0.0.1:5000/newEntry returned code: 405

Output (Endpoint unreachable)

{"timestamp": "2021\/02\/25 05:06:08", "l": 449, "w": 129, "h": 432, "weight": 2339, "barcode": "UUYXJ9Y447M", "shape": "Irregular", "device": "BO2X6TD2", "note": "B71U", "attributes": {"AF7Y": "BX44"}}
Error while sending request to endpoint: http://127.0.0.1:5000/newEntry