bosdyn-client 4.1.0__py3-none-any.whl → 5.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
bosdyn/client/__init__.py CHANGED
@@ -22,3 +22,4 @@ from .exceptions import (ClientCancelledOperationError, CustomParamError, Error,
22
22
  from .robot import Robot
23
23
  from .sdk import BOSDYN_RESOURCE_ROOT, Sdk, create_standard_sdk
24
24
 
25
+
@@ -4,6 +4,8 @@
4
4
  # is subject to the terms and conditions of the Boston Dynamics Software
5
5
  # Development Kit License (20191101-BDSDK-SL).
6
6
 
7
+ # Boston Dynamics, Inc. Confidential Information.
8
+ # Copyright 2025. All Rights Reserved.
7
9
  """Command-line utility code for interacting with robot services."""
8
10
 
9
11
  from __future__ import division
@@ -705,13 +707,16 @@ class LogStatusCommands(Subcommands):
705
707
  subparsers: List of argument parsers.
706
708
  command_dict: Dictionary of command names which take parsed options.
707
709
  """
708
- super(LogStatusCommands, self).__init__(subparsers, command_dict, [
709
- GetLogCommand,
710
- GetActiveLogStatusesCommand,
711
- ExperimentLogCommand,
712
- StartRetroLogCommand,
713
- TerminateLogCommand,
714
- ])
710
+ super(LogStatusCommands, self).__init__(
711
+ subparsers,
712
+ command_dict,
713
+ [
714
+ GetLogCommand,
715
+ GetActiveLogStatusesCommand,
716
+ ExperimentLogCommand,
717
+ StartRetroLogCommand,
718
+ TerminateLogCommand,
719
+ ])
715
720
 
716
721
 
717
722
  class GetLogCommand(Command):
@@ -947,6 +952,8 @@ class TerminateLogCommand(Command):
947
952
  return True
948
953
 
949
954
 
955
+
956
+
950
957
  class RobotIdCommand(Command):
951
958
  """Show robot-id."""
952
959
 
@@ -2646,6 +2653,7 @@ def main(args=None):
2646
2653
  parser = argparse.ArgumentParser(prog='bosdyn.client', description=main.__doc__)
2647
2654
  add_common_arguments(parser, credentials_no_warn=True)
2648
2655
 
2656
+
2649
2657
  command_dict = {} # command name to fn which takes parsed options
2650
2658
  subparsers = parser.add_subparsers(title='commands', dest='command')
2651
2659
 
@@ -167,7 +167,7 @@ def clean_filename(filename):
167
167
  filename(string): Original filename to clean.
168
168
 
169
169
  Returns:
170
- Valid filename with removed characters :*?<>|
170
+ Valid filename with removed characters :?<>|*
171
171
  """
172
172
 
173
173
  return "".join(i for i in filename if i not in ":*?<>|")
@@ -338,8 +338,8 @@ class DataAcquisitionStoreClient(BaseClient):
338
338
  def query_max_capture_id(self, **kwargs):
339
339
  """Query max capture id from the robot.
340
340
  Returns:
341
- QueryMaxCaptureIdResult, which has a max_capture_id uint64, corresponding to the
342
- greatest capture id on the robot. Used for skiping DAQ synchronization
341
+ QueryMaxCaptureIdResult, which has a max_capture_id uint64, corresponding to the
342
+ greatest capture id on the robot. Used for skipping DAQ synchronization
343
343
  on connect.
344
344
  """
345
345
  request = data_acquisition_store.QueryMaxCaptureIdRequest()
@@ -14,7 +14,7 @@ from google.protobuf.any_pb2 import Any
14
14
  from google.protobuf.timestamp_pb2 import Timestamp
15
15
 
16
16
  from bosdyn.api.gps.gps_pb2 import GpsDataPoint
17
- from bosdyn.util import RobotTimeConverter, now_timestamp, seconds_to_timestamp
17
+ from bosdyn.util import RobotTimeConverter, now_sec, seconds_to_timestamp
18
18
 
19
19
 
20
20
  has_warned_no_zda = False
@@ -36,6 +36,7 @@ class NMEAParser(object):
36
36
  # If your GPS outputs data at 20 Hz, this constant must be less than 0.050 seconds.
37
37
  self.grouping_timeout = 0.025
38
38
  self.last_failed_read_log_time = None
39
+ self.last_gga = None
39
40
 
40
41
  def nmea_message_group_to_gps_data_point(self, nmea_messages: List[Tuple[str, str, int]],
41
42
  time_converter: RobotTimeConverter):
@@ -53,12 +54,13 @@ class NMEAParser(object):
53
54
  data, raw_nmea_msg, client_timestamp = nmea_message_list
54
55
 
55
56
  if data.sentence_type == 'GGA':
57
+ self.last_gga = raw_nmea_msg
56
58
  if data.latitude is not None and data.longitude is not None and data.altitude is not None:
57
59
  data_point.llh.latitude = data.latitude
58
60
  data_point.llh.longitude = data.longitude
59
61
  data_point.llh.height = data.altitude
60
62
 
61
- if data.num_sats is not None and int(data.num_sats) > 0:
63
+ if data.num_sats is not None and data.num_sats != '' and int(data.num_sats) > 0:
62
64
  for _ in range(int(data.num_sats)):
63
65
  sat = data_point.satellites.add()
64
66
 
@@ -86,7 +88,12 @@ class NMEAParser(object):
86
88
  data_point.accuracy.vertical = data.std_dev_altitude
87
89
 
88
90
  elif data.sentence_type == 'ZDA':
89
- gps_timestamp = data.datetime
91
+ try:
92
+ gps_timestamp = data.datetime
93
+ except:
94
+ self.logger.exception("Failed to extract datetime from ZDA message.")
95
+ continue
96
+
90
97
  # Protobuf timestamp does not use timezone aware timestamps.
91
98
  gps_timestamp_no_tz = gps_timestamp.replace(tzinfo=None)
92
99
  data_point.timestamp_gps.FromDatetime(gps_timestamp_no_tz)
@@ -111,7 +118,7 @@ class NMEAParser(object):
111
118
 
112
119
  def parse(self, new_data: str, time_converter: RobotTimeConverter,
113
120
  check: bool = True) -> List[GpsDataPoint]:
114
- timestamp = time.time() # Client timestamp when received.
121
+ timestamp = now_sec() # Client timestamp when received.
115
122
  self.data = self.data + new_data
116
123
 
117
124
  if len(self.data) == 0:
@@ -143,14 +150,14 @@ class NMEAParser(object):
143
150
  except Exception as e:
144
151
  # Parsing error, log and skip.
145
152
  # Throttle the logs.
146
- now = time.time()
153
+ now = now_sec()
147
154
  if self.last_failed_read_log_time is None or (
148
155
  now - self.last_failed_read_log_time) > self.LOG_THROTTLE_TIME:
149
156
  self.logger.exception(f"Failed to parse {stripped}. Is it NMEA?")
150
157
  self.last_failed_read_log_time = now
151
158
  continue
152
159
 
153
- # if the message does not contain a timestamp attribute, abandon the rest of the logic
160
+ # If the message does not contain a timestamp attribute, abandon the rest of the logic
154
161
  # and go to the beginning of the loop
155
162
  if not hasattr(nmea_msg, 'timestamp'):
156
163
  continue
@@ -193,3 +200,6 @@ class NMEAParser(object):
193
200
  break
194
201
 
195
202
  return [self.nmea_message_group_to_gps_data_point(x, time_converter) for x in found_subsets]
203
+
204
+ def get_latest_gga(self):
205
+ return self.last_gga
@@ -17,6 +17,7 @@ from bosdyn.api.gps.gps_pb2 import GpsDataPoint, GpsDevice
17
17
  from bosdyn.client.exceptions import ProxyConnectionError
18
18
  from bosdyn.client.gps.aggregator_client import AggregatorClient
19
19
  from bosdyn.client.gps.NMEAParser import NMEAParser
20
+ from bosdyn.client.gps.ntrip_client import NtripClient, NtripClientParams
20
21
  from bosdyn.client.robot import UnregisteredServiceNameError
21
22
  from bosdyn.util import RobotTimeConverter, duration_to_seconds
22
23
 
@@ -70,6 +71,9 @@ class NMEAStreamReader(object):
70
71
 
71
72
  return new_points
72
73
 
74
+ def get_latest_gga(self):
75
+ return self.parser.get_latest_gga()
76
+
73
77
 
74
78
  class GpsListener:
75
79
 
@@ -77,10 +81,22 @@ class GpsListener:
77
81
  self.logger = logger
78
82
  self.robot = robot
79
83
  self.time_converter = time_converter
84
+ self.stream = stream
80
85
  self.reader = NMEAStreamReader(logger, stream, body_tform_gps, verbose)
81
86
  self.gps_device = GpsDevice()
82
87
  self.gps_device.name = name
83
88
  self.aggregator_client = None
89
+ self.ntrip_client = None
90
+
91
+ def run_ntrip_client(self, ntrip_params: NtripClientParams):
92
+ self.ntrip_client = NtripClient(self.stream, ntrip_params, self.logger)
93
+ self.ntrip_client.start_stream()
94
+
95
+ def stop_ntrip_client(self):
96
+ if self.ntrip_client is None:
97
+ return
98
+ self.ntrip_client.stop_stream()
99
+ self.ntrip_client = None
84
100
 
85
101
  def run(self):
86
102
  # It is possible for a payload to come up faster than the service. Loop a few times
@@ -118,7 +134,7 @@ class GpsListener:
118
134
  agg_future = None
119
135
 
120
136
  # Attach and run until a SIGINT is received.
121
- self.logger.info('Looping')
137
+ self.logger.info('Listening for GPS data.')
122
138
  try:
123
139
  while True:
124
140
  try:
@@ -156,5 +172,19 @@ class GpsListener:
156
172
  else:
157
173
  time_passed_since_last_rpc = time.time() - timestamp_of_last_rpc
158
174
 
175
+ # If we are running an NTRIP client, pass it the latest GGA message.
176
+ if self.ntrip_client is not None:
177
+ # If the NTRIP Client's stream has been closed, restart it.
178
+ if not self.ntrip_client.is_streaming():
179
+ self.logger.info("Restarting NTRIP Client!")
180
+ self.ntrip_client.start_stream()
181
+
182
+ latest_gga = self.reader.get_latest_gga()
183
+ if latest_gga is not None:
184
+ self.ntrip_client.handle_nmea_gga(latest_gga)
185
+
159
186
  except KeyboardInterrupt:
160
187
  print() # Get past the ^C in the console output
188
+
189
+ # Just in case there is an NTRIP client still running, stop it here.
190
+ self.stop_ntrip_client()
@@ -0,0 +1,240 @@
1
+ # Copyright (c) 2023 Boston Dynamics, Inc. All rights reserved.
2
+ #
3
+ # Downloading, reproducing, distributing or otherwise using the SDK Software
4
+ # is subject to the terms and conditions of the Boston Dynamics Software
5
+ # Development Kit License (20191101-BDSDK-SL).
6
+
7
+ import base64
8
+ import socket
9
+ import ssl
10
+ import time
11
+ from threading import Thread
12
+
13
+ SERVER_RECONNECT_DELAY = 60 # seconds of delay before retry after server error
14
+ SOCKET_TIMEOUT = 10 # socket timeout in seconds
15
+ SOCKET_MAX_RECV_TIMEOUTS = 12 # number of timeouts before reconnecting
16
+
17
+ DEFAULT_NTRIP_SERVER = ""
18
+ DEFAULT_NTRIP_PORT = 2101
19
+ DEFAULT_NTRIP_TLS_PORT = 2102
20
+
21
+
22
+ class NtripClientParams:
23
+ """
24
+ Class for storing parameters for connecting an NTRIP client to an NTRIP server.
25
+ """
26
+
27
+ def __init__(self, server=DEFAULT_NTRIP_SERVER, port=DEFAULT_NTRIP_PORT, user="", password="",
28
+ mountpoint="", tls=False):
29
+ """
30
+ Constructor.
31
+ """
32
+ self.server = server
33
+ self.port = port
34
+ self.user = user
35
+ self.password = password
36
+ self.mountpoint = mountpoint
37
+ self.tls = tls
38
+
39
+
40
+ class NtripClient:
41
+ """
42
+ Client used to connect to an NTRIP server to download GPS corrections. These corrections are
43
+ then forwarded on to the GPS device using the given stream.
44
+ """
45
+
46
+ def __init__(self, device, params: NtripClientParams, logger):
47
+ """
48
+ Constructor.
49
+ """
50
+ self.device = device
51
+ self.host = params.server
52
+ self.port = params.port
53
+ self.user = params.user
54
+ self.password = params.password
55
+ self.mountpoint = params.mountpoint
56
+ self.tls = params.tls
57
+
58
+ self.thread = None
59
+ self.streaming = False
60
+ self.sock = None
61
+
62
+ self.logger = logger
63
+
64
+ def make_request(self):
65
+ """
66
+ Make a connection request to an NTRIP server.
67
+ """
68
+ auth_str = base64.b64encode(f"{self.user}:{self.password}".encode()).decode()
69
+ request = (f"GET /{self.mountpoint} HTTP/1.1\r\n"
70
+ f"Host: {self.host}:{self.port}\r\n"
71
+ "User-Agent: NTRIP Python Client\r\n"
72
+ "Accept: */*\r\n"
73
+ f"Authorization: Basic {auth_str}\r\n"
74
+ "Connection: close\r\n"
75
+ "\r\n")
76
+ return request.encode()
77
+
78
+ def start_stream(self):
79
+ """
80
+ Start streaming data from an NTRIP server to a GPS receiver.
81
+ """
82
+ if self.streaming:
83
+ self.stop_stream()
84
+ self.thread = Thread(target=self.stream_data_worker, daemon=True)
85
+ self.thread.start()
86
+
87
+ def stop_stream(self):
88
+ """
89
+ Stop streaming NTRIP data.
90
+ """
91
+ self.streaming = False
92
+ if self.thread:
93
+ self.thread.join()
94
+ self.thread = None
95
+
96
+ def is_streaming(self):
97
+ """
98
+ Determine if we are streaming NTRIP data.
99
+ """
100
+ return self.streaming
101
+
102
+ def send_gga(self, gga):
103
+ """
104
+ Given a GPGGA message, send it to the NTRIP server. This helps the NTRIP server send
105
+ corrections that are applicable to the area in which the receiver is operating.
106
+ """
107
+ if self.sock:
108
+ try:
109
+ self.sock.send(gga.encode())
110
+ except OSError:
111
+ self.logger.warning("Error sending GGA")
112
+ return False
113
+ return True
114
+ return False
115
+
116
+ def create_icy_session(self):
117
+ """
118
+ NTRIP Rev1 uses Shoutcast (ICY). Create an ICY session to stream RTCM data.
119
+ """
120
+ try:
121
+ # Create a socket connection to the host and port.
122
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
123
+ if self.tls:
124
+ self.logger.info("Using secure connection")
125
+ context = ssl.create_default_context()
126
+ sock = context.wrap_socket(sock, server_hostname=self.host)
127
+ sock.settimeout(SOCKET_TIMEOUT)
128
+ self.logger.info("Connecting to NTRIP Server.")
129
+ sock.connect((self.host, self.port))
130
+ self.logger.info("Connected to NTRIP Server.")
131
+
132
+ # Send the request.
133
+ self.logger.info("Sending NTRIP Request.")
134
+ sock.send(self.make_request())
135
+ # Read a chunk of data, expecting to get the status line and headers.
136
+ response = sock.recv(1024)
137
+
138
+ response_lines = response.split(b"\r\n")
139
+ if len(response_lines) < 2:
140
+ self.logger.error("Invalid response from server: %s", response)
141
+ return False
142
+ status = response_lines[0].decode().split(" ")
143
+ self.logger.info(response_lines[0].decode())
144
+ if status[1] != "200":
145
+ self.logger.error("HTTP Error: %s, retrying in %d seconds",
146
+ response_lines[0].decode(), SERVER_RECONNECT_DELAY)
147
+ sock.close()
148
+ time.sleep(SERVER_RECONNECT_DELAY)
149
+ return False
150
+ self.logger.info("NTRIP Request Response received.")
151
+ for line in range(1, len(response_lines)):
152
+ if response_lines[line] == b"":
153
+ # Empty line, end of headers.
154
+ # The rest of the response contains data.
155
+ response = b"\r\n".join(response_lines[line + 1:])
156
+ break
157
+ self.logger.info(response_lines[line])
158
+
159
+ if response:
160
+ self.handle_ntrip_data(response)
161
+
162
+ # Make the socket available for sending GGA.
163
+ self.sock = sock
164
+ return True
165
+ except (socket.herror, socket.gaierror):
166
+ self.logger.warning("Error connecting to server %s:%d", self.host, self.port)
167
+ return False
168
+ except (TimeoutError, socket.timeout):
169
+ self.logger.warning("Connection timeout")
170
+ return False
171
+
172
+ def stream_data(self):
173
+ """
174
+ Stream NTRIP data from a connected server and send it to a GPS receiver.
175
+ """
176
+
177
+ # NTRIP Rev1 uses Shoutcast (ICY), which looks a lot like HTTP but isn't quite the same.
178
+ # Create an ICY session here to talk to an NTRIP Rev1 caster.
179
+ ok = self.create_icy_session()
180
+ if not ok:
181
+ self.logger.error("Failed to create NTRIP Rev1 ICY session.")
182
+ return
183
+
184
+ timeouts = 0
185
+ has_data = False
186
+ while self.streaming and timeouts < SOCKET_MAX_RECV_TIMEOUTS:
187
+ try:
188
+ response = self.sock.recv(2048)
189
+ if response:
190
+ timeouts = 0
191
+ self.handle_ntrip_data(response)
192
+ if not has_data:
193
+ self.logger.info("Received NTRIP data.")
194
+ has_data = True
195
+ else:
196
+ self.logger.warning("Connection closed by server")
197
+ break
198
+ except (socket.herror, socket.gaierror):
199
+ self.logger.warning("Error connecting to server %s:%d", self.host, self.port)
200
+ except (TimeoutError, socket.timeout):
201
+ self.logger.exception("Socket timeout.")
202
+ timeouts += 1
203
+
204
+ self.logger.info("NTRIP stream closed. Closing socket.")
205
+ # Close the socket.
206
+ try:
207
+ self.sock.close()
208
+ except OSError:
209
+ pass
210
+ self.sock = None
211
+
212
+ self.logger.info("NTRIP client finished.")
213
+
214
+ def stream_data_worker(self):
215
+ """
216
+ NTRIP client worker thread function.
217
+ """
218
+ self.streaming = True
219
+ while self.streaming:
220
+ self.stream_data()
221
+
222
+ def handle_ntrip_data(self, data):
223
+ """
224
+ Callback for handling NTRIP data.
225
+ """
226
+ # Send to receiver as is.
227
+ self.device.write(data)
228
+
229
+ def handle_nmea_gga(self, sentence):
230
+ """
231
+ Process an NMEA-GGA sentence passed in as a string.
232
+ """
233
+ fields = sentence.split(",")
234
+ if len(fields) < 7:
235
+ return
236
+ quality = int(fields[6] or 0)
237
+ if quality not in (0, 6): # No fix or estimated.
238
+ if not self.send_gga(sentence + "\r\n"):
239
+ # Something went wrong, restart the stream.
240
+ self.start_stream()
@@ -413,11 +413,26 @@ class GraphNavClient(BaseClient):
413
413
  request = self._build_navigate_to_anchor_request(
414
414
  seed_tform_goal, travel_params, route_params, cmd_duration, leases, used_endpoint,
415
415
  command_id, goal_waypoint_rt_seed_ewrt_seed_tolerance, gps_navigation_params)
416
- return self.call_async(self._stub.NavigateTo, request,
416
+ return self.call_async(self._stub.NavigateToAnchor, request,
417
417
  value_from_response=_command_id_from_navigate_route_response,
418
418
  error_from_response=_navigate_to_anchor_error, copy_request=False,
419
419
  **kwargs)
420
420
 
421
+ def navigate_to_anchor_full_async(self, seed_tform_goal, cmd_duration, route_params=None,
422
+ travel_params=None, leases=None, timesync_endpoint=None,
423
+ goal_waypoint_rt_seed_ewrt_seed_tolerance=None,
424
+ command_id=None, gps_navigation_params=None, **kwargs):
425
+ """Async version of navigate_to_anchor() that returns the full response."""
426
+ used_endpoint = timesync_endpoint or self._timesync_endpoint
427
+ if not used_endpoint:
428
+ raise GraphNavServiceResponseError(response=None, error_message='No timesync endpoint!')
429
+ request = self._build_navigate_to_anchor_request(
430
+ seed_tform_goal, travel_params, route_params, cmd_duration, leases, used_endpoint,
431
+ command_id, goal_waypoint_rt_seed_ewrt_seed_tolerance, gps_navigation_params)
432
+ return self.call_async(self._stub.NavigateToAnchor, request,
433
+ error_from_response=_navigate_to_anchor_error, copy_request=False,
434
+ **kwargs)
435
+
421
436
  def navigation_feedback(self, command_id=0, **kwargs):
422
437
  """Returns the feedback corresponding to the active route follow command.
423
438
 
@@ -1075,6 +1090,10 @@ def _get_streamed_data(response, data_type):
1075
1090
  return proto_instance
1076
1091
 
1077
1092
 
1093
+ def _get_streamed_download_graph(response):
1094
+ """Reads a streamed response to recreate a DownloadGraphRequest"""
1095
+ download_graph = _get_streamed_data(response, graph_nav_pb2.DownloadGraphResponse)
1096
+ return download_graph.graph
1078
1097
 
1079
1098
 
1080
1099
  def _get_streamed_waypoint_snapshot(response):
@@ -1327,6 +1346,12 @@ def _navigate_feedback_error(response):
1327
1346
 
1328
1347
 
1329
1348
 
1349
+ @handle_common_header_errors
1350
+ def _download_graph_stream_errors(response):
1351
+ """Return a custom exception based on download graph streaming response, None if no error."""
1352
+ return None
1353
+
1354
+
1330
1355
  @handle_common_header_errors
1331
1356
  @handle_unset_status_error(unset='STATUS_UNKNOWN')
1332
1357
  def _download_waypoint_snapshot_stream_errors(response):
@@ -52,3 +52,37 @@ class GripperCameraParamClient(BaseClient):
52
52
  """Async version of gripper_camera_get_param_service_command()."""
53
53
  return self.call_async(self._stub.GetParams, gripper_camera_get_param_request,
54
54
  error_from_response=common_header_errors, **kwargs)
55
+
56
+ def set_camera_calib(self, set_gripper_camera_calib_request, **kwargs):
57
+ """Issue gripper camera calibration
58
+
59
+ Args:
60
+ set_gripper_camera_calib_request (gripper_camera_params_pb2.GripperCameraCalibrationRequest) : The command request to set gripper camera calibration
61
+
62
+ Returns:
63
+ The GripperCameraCalibrationResponse message.
64
+ """
65
+ return self.call(self._stub.SetCamCalib, set_gripper_camera_calib_request,
66
+ error_from_response=common_header_errors, **kwargs)
67
+
68
+ def set_camera_calib_async(self, set_gripper_camera_calib_request, **kwargs):
69
+ """Async version of set_camera_calib()."""
70
+ return self.call_async(self._stub.SetCamCalib, set_gripper_camera_calib_request,
71
+ error_from_response=common_header_errors, **kwargs)
72
+
73
+ def get_camera_calib(self, get_gripper_camera_calib_request, **kwargs):
74
+ """Issue gripper camera get calibration
75
+
76
+ Arge:
77
+ get_gripper_camera_calib_request (gripper_camera_params_pb2.GripperCameraGetCalibrationRequest) : The command reqeust to get gripper camera calibration
78
+
79
+ Returns:
80
+ The GripperCameraGetCalibrationResponse message, which contains the GripperCameraCalibrationProto
81
+ """
82
+ return self.call(self._stub.GetCamCalib, get_gripper_camera_calib_request,
83
+ error_from_response=common_header_errors, **kwargs)
84
+
85
+ def get_camera_calib_asnyc(self, get_gripper_camera_calib_request, **kwargs):
86
+ """Asnyc version of get_camera_calib()."""
87
+ return self.call_async(self._stub.GetCamCalib, get_gripper_camera_calib_request,
88
+ error_from_response=common_header_errors, **kwargs)
bosdyn/client/image.py CHANGED
@@ -204,6 +204,22 @@ def _get_image_value(response):
204
204
  return response.image_responses
205
205
 
206
206
 
207
+ def pixel_format_to_numpy_type(pixel_format):
208
+ if pixel_format == image_pb2.Image.PIXEL_FORMAT_GREYSCALE_U8:
209
+ dtype = np.uint8
210
+ elif pixel_format == image_pb2.Image.PIXEL_FORMAT_RGB_U8:
211
+ dtype = np.uint8
212
+ elif pixel_format == image_pb2.Image.PIXEL_FORMAT_RGBA_U8:
213
+ dtype = np.uint8
214
+ elif pixel_format == image_pb2.Image.PIXEL_FORMAT_DEPTH_U16:
215
+ dtype = np.uint16
216
+ elif pixel_format == image_pb2.Image.PIXEL_FORMAT_GREYSCALE_U16:
217
+ dtype = ">u2" # Big endian
218
+ else:
219
+ raise Exception('Image PixelFormat type {} not supported'.format(pixel_format))
220
+ return dtype
221
+
222
+
207
223
  def write_pgm_or_ppm(image_response, filename="", filepath=".", include_pixel_format=False):
208
224
  """Write raw data from image_response to a PGM file.
209
225