Data acquisition from the IE Vision Connector

AI Inference Server Function Manual

Portfolio
Industrial AI
Product
AI Inference Server
Software version
2.1.0
Language
en-US

AI Inference Server offers two options for recording data from the IE Vision Connector.

  • via Databus (low data throughput)

  • via ZMQ (high data throughput)

The second method has higher performance and is therefore recommended for images with higher resolution.

A detailed configuration description can be found in the IE Vision Connector user manual.

IE Vision Connector with low data throughput (Databus-based, string)

Only an earlier version of the IE Vision Connector (p 0.9.5) supports sending images to the Databus. This payload format in the AI Inference Server is only retained for reasons of backward compatibility. Newer versions of IE Vision Connector (≥ v 1.0.0) only support data transfer with a high data throughput.

  • Pipeline configuration

    • The type String should be chosen for the relevant variable.

  • Like with the other connectors, this connector should be selected/created in the Data connection section of the configuration page.

  • The preset port is the same as for the Databus: 1883

  • The Topic-Variable-Mapping is the same as in the case of the Databus.

  • The payload structure with use of the IE Vision Connector via Databus looks like this:

{

"timestamp":"2021-12-14T10:37:21.315513",

"sensor_id":"a204dba4-274e-43ce-9a71-55de9e715e72",

"image":"data:image/png;base64,iVBORw0KGgoAAAANSU…",

"status":{"genicam_signal":{"code":3}}

}

The Python script should take care of handling this payload format.

Example script, assuming the vision_payload parameter contains the above payload JSON object, get_image_from_vision_payload returns the image object as PIL image:

from urllib.request import urlopen

from PIL import Image

import io

from keras.preprocessing import image as imglib

import numpy as np

from tensorflow import keras

from log_module import LogModule

logger = LogModule()

 

model = keras.models.load_model('models/classification_mobilnet.h5')

IMAGE_WIDTH = 224

IMAGE_HEIGHT = 224

IMAGE_SIZE = (IMAGE_WIDTH, IMAGE_HEIGHT)

SCALE = 255

def get_image_from_vision_payload(vision_payload):

    """

    Takes a Vision Connector JSON payload, decodes the image and returns it as PIL image object. Returns None if decoding fails.

    """

    global IMAGE_SIZE

    try:

        image_string = vision_payload['image']

        with urlopen(image_string) as response:

            assert response.headers["Content-type"] in ["image/png", "image/jpeg"]

            logger.debug("Verified image type is PNG or JPEG")

            image_bytes = response.read()

            pil_image = Image.open(io.BytesIO(image_bytes)).resize(IMAGE_SIZE)

            logger.debug(f"Image info: {pil_image}")

        return pil_image

    except BaseException:

        logger.debug("Error decoding image from vision payload")

        return None

IE Vision Connector with high data throughput (ZMQ-based, multi-part)

  • For performance reasons, the Vision Connector can publish the images in binary format via the ZMQ protocol

  • Pipeline configuration

  • The AI expert should select the type (ImageSet or Object (legacy type)) for the variable of the model. ImageSet is preferred.

  • The connector configuration is shown in the following screenshot. The default port is 5555

Example of a topic-variable mapping:

The Python script receives this variable as dict, which is structured differently for the ImageSet and Object input types.

The data flow from the camera to the Python script looks like this:

For the ImageSet type, the dict variable is structured as follows:

from log_module import LogModule

logger = LogModule()

 

def process_input(data: dict):

 

# Note: the input dict of the process_input always contains a timestamp field which is a string and it is conform to the ISO 8601

# In this case it will be equal to the data["image"]["timestamp"]

logger.info("Input timestamp: {0}".format(data["timestamp"]))

 

logger.info("Data:")

logger.info("Imageset version: {0}".format(data["image"]["version"]))

logger.info("Imageset timestamp: {0}".format(data["image"]["timestamp"]))

logger.info("Imageset cameraid: {0}".format(data["image"]["cameraid"]))

images = data["image"]["detail"]

logger.info("Timestamp of the input: {0}".format(str(images[0]["id"])))

 

logger.info("Image id: {0}".format(str(images[0]["id"])))

logger.info("Image seq: {0}".format(str(images[0]["seq"])))

# the timestamp in the detail is optional and it is a string allowing other timestamp formats beside the ISO 8601

if "timestamp" in images[0]:

    logger.info("Image timestamp: {0}".format(images[0]["timestamp"]))

logger.info("Image format: {0}".format(images[0]["format"]))

# The ‘Genicam’ defined formats like BGR8 or Mono8 always provide the width and height, but other formats may not provide it. For example the "PNG" od "JPG"formats will not provide it.

if "width" in images[0]:

    logger.info("Image width: {0}".format(str(images[0]["width"])))

    logger.info("Image height: {0}".format(str(images[0]["height"])))

# Line padding is optional, its existence depends on the ‘format’

if "linepadding" in images[0]:

    logger.info("Image linepadding: {0}".format(images[0]["linepadding"]))

logger.info("Image byte size: {0}".format(str(len(images[0]["image"]))))

# call the model

...

     # prepare and return the prediction result

     output = ...

    

    return output

  • The "ImageSet" input type in the pipeline configuration is only supported with the newly introduced "process_input" interface.

  • The data always contains the "timestamp" field as string in ISO 8601 format. For the ImageSet type, this is identical to the "timestamp" within the "data" input.

  • The data contains the variable from the pipeline package if it was set to "image" in this example.

  • The types of the various elements in the image are as follows:

    • version – String – Version of the message format. Latest: "1"

    • cameraid – String – ID of the camera device

    • timestamp – String - in ISO 8601 format with millisecond accuracy, e.g. "2022-01-31T20:09:28.987Z"

    • customfields – String - JSON containing additional information added to the image message

    • detail – array<Image Detail> - Array field consisting of N elements. The content includes necessary information for the image processing

    Image Detail

    • – String – Image format specified in the GenICam standard, see: https://www.emva.org/wp-content/uploads/GenICam_SFNC_v2_7.pdf, e.g. RGB8, BGR8, Mono8. The most likely values might be: "Genicam", "Compressed".

    • width – Integer – Image width Mandatory for formats that require a width (e.g. RGB8 and all Genicam formats).

    • heigth – Integer – Image height Mandatory for formats that require a height (e.g. RGB8 and all Genicam formats).

    • metadata – String – Any JSON data about the image

    • linepadding – optional integer – is provided for some Genicam formats. Although optional, it is set to 0 by the VCA.

    • image – Binary – Binary representation of the image

  • The so-called multi-part ZMQ binary format looks like this in Python syntax:

[

b'"6ec59ac7-9288-402f-9008-4a2aaa414f8e"

b'{"version":"1","count":1,"detail":[{"id":"6ec59ac7-9288-402f-9008-4a2aaa414f8e","seq":987, "height":123,"width":345, "format":"RGB", "linepadding":0, "metadata":{"custom":"field"}}],"timestamp":"2022-01-31T20:09:28.987Z","customfields":{"custom":"field"}}

b'\x18\x18\x1b\x18\xbe\xbb\xb7\xf9\xf9\xf3\xeb\xe8\xe3\xec\xec\xe6\xeb\xec\xea\xe1\xef\xed\xc3\xe7\xe7\xbc...

]

For the legacy type Object, the dict variable is structured as follows:

from log_module import LogModule

logger = LogModule()

def process_input(data: dict):

logger.info("Data:")

logger.info("Image format: {0}".format(data["image"]["mimeType"]))

logger.info("Image width: {0}".format(str(data["image"]["resolutionWidth"])))

logger.info("Image height: {0}".format(str(data["image"]["resolutionHeight"])))

logger.info("Image data type: {0}".format(data["image"]["dataType"]))

logger.info("Image color channels: {0}".format(str(data["image"]["channelsPerPixel"])))

logger.info("Image byte size: {0}".format(str(len(data["image"]["image"]))))

# call the model

...

# prepare and return the prediction result

output = ...

return output

  • The Object and ImageSet input types in the pipeline configuration are only supported with the newly introduced interface process_input.

  • The data contains the variable from the pipeline package if it was set to image in this example.

  • The data also includes the time stamp of the image

    • timestamp: String in ISO8601 format with millisecond accuracy, e.g. "2022-01-31T20:09:28.987Z"

  • The types of the various elements in the image are as follows:

    • mimeType: String – always "image/raw"

    • resolutionWidth, resolutionHeight: Integer – example: 640 or 480

    • dataType: String – always "uint8" – 1 byte per color channel

    • channelsPerPixel – Always 3, and the color order is always BGR

    • image – bytes() – contains the image, each pixel is defined by 3 bytes

  • The so-called multi-part ZMQ binary format looks like this in Python syntax:

[

b'{"timestamp": "2022-01-31-T20:09:28.987251""sensor_id": "bd41278f-7a40-4c42-9e96-c6d5be2695fa", "Content-Type": ["image/raw"]}',

b'[{"dtype": "uint8", "shape": [360, 640, 3]}]',

b' \x18\x18 \x1b\x18\xbe\xbb\xb7\xf9\xf9\xf3\xeb\xe8\xe3\xec\xec\xe6\xeb\xec\xea\xe1\xef\xed\xc3\xe7\xe7\xbc...'

]

  • The mapping between the multi-part binary file and elements of the dict for the above image variable is as follows in the input:

ZMQ multi-part

Variable

Comment

The first string in the input

-

The topic that is used during mapping on the user interface of the AI Inference Server. Does not exist on the Python script level.

timestamp

timestamp

ISO 6801

sensor_id

-

Part of the topic. Does not exist on the Python script level.

Content-Type

image["mimeType"]

Always "image/raw"

shape[0]

image["resolutionWidth"]

 

shape[1]

image["resolutionHeight"]

 

shape[2]

image["channelsPerPixel"]

Always 3 and means BGR order

dtype

dataType

Always "uint8"

The next binary part

image["image"]

 

Notes on the multi-part structure

  • Multiple resolutions, data types and image files are allowed in a single message. The AI Inference Server splits them and the Python script receives them individually.

  • The AI model should validate the input as best practice. The AI Inference Server converts the ZMQ format into the "Python" format, but cannot and does not check the contents. For example, the fixed content of the mimeType is a contract that is provided by the VCA provider and not by the AI Inference Server.

See alsoGeniCam_SFNC