Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance Improvement on Python API #621

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions python/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ protobuf
numpy
websockets
pytest
pillow
36 changes: 25 additions & 11 deletions python/test/test_builder.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import json
from easydict import EasyDict as edict

from xviz_avs.builder import XVIZBuilder, XVIZUIPrimitiveBuilder, XVIZTimeSeriesBuilder, XVIZVariableBuilder
import unittest
import numpy as np

from easydict import EasyDict as edict
from PIL import Image
from xviz_avs.builder import (XVIZBuilder, XVIZTimeSeriesBuilder,
XVIZUIPrimitiveBuilder, XVIZVariableBuilder)

PRIMARY_POSE_STREAM = '/vehicle_pose'

Expand Down Expand Up @@ -69,7 +72,8 @@ def setUp(self):
setup_pose(self.builder)

def test_image(self):
data = bytes(b'12345')
data = np.array([[1,2],[3,4]])
data = Image.fromarray(data.astype('u1')).convert('RGB')
self.builder.primitive('/camera/1')\
.image(data)

Expand All @@ -82,7 +86,12 @@ def test_image(self):
'/camera/1': {
'images': [
{
'data': 'MTIzNDU='
'width_px': 2,
'height_px': 2,
'data': b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x02\x00'\
b'\x00\x00\x02\x08\x02\x00\x00\x00\xfd\xd4\x9as\x00\x00' \
b'\x00\x16IDATx\x9ccdddddddabbbdd\x04\x00\x00\x9b\x00\x15'\
b'\x86\xe2,\xcc\x00\x00\x00\x00IEND\xaeB`\x82'
}
]
}
Expand Down Expand Up @@ -132,10 +141,10 @@ def setUp(self):
setup_pose(self.builder)

def test_null(self):
builder = XVIZUIPrimitiveBuilder(None, None)
builder = XVIZUIPrimitiveBuilder(None)
data = builder.stream('/test').get_data()

assert data is None
assert not data

def test_treetable_no_rows(self):
TEST_COLUMNS = [{'display_text': 'Name', 'type': 'STRING'}]
Expand Down Expand Up @@ -269,10 +278,10 @@ def setUp(self):
setup_pose(self.builder)

def test_null(self):
builder = XVIZTimeSeriesBuilder(None, None)
builder = XVIZTimeSeriesBuilder(None)
data = builder.stream('/test').get_data()

assert data is None
assert not data

def test_single_entry(self):
self.builder = XVIZBuilder()
Expand Down Expand Up @@ -530,10 +539,10 @@ def setUp(self):
setup_pose(self.builder)

def test_null(self):
builder = XVIZVariableBuilder(None, None)
builder = XVIZVariableBuilder(None)
data = builder.stream('/test').get_data()

assert data is None
assert not data

def test_int32s_variable(self):
self.builder.variable('/test_var').values([1, 2, 3])
Expand Down Expand Up @@ -666,3 +675,8 @@ def test_bools_variable(self):
}

assert data == expected

if __name__ == "__main__":
t = TestFutureInstanceBuilder()
t.setUp()
t.test_primitives()
41 changes: 20 additions & 21 deletions python/test/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,17 +227,17 @@ def test_glb_point_cloud_writer(self):

expected = b'glTF\x02\x00\x00\x00D\x03\x00\x00\xf8\x02\x00\x00JSON{"asset":{"version":"2"'\
b'},"buffers":[{"byteLength":48}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLeng'\
b'th":36},{"buffer":0,"byteOffset":36,"byteLength":12}],"accessors":[{"bufferView":0,'\
b'"type":"VEC3","componentType":5126,"count":3},{"bufferView":1,"type":"VEC4","compon'\
b'entType":5121,"count":3}],"images":[],"meshes":[],"extensions":{"AVS_xviz":{"type":'\
b'th":12},{"buffer":0,"byteOffset":12,"byteLength":36}],"accessors":[{"bufferView":0,'\
b'"type":"VEC4","componentType":5121,"count":3},{"bufferView":1,"type":"VEC3","compon'\
b'entType":5126,"count":3}],"images":[],"meshes":[],"extensions":{"AVS_xviz":{"type":'\
b'"xviz/state_update","data":{"update_type":"INCREMENTAL","updates":[{"timestamp":2.0'\
b'00000000001,"poses":{"/vehicle_pose":{"timestamp":2.000000000001,"map_origin":{"lon'\
b'gitude":4.4,"latitude":5.5,"altitude":6.6},"position":[44.0,55.0,66.0],"orientation'\
b'":[0.44,0.55,0.66]}},"primitives":{"/test_primitive":{"points":[{"points":"#/access'\
b'ors/0","colors":"#/accessors/1"}]}}}]}}},"extensionsUsed":["AVS_xviz"]} 0\x00\x00'\
b'\x00BIN\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80?\x00\x00'\
b'\x80?\x00\x00\x80?\x00\x00\x00@\x00\x00\x00@\x00\x00\x00@\xff\x00\x00\x80\x00\xff'\
b'\x00\x80\x00\x00\xff\x80'
b'":[0.44,0.55,0.66]}},"primitives":{"/test_primitive":{"points":[{"colors":"#/access'\
b'ors/0","points":"#/accessors/1"}]}}}]}}},"extensionsUsed":["AVS_xviz"]} 0\x00\x00' \
b'\x00BIN\x00\xff\x00\x00\x80\x00\xff\x00\x80\x00\x00\xff\x80\x00\x00\x00\x00\x00\x00'\
b'\x00\x00\x00\x00\x00\x00\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x00@\x00' \
b'\x00\x00@\x00\x00\x00@'

assert data == expected

Expand All @@ -257,7 +257,7 @@ def test_glb_polyline_writer(self):
b'ehicle_pose":{"timestamp":2.000000000001,"map_origin":{"longitude":4.4,"latitude":5'\
b'.5,"altitude":6.6},"position":[44.0,55.0,66.0],"orientation":[0.44,0.55,0.66]}},"pr'\
b'imitives":{"/test_primitive":{"polylines":[{"vertices":"#/accessors/0"}]}}}]}}},"ex'\
b'tensionsUsed":["AVS_xviz"]} \x18\x00\x00\x00BIN\x00\x00\x00\x80?\x00\x00\x80?\x00'\
b'tensionsUsed":["AVS_xviz"]} \x18\x00\x00\x00BIN\x00\x00\x00\x80?\x00\x00\x80?\x00' \
b'\x00\x80?\x00\x00\x00@\x00\x00\x00@\x00\x00\x00@'

assert data == expected
Expand All @@ -279,8 +279,8 @@ def test_glb_polygon_writer(self):
b'":5.5,"altitude":6.6},"position":[44.0,55.0,66.0],"orientation":[0.44,0.55,0.66]}},'\
b'"primitives":{"/test_primitive":{"polygons":[{"base":{"style":{"height":2.0}},"vert'\
b'ices":"#/accessors/0"}]}}}]}}},"extensionsUsed":["AVS_xviz"]} <\x00\x00\x00BIN\x00'\
b'\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x00@\x00\x00\x00@\x00\x00\x00@\x00'\
b'\x00@@\x00\x00@@\x00\x00@@\x00\x00\x80@\x00\x00\x80@\x00\x00\x80@\x00\x00\x80?\x00'\
b'\x00\x00\x80?\x00\x00\x80?\x00\x00\x80?\x00\x00\x00@\x00\x00\x00@\x00\x00\x00@\x00' \
b'\x00@@\x00\x00@@\x00\x00@@\x00\x00\x80@\x00\x00\x80@\x00\x00\x80@\x00\x00\x80?\x00' \
b'\x00\x80?\x00\x00\x80?'

assert data == expected
Expand All @@ -293,13 +293,12 @@ def test_protobuf_normal_writer(self):
writer.write_message(builder.get_message())
data = source.read()

expected = b'glTF\x02\x00\x00\x004\x02\x00\x00\x18\x02\x00\x00JSON{"asset":{"version":"2"'\
b'},"buffers":[{"byteLength":0}],"bufferViews":[],"accessors":[],"image":[],"meshes":'\
b'[],"extensions":{"AVS_xviz":{"type":"#xviz/state_update","data":{"update_type":"#IN'\
b'CREMENTAL","updates":[{"timestamp":2.000000000001,"poses":{"/vehicle_pose":{"timest'\
b'amp":2.000000000001,"map_origin":{"longitude":4.4,"latitude":5.5,"altitude":6.6},"p'\
b'osition":[44.0,55.0,66.0],"orientation":[0.44,0.55,0.66]}},"primitives":{"/test_pri'\
b'mitive":{"circles":[{"center":[0.0,0.0,0.0],"radius":2.0}]}}}]}}},"extensionsUsed":'\
b'["AVS_xviz"]}\x00\x00\x00\x00\x00\x00BIN\x00'

# XXX: assert data == expected
expected = b'PBE1\n\x11xviz/state_update\x12\xd1\x01\n\'type.googleapis.com/xviz.v2.State'\
b'Update\x12\xa5\x01\x08\x02\x12\xa0\x01\t\xcc\x08\x00\x00\x00\x00\x00@\x12k\n\r/vehi'\
b'cle_pose\x12Z\t\xcc\x08\x00\x00\x00\x00\x00@\x12\x1b\t\x9a\x99\x99\x99\x99\x99\x11@'\
b'\x11\x00\x00\x00\x00\x00\x00\x16@\x19ffffff\x1a@\x1a\x18\x00\x00\x00\x00\x00\x00F@' \
b'\x00\x00\x00\x00\x00\x80K@\x00\x00\x00\x00\x00\x80P@"\x18)\\\x8f\xc2\xf5(\xdc?\x9a' \
b'\x99\x99\x99\x99\x99\xe1?\x1f\x85\xebQ\xb8\x1e\xe5?\x1a(\n\x0f/test_primitive\x12' \
b'\x15"\x13\x12\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00@'

assert data == expected
2 changes: 1 addition & 1 deletion python/xviz_avs/builder/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
PRIMITIVE_TYPES,\
UIPRIMITIVE_TYPES
from .xviz_builder import XVIZBuilder
from .xviz_ui_builder import XVIZUIBuilder
from .ui_builder import XVIZUIBuilder

from .metadata import XVIZMetadataBuilder
from .pose import XVIZPoseBuilder
Expand Down
54 changes: 40 additions & 14 deletions python/xviz_avs/builder/base_builder.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import logging
from typing import Union
from typing import Union, NewType
from easydict import EasyDict as edict

from xviz_avs.message import XVIZMessage
from xviz_avs.v2.session_pb2 import Metadata, StreamMetadata
from xviz_avs.v2.style_pb2 import StyleStreamValue

# expose the constants
ANNOTATION_TYPES = StreamMetadata.AnnotationType
CATEGORY = StreamMetadata.Category
COORDINATE_TYPES = StreamMetadata.CoordinateType
SCALAR_TYPE = StreamMetadata.ScalarType
PRIMITIVE_TYPES = StreamMetadata.PrimitiveType
UIPRIMITIVE_TYPES = StreamMetadata.UIPrimitiveType

# for type hints
ANNOTATION_TYPES_T = NewType("ANNOTATION_TYPES", int)
CATEGORY_T = NewType("CATEGORY", int)
COORDINATE_TYPES_T = NewType("COORDINATE_TYPES", int)
SCALAR_TYPE_T = NewType("SCALAR_TYPE", int)
PRIMITIVE_TYPES_T = NewType("PRIMITIVE_TYPES", int)
UIPRIMITIVE_TYPES_T = NewType("UIPRIMITIVE_TYPES", int)

PRIMITIVE_STYLE_MAP = dict([
(PRIMITIVE_TYPES.CIRCLE, [
'opacity',
Expand Down Expand Up @@ -82,26 +91,26 @@ class XVIZBaseBuilder:
# Reference
[@xviz/builder/xviz-base-builder]/(https://github.com/uber/xviz/blob/master/modules/builder/src/builders/xviz-base-builder.js)
"""
def __init__(self, category, metadata: Union[Metadata, XVIZMessage], logger=None):
def __init__(self, category, metadata: Union[Metadata, XVIZMessage]):
self._stream_id = None
self._category = category
self._metadata = metadata.data if isinstance(metadata, XVIZMessage) else metadata
self._logger = logger or logging.getLogger("xviz")
self._metadata = metadata._data if isinstance(metadata, XVIZMessage) else metadata
self._logger = logging.getLogger("xviz")

def stream(self, stream_id):
def stream(self, stream_id: str):
if self._stream_id:
self._flush()
self._stream_id = stream_id
return self

@property
def stream_id(self):
def stream_id(self) -> str:
return self._stream_id
@property
def category(self):
def category(self) -> CATEGORY:
return self._category
@property
def metadata(self):
def metadata(self) -> XVIZMessage:
return self._metadata

def _flush(self):
Expand Down Expand Up @@ -149,22 +158,39 @@ def _validate(self):
import array
from xviz_avs.v2.style_pb2 import StyleObjectValue, StyleStreamValue

def build_object_style(style):
def build_color(color: Union[list, tuple, bytes, str]) -> bytes:
'''
Convert css style color string to bytes
'''
if isinstance(color, (list, tuple, bytes)):
return bytes(color)
elif isinstance(color, str):
color = color.lstrip('#')
if len(color) in [3, 4]: # '#rgb' or '#rgba' style
return bytes([16 * int(c, 16) for c in color])
elif len(color) in [6, 8]: # '#rrggbb' or '#rrggbbaa' style
return bytes([int(color[i*2:i*2+2], 16)
for i in range(len(color) // 2)])
else:
raise ValueError("Unrecognized color string!")
raise ValueError("Unrecognized color object!")

def build_object_style(style: dict) -> StyleObjectValue:
'''
Create StyleObjectValue from dictionary. It basically deal with list of bytes.
'''
if 'fill_color' in style.keys():
style['fill_color'] = bytes(style['fill_color'])
style['fill_color'] = build_color(style['fill_color'])
if 'stroke_color' in style.keys():
style['stroke_color'] = bytes(style['stroke_color'])
style['stroke_color'] = build_color(style['stroke_color'])
return StyleObjectValue(**style)

def build_stream_style(style):
def build_stream_style(style: dict) -> StyleStreamValue:
'''
Create StyleStreamValue from dictionary. It basically deal with list of bytes.
'''
if 'fill_color' in style.keys():
style['fill_color'] = bytes(style['fill_color'])
style['fill_color'] = build_color(style['fill_color'])
if 'stroke_color' in style.keys():
style['stroke_color'] = bytes(style['stroke_color'])
style['stroke_color'] = build_color(style['stroke_color'])
return StyleStreamValue(**style)
48 changes: 26 additions & 22 deletions python/xviz_avs/builder/future_instance.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
from typing import Tuple
from collections import defaultdict

from xviz_avs.builder.base_builder import CATEGORY, PRIMITIVE_TYPES
from xviz_avs.builder.primitive import XVIZPrimitiveBuilder
from xviz_avs.v2.core_pb2 import FutureInstances, PrimitiveState


class XVIZFutureInstanceBuilder(XVIZPrimitiveBuilder):
def __init__(self, metadata, logger=None):
super().__init__(metadata, logger)
def __init__(self, metadata):
super().__init__(metadata)
self._category = CATEGORY.FUTURE_INSTANCE # Override category

self.reset()
self._futures = {}
self._futures_buffer = defaultdict(list)

# Store entries in this list as (timestamp, type, primitive)
# which we convert to Protobuf messages upon get_data()
self._futures_list = {}
self._futures_list = defaultdict(list)

def reset(self):
super().reset()
self._ts = None

def timestamp(self, timestamp):
def timestamp(self, timestamp: float) -> 'XVIZFutureInstanceBuilder':
self._ts = timestamp
return self

Expand All @@ -39,7 +43,8 @@ def _get_primitives_type(self, primitives, primitive_type):
elif primitive_type == PRIMITIVE_TYPES.TEXT:
return primitives.texts
else:
raise ValueError("FutureInstance type '{0}' is not recognized".format(primitive_type))
self._logger.error("FutureInstance type '%s' is not recognized", str(primitive_type))
return []

def _flush_futures_list(self):
# Since you cannot insert into a repeated message field
Expand All @@ -54,36 +59,35 @@ def _flush_futures_list(self):
entries.sort(key=lambda e: e[0])
last_ts = None

for entry in entries:
if last_ts is None or entry[0] != last_ts:
for ets, etype, eprimitive, ebuffer in entries:
if last_ts is None or ets != last_ts:
# Adding a new timestamp entry to the arrays
last_ts = entry[0]
futures.timestamps.append(entry[0])
last_ts = ets
futures.timestamps.append(ets)
future_prim = futures.primitives.add()
future_prim_type = self._get_primitives_type(future_prim, entry[1])
future_prim_type.append(entry[2])

else:
index = len(futures.timestamps)-1
future_prim = futures.primitives[index]
future_prim_type = self._get_primitives_type(future_prim, entry[1])
future_prim_type.append(entry[2])

future_prim_type = self._get_primitives_type(future_prim, etype)
future_prim_type.append(eprimitive)

if ebuffer is not None:
self._futures_buffer[(stream, ets)].append(ebuffer)


def _flush(self):
if self._stream_id not in self._futures_list:
self._futures_list[self._stream_id] = []
primitive = self._format_primitive(len(self._futures_list[self._stream_id]))

primitive = self._format_primitive()
self._futures_list[self._stream_id].append((self._ts, self._type, primitive))
self._futures_list[self._stream_id].append((self._ts, self._type, primitive, self._vertices_buffer))

self.reset()

def get_data(self):
def get_data(self) -> Tuple[FutureInstances, dict]:
if self._type:
self._flush()

self._flush_futures_list()

if not self._futures:
return None

return self._futures
return self._futures, self._futures_buffer
6 changes: 3 additions & 3 deletions python/xviz_avs/builder/link.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
from xviz_avs.v2.core_pb2 import Link

class XVIZLinkBuilder(XVIZBaseBuilder):
def __init__(self, metadata, logger=None):
super().__init__(None, metadata, logger)
def __init__(self, metadata):
super().__init__(None, metadata)
self._links = None
self._target_stream = None

def parent(self, target_stream):
def parent(self, target_stream: str):
self._target_stream = target_stream

def _flush(self):
Expand Down
Loading