Skip to content

Sending & querying images

Sending images

In this example we will send images with labels to SEEREP.

In order to save images, we need to mandatorily provide the intrinsics of the camera used to capture them. After the successfully saving the camera intrinsics, we need to provide the uuid of it along with the images. SEEREP will ensure that the Camera Intrinsics UUID provided with an image has a UUID stored against it.

Additionally we add some coordinate transformations at the end.

Source: examples/python/gRPC/images/gRPC_pb_sendLabeledImage.py

#!/usr/bin/env python3
# NOTE: This file is referenced in the following mkdocs files:
#   images.md
# Any changes done in here will be reflected in there
import time
import uuid
from typing import List, Optional, Tuple

import numpy as np
from google.protobuf import empty_pb2
from grpc import Channel
from seerep.pb import camera_intrinsics_pb2 as cameraintrinsics
from seerep.pb import (
    camera_intrinsics_service_pb2_grpc as camintrinsics_service,
)
from seerep.pb import image_pb2 as image
from seerep.pb import image_service_pb2_grpc as imageService
from seerep.pb import label_category_pb2, label_pb2
from seerep.pb import meta_operations_pb2_grpc as metaOperations
from seerep.pb import projectCreation_pb2 as projectCreation
from seerep.pb import tf_service_pb2_grpc as tfService
from seerep.pb import transform_stamped_pb2 as tf
from seerep.util.common import get_gRPC_channel


def send_labeled_images(
    target_proj_uuid: Optional[str] = None,
    grpc_channel: Channel = get_gRPC_channel(),
) -> Tuple[
    List[List[image.Image]], List[int], cameraintrinsics.CameraIntrinsics
]:
    """sends test images via the given grpc_channel to the specified target
    project uuid"""

    # 1. Get gRPC service objects
    stub = imageService.ImageServiceStub(grpc_channel)
    stubTf = tfService.TfServiceStub(grpc_channel)
    stubMeta = metaOperations.MetaOperationsStub(grpc_channel)
    stubCI = camintrinsics_service.CameraIntrinsicsServiceStub(grpc_channel)

    # 2. Check if we have an existing test project (or target_proj_uuid is set),
    # if not, one is created.
    if target_proj_uuid is None:
        # 3. Get all projects from the server
        response = stubMeta.GetProjects(empty_pb2.Empty())
        for project in response.projects:
            print(project.name + " " + project.uuid)
            if project.name == "testproject":
                target_proj_uuid = project.uuid

        if target_proj_uuid is None:
            # 4. create a project
            creation = projectCreation.ProjectCreation(
                name="testproject", mapFrameId="map"
            )
            projectCreated = stubMeta.CreateProject(creation)
            target_proj_uuid = projectCreated.uuid

    theTime = int(time.time())

    #####
    # A valid camera intrinsics UUID is needed here for succesful storage
    # of Images
    # Add new Camera Intrinsics with placeholder data

    ciuuid = str(uuid.uuid4())

    camin = cameraintrinsics.CameraIntrinsics()

    camin.header.stamp.seconds = 4
    camin.header.stamp.nanos = 3

    camin.header.frame_id = "camintrinsics"

    camin.header.uuid_project = target_proj_uuid
    camin.header.uuid_msgs = ciuuid

    camin.region_of_interest.x_offset = 2
    camin.region_of_interest.y_offset = 1
    camin.region_of_interest.height = 5
    camin.region_of_interest.width = 4
    camin.region_of_interest.do_rectify = 4

    camin.height = 5
    camin.width = 4

    camin.distortion_model = "plumb_bob"

    camin.distortion.extend(list(range(0, 3)))

    camin.intrinsic_matrix.extend([3, 4, 5, 10, 7, 8, 9, 10, 11])
    camin.rectification_matrix.extend([3, 4, 5, 6, 7, 8, 9, 10, 11])
    camin.projection_matrix.extend([3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])

    camin.binning_x = 6
    camin.binning_y = 7

    camin.maximum_viewing_distance = 5

    stubCI.TransferCameraIntrinsics(camin)

    # 5. Create ten images
    # for debugging and testing this example add all sent images to a list
    sent_images_list: List[Tuple[str, image.Image]] = []

    for n in range(10):
        theImage = image.Image()

        rgb = []
        lim = 256  # 256 x 256 pixels
        for i in range(lim):
            for j in range(lim):
                x = float(i) / lim
                y = float(j) / lim
                z = float(j) / lim
                r = np.ubyte((x * 255.0 + n) % 255)
                g = np.ubyte((y * 255.0 + n) % 255)
                b = np.ubyte((z * 255.0 + n) % 255)
                rgb.append(r)
                rgb.append(g)
                rgb.append(b)

        # Add image meta-data
        theImage.header.frame_id = "camera"
        theImage.header.stamp.seconds = theTime + n
        theImage.header.stamp.nanos = 0
        theImage.header.uuid_project = target_proj_uuid
        theImage.height = lim
        theImage.width = lim
        theImage.encoding = "rgb8"
        theImage.step = 3 * lim
        theImage.uuid_camera_intrinsics = ciuuid

        # Add image data
        theImage.data = bytes(rgb)

        # 5. create label
        labelStr = ["label1", "label2"]
        for iCategory in ["category A", "category B"]:
            labelsCategory = label_category_pb2.LabelCategory()
            labelsCategory.category = iCategory
            labelsCategory.datumaroJson = "a very valid datumaro json"

            for labelAct in labelStr:
                label = label_pb2.Label()
                label.label = labelAct
                label.labelIdDatumaro = 1
                label.instanceUuid = str(uuid.uuid4())
                label.instanceIdDatumaro = 22
                labelsCategory.labels.append(label)

            theImage.labels.append(labelsCategory)

        # 8. Send image to the server
        uuidImg = stub.TransferImage(theImage)

        # also add them to the list
        sent_images_list.append((uuidImg.message, theImage))

    # a list for debugging and testing, if interpolated coordinates
    # according to timestamp of the images and tfs are correct
    tf_times: List[int] = []

    # 8. Add coordinate transformations and send them to the server
    theTf = tf.TransformStamped()
    theTf.header.frame_id = "map"
    theTf.header.stamp.seconds = theTime
    theTf.header.uuid_project = target_proj_uuid
    theTf.child_frame_id = "camera"
    theTf.transform.translation.x = 1
    theTf.transform.translation.y = 2
    theTf.transform.translation.z = 3
    theTf.transform.rotation.x = 0
    theTf.transform.rotation.y = 0
    theTf.transform.rotation.z = 0
    theTf.transform.rotation.w = 1
    stubTf.TransferTransformStamped(theTf)
    tf_times.append(theTf.header.stamp.seconds)

    theTf.header.stamp.seconds = theTime + 10
    theTf.transform.translation.x = 100
    theTf.transform.translation.y = 200
    theTf.transform.translation.z = 300
    stubTf.TransferTransformStamped(theTf)
    tf_times.append(theTf.header.stamp.seconds)

    # return sent data
    return sent_images_list, tf_times, camin


if __name__ == "__main__":
    sent_image_ls_data, _, _ = send_labeled_images()
    camera_intrinsics_allimgs = {
        intrins_uuid[1].uuid_camera_intrinsics
        for intrins_uuid in sent_image_ls_data
    }

    # print statement to seperate the messages of the function
    print()
    print(
        f"camera intrinsics will be saved against the uuid(s): "
        f"{camera_intrinsics_allimgs}"
    )
    img_uuids = [img[0] for img in sent_image_ls_data]
    for i, img_uuid in enumerate(img_uuids):
        print(f"the uuid of the sent image number {i} is: {img_uuid}")

Output:

testproject 842de425-2d50-4adf-8aa3-6df257a7c76c
testproject ff739be8-cf0c-4657-bff0-f66f3e7f578d

camera intrinsics will be saved against the uuid(s): {'909c8439-36b6-44ff-9561-f7921de5d8e8'}
the uuid of the sent image number 0 is: 261a7b2c-297b-42db-93da-770e5f7f53cf
the uuid of the sent image number 1 is: feddb62d-fdfa-494a-8e4a-e1b2209134a6
the uuid of the sent image number 2 is: 6956ea01-b834-461f-85cf-5621e04f02fb
the uuid of the sent image number 3 is: 111e62dd-9aba-4fde-ae3d-ed3e3b6041f8
the uuid of the sent image number 4 is: 67138174-08a9-4943-848a-736bddd755b9
the uuid of the sent image number 5 is: 5e139007-9eea-4e71-9c5a-44f08c93027e
the uuid of the sent image number 6 is: c10eff49-4412-425d-99ef-d8b3f95c3806
the uuid of the sent image number 7 is: 708be3d8-7ddb-4ef5-9b00-37021b7b83ff
the uuid of the sent image number 8 is: 4ce47c5c-60d2-4f78-810f-ac730136f462
the uuid of the sent image number 9 is: 6c9ca46b-84fb-40d2-8523-2ad07284a43c

Query images

Now we will query the previously send images with some criteria. Possible query parameters are:

2D Polygon (spatial query)

Spatial queries in SEEREP are performed using a 2D polygon. This polygon should be simple (no more than 2 vertices on the same edge) and convex (no edges curving inward). This 2D polygon lies on a interval on the z-axis defined through a z-coordinate point and a height value. Queries are performed by forming an encompassing axis aligned bounding box from the polygon. This can lead to an AABB larger than the polygon and poses the potential problem of returning results to the user which are not fully inside the query polygon. That problem is resolved by providing a boolean variable called fullyEncapsulated`. If false, resultant polygons, which are partially inside the query polygon are also returned.

A time interval (temporal query)

Temporal queries in SEEREP are performed using a time interval. When using image queries the stamp in the header of those images is used and when that time lies in the interval the image is returned as part of the response. The interval is closed.

Labels (semantic query)

ProjectUuids

Only performs the query in these included projects specified by their uuids.

WithoutData

If the pixel data of the image should not be returned in order to save bandwith.

InMapFrame

Whether the query is done in the map frame. If not, the provided polygon for the spatial query will be transformed from the geodesic coordinates of the project into the map frame beforehand.

Example code for querying images

Source: examples/python/gRPC/images/gRPC_fb_queryImage.py

#!/usr/bin/env python3
# NOTE: This file is referenced in the following mkdocs files:
#   images.md
# Any changes done in here will be reflected in there
import sys
from typing import List

import flatbuffers
from grpc import Channel
from seerep.fb import Image
from seerep.fb import image_service_grpc_fb as imageService
from seerep.util.common import get_gRPC_channel
from seerep.util.fb_helper import (
    create_label,
    create_label_category,
    createPoint2d,
    createPolygon2D,
    createQuery,
    createTimeInterval,
    createTimeStamp,
    getOrCreateProject,
)


def query_images_raw(
    fbb: flatbuffers.Builder,
    grpc_channel: Channel,
    target_proj_uuid: str,
    *args,
    **kwargs,
) -> List[bytearray]:
    """
    Query images from SEEREP and return the raw FlatBuffer buffers.

    Args:
        grpc_channel (Channel): gRPC channel to communicate with the server.
        target_proj_uuid (str): UUID of the target project.
        *args: Variable args to pass to the query.
        **kwargs: Keyword args to pass to the query.

    Returns:
        List[bytearray]: A list buffers of images matching the query.
    """
    image_service_stub = imageService.ImageServiceStub(grpc_channel)

    project_uuid_buffer = fbb.CreateString(target_proj_uuid)

    query = createQuery(
        fbb, *args, projectUuids=[project_uuid_buffer], **kwargs
    )

    fbb.Finish(query)
    query_buf = fbb.Output()

    return image_service_stub.GetImage(bytes(query_buf))


def query_images(
    fbb: flatbuffers.Builder,
    grpc_channel: Channel,
    target_proj_uuid: str,
    *args,
    **kwargs,
) -> List[Image.Image]:
    """
    Query images from SEEREP.

    Args:
        grpc_channel (Channel): gRPC channel to communicate with the server.
        target_proj_uuid (str): The UUID of the target project.
        *args: Variable args to pass to the query.
        **kwargs: Keyword args to pass to the query.

    Returns:
        List[Image.Image]: A list of images matching the query.
    """
    return [
        Image.Image.GetRootAs(img)
        for img in query_images_raw(
            fbb, grpc_channel, target_proj_uuid, *args, **kwargs
        )
    ]


if __name__ == "__main__":
    fbb = flatbuffers.Builder()
    grpc_channel = get_gRPC_channel()
    project_uuid = getOrCreateProject(fbb, grpc_channel, "testproject")

    # create the data for the query
    scale = 100
    vertices = [
        createPoint2d(fbb, x * scale, y * scale)
        for x, y in [(-1.0, -1.0), (-1.0, 1.0), (1.0, 1.0), (1.0, -1.0)]
    ]

    polygon_2d = createPolygon2D(fbb, 700, -100, vertices)

    time_min = createTimeStamp(fbb, 1610549273, 0)
    time_max = createTimeStamp(fbb, 1938549273, 0)
    time_interval = createTimeInterval(fbb, time_min, time_max)

    project_uuids = [fbb.CreateString(project_uuid)]

    labels = [
        create_label(builder=fbb, label=label_str, label_id=i)
        for i, label_str in enumerate(["label1", "label2"])
    ]

    labelsCategory = [
        create_label_category(
            builder=fbb,
            labels=labels,
            datumaro_json="a very valid datumaro json",
            category="category A",
        )
    ]

    matching_images = query_images(
        fbb,
        grpc_channel,
        project_uuid,
        polygon2d=polygon_2d,
        labels=labelsCategory,
        withoutData=True,
        fullyEncapsulated=False,
        inMapFrame=True,
    )

    if matching_images is None or len(matching_images) == 0:
        print("No images matched the query.")
        sys.exit()

    print(f"Number of images matching the query: {len(matching_images)}")

    for img in matching_images:
        print(
            "------------------------------------------------------------------"
        )
        print(f"Msg UUID: {img.Header().UuidMsgs().decode('utf-8')}")
        print(f"Number of labels: {img.LabelsLength()}")
        if img.LabelsLength() > 0:
            print(
                "First label: "
                + img.Labels(0).Labels(0).Label().decode("utf-8")
            )
    print("------------------------------------------------------------------")

After sending the images, executing the query script results in the following output:

count of images: 10
------------------------------------------------------------------
uuidmsg: 111e62dd-9aba-4fde-ae3d-ed3e3b6041f8
count of labels: 2
first label: label1
------------------------------------------------------------------
uuidmsg: 261a7b2c-297b-42db-93da-770e5f7f53cf
count of labels: 2
first label: label1
------------------------------------------------------------------
uuidmsg: 4ce47c5c-60d2-4f78-810f-ac730136f462
count of labels: 2
first label: label1
------------------------------------------------------------------
uuidmsg: 5e139007-9eea-4e71-9c5a-44f08c93027e
count of labels: 2
first label: label1
------------------------------------------------------------------
uuidmsg: 67138174-08a9-4943-848a-736bddd755b9
count of labels: 2
first label: label1
------------------------------------------------------------------
uuidmsg: 6956ea01-b834-461f-85cf-5621e04f02fb
count of labels: 2
first label: label1
------------------------------------------------------------------
uuidmsg: 6c9ca46b-84fb-40d2-8523-2ad07284a43c
count of labels: 2
first label: label1
------------------------------------------------------------------
uuidmsg: 708be3d8-7ddb-4ef5-9b00-37021b7b83ff
count of labels: 2
first label: label1
------------------------------------------------------------------
uuidmsg: c10eff49-4412-425d-99ef-d8b3f95c3806
count of labels: 2
first label: label1
------------------------------------------------------------------
uuidmsg: feddb62d-fdfa-494a-8e4a-e1b2209134a6
count of labels: 2
first label: label1
------------------------------------------------------------------

Source: examples/python/gRPC/images/gRPC_pb_queryImage.py

#!/usr/bin/env python3
# NOTE: This file is referenced in the following mkdocs files:
#   images.md
# Any changes done in here will be reflected in there
import sys
from typing import List, Optional

from google.protobuf import empty_pb2
from grpc import Channel
from seerep.pb import image_pb2 as image
from seerep.pb import image_service_pb2_grpc as imageService
from seerep.pb import label_category_pb2, label_pb2
from seerep.pb import meta_operations_pb2_grpc as metaOperations
from seerep.pb import point2d_pb2 as point2d
from seerep.pb import query_pb2 as query
from seerep.util.common import get_gRPC_channel


def query_images(
    target_project_uuid: Optional[str] = None,
    grpc_channel: Channel = get_gRPC_channel(),
) -> List[image.Image]:
    # 1. Get gRPC service objects
    stub = imageService.ImageServiceStub(grpc_channel)
    stubMeta = metaOperations.MetaOperationsStub(grpc_channel)

    # 3. Check if we have an existing test project, if not, we stop here
    if target_project_uuid is None:
        # 2. Get all projects from the server
        response = stubMeta.GetProjects(empty_pb2.Empty())
        for project in response.projects:
            print(project.name + " " + project.uuid + "\n")
            if project.name == "testproject":
                target_project_uuid = project.uuid

        if target_project_uuid is None:
            print("""
                No project with name 'testproject' found! Execute
                gRPC_pb_sendLabeledImage.py beforehand!
            """)
            sys.exit()

    # 4. Create a query with parameters
    theQuery = query.Query()
    theQuery.projectuuid.append(target_project_uuid)

    theQuery.polygon.z = -200
    theQuery.polygon.height = 800

    theQuery.inMapFrame = True

    scale = 150
    vertices = [
        point2d.Point2D(x=x, y=y)
        for x, y in [
            (-scale, -scale),
            (-scale, scale),
            (scale, scale),
            (scale, -scale),
        ]
    ]
    theQuery.polygon.vertices.extend(vertices)

    # since epoche
    theQuery.timeinterval.time_min.seconds = 1638549273
    theQuery.timeinterval.time_min.nanos = 0
    theQuery.timeinterval.time_max.seconds = 1938549273
    theQuery.timeinterval.time_max.nanos = 0

    # labels
    labelsCategory = label_category_pb2.LabelCategory()
    labelsCategory.category = "category A"
    label = label_pb2.Label()
    label.label = "label1"
    labelsCategory.labels.append(label)
    theQuery.labelCategory.append(labelsCategory)

    # theQuery.inMapFrame = True
    theQuery.fullyEncapsulated = False

    # 5. Query the server for images matching the query and return them
    return list(stub.GetImage(theQuery))


if __name__ == "__main__":
    queried_imgs = query_images()
    print(f"count of images {len(queried_imgs)}")
    for img in queried_imgs:
        print(
            f"uuidmsg: {img.header.uuid_msgs}"
            + "\n"
            + f"first label: {img.labels[0].labels[0].label}"
        )

After sending the images, executing the query script results in the following output:

count of images 8
uuidmsg: 111e62dd-9aba-4fde-ae3d-ed3e3b6041f8
first label: label1
uuidmsg: 261a7b2c-297b-42db-93da-770e5f7f53cf
first label: label1
uuidmsg: 5e139007-9eea-4e71-9c5a-44f08c93027e
first label: label1
uuidmsg: 67138174-08a9-4943-848a-736bddd755b9
first label: label1
uuidmsg: 6956ea01-b834-461f-85cf-5621e04f02fb
first label: label1
uuidmsg: 708be3d8-7ddb-4ef5-9b00-37021b7b83ff
first label: label1
uuidmsg: c10eff49-4412-425d-99ef-d8b3f95c3806
first label: label1
uuidmsg: feddb62d-fdfa-494a-8e4a-e1b2209134a6
first label: label1