code: def tutorial(args):
      """
      This function is intended to be a tutorial on how to retrieve data in a
      synchronous way, and project 3D points from a lidar to a 2D camera.
      """
      # Connect to the server
      client = carla.Client(args.host, args.port)
      client.set_timeout(2.0)
      world = client.get_world()
      bp_lib = world.get_blueprint_library()
  
      traffic_manager = client.get_trafficmanager(8000)
      traffic_manager.set_synchronous_mode(True)
  
      original_settings = world.get_settings()
      settings = world.get_settings()
      settings.synchronous_mode = True
      settings.fixed_delta_seconds = 3.0
      world.apply_settings(settings)
  
      vehicle = None
      camera = None
      lidar = None
  
      try:
          if not os.path.isdir("_out"):
              os.mkdir("_out")
          # Search the desired blueprints
          vehicle_bp = bp_lib.filter("vehicle.lincoln.mkz_2017")[0]
          camera_bp = bp_lib.filter("sensor.camera.rgb")[0]
          lidar_bp = bp_lib.filter("sensor.lidar.ray_cast")[0]
  
          # Configure the blueprints
          camera_bp.set_attribute("image_size_x", str(args.width))
          camera_bp.set_attribute("image_size_y", str(args.height))
  
          if args.no_noise:
              lidar_bp.set_attribute("dropoff_general_rate", "0.0")
              lidar_bp.set_attribute("dropoff_intensity_limit", "1.0")
              lidar_bp.set_attribute("dropoff_zero_intensity", "0.0")
          lidar_bp.set_attribute("upper_fov", str(args.upper_fov))
          lidar_bp.set_attribute("lower_fov", str(args.lower_fov))
          lidar_bp.set_attribute("channels", str(args.channels))
          lidar_bp.set_attribute("range", str(args.range))
          lidar_bp.set_attribute(
              "points_per_second", str(args.points_per_second)
          )
  
          # Spawn the blueprints
          vehicle = world.spawn_actor(
              blueprint=vehicle_bp,
              transform=world.get_map().get_spawn_points()[0],
          )
          vehicle.set_autopilot(True)
          camera = world.spawn_actor(
              blueprint=camera_bp,
              transform=carla.Transform(carla.Location(x=1.6, z=1.6)),
              attach_to=vehicle,
          )
          lidar = world.spawn_actor(
              blueprint=lidar_bp,
              transform=carla.Transform(carla.Location(x=1.0, z=1.8)),
              attach_to=vehicle,
          )
  
          # Build the K projection matrix:
          # K = [[Fx,  0, image_w/2],
          #      [ 0, Fy, image_h/2],
          #      [ 0,  0,         1]]
          image_w = camera_bp.get_attribute("image_size_x").as_int()
          image_h = camera_bp.get_attribute("image_size_y").as_int()
          fov = camera_bp.get_attribute("fov").as_float()
          focal = image_w / (2.0 * np.tan(fov * np.pi / 360.0))
  
          # In this case Fx and Fy are the same since the pixel aspect
          # ratio is 1
          K = np.identity(3)
          K[0, 0] = K[1, 1] = focal
          K[0, 2] = image_w / 2.0
          K[1, 2] = image_h / 2.0
  
          # The sensor data will be saved in thread-safe Queues
          image_queue = Queue()
          lidar_queue = Queue()
  
          camera.listen(lambda data: sensor_callback(data, image_queue))
          lidar.listen(lambda data: sensor_callback(data, lidar_queue))
  
          for frame in range(args.frames):
              world.tick()
              world_frame = world.get_snapshot().frame
  
              try:
                  # Get the data once it's received.
                  image_data = image_queue.get(True, 1.0)
                  lidar_data = lidar_queue.get(True, 1.0)
              except Empty:
                  print("[Warning] Some sensor data has been missed")
                  continue
  
              assert image_data.frame == lidar_data.frame == world_frame
              # At this point, we have the synchronized information from the 2 sensors.
              sys.stdout.write(
                  "\r(%d/%d) Simulation: %d Camera: %d Lidar: %d"
                  % (
                      frame,
                      args.frames,
                      world_frame,
                      image_data.frame,
                      lidar_data.frame,
                  )
                  + " "
              )
              sys.stdout.flush()
  
              # Get the raw BGRA buffer and convert it to an array of RGB of
              # shape (image_data.height, image_data.width, 3).
              im_array = np.copy(
                  np.frombuffer(
                      image_data.raw_data, dtype=np.dtype("uint8")
                  )
              )
              im_array = np.reshape(
                  im_array, (image_data.height, image_data.width, 4)
              )
              im_array = im_array[:, :, :3][:, :, ::-1]
  
              # Get the lidar data and convert it to a numpy array.
              p_cloud_size = len(lidar_data)
              p_cloud = np.copy(
                  np.frombuffer(
                      lidar_data.raw_data, dtype=np.dtype("f4")
                  )
              )
              p_cloud = np.reshape(p_cloud, (p_cloud_size, 4))
  
              # Lidar intensity array of shape (p_cloud_size,) but, for now, let's
              # focus on the 3D points.
              intensity = np.array(p_cloud[:, 3])
  
              # Point cloud in lidar sensor space array of shape (3, p_cloud_size).
              local_lidar_points = np.array(p_cloud[:, :3]).T
  
              # Add an extra 1.0 at the end of each 3d point so it becomes of
              # shape (4, p_cloud_size) and it can be multiplied by a (4, 4) matrix.
              local_lidar_points = np.r_[
                  local_lidar_points,
                  [np.ones(local_lidar_points.shape[1])],
              ]
  
              # This (4, 4) matrix transforms the points from lidar space to world space.
              lidar_2_world = lidar.get_transform().get_matrix()
  
              # Transform the points from lidar space to world space.
              world_points = np.dot(lidar_2_world, local_lidar_points)
  
              # This (4, 4) matrix transforms the points from world to sensor coordinates.
              world_2_camera = np.array(
                  camera.get_transform().get_inverse_matrix()
              )
  
              # Transform the points from world space to camera space.
              sensor_points = np.dot(world_2_camera, world_points)
  
              # New we must change from UE4's coordinate system to an "standard"
              # camera coordinate system (the same used by OpenCV):
  
              # ^ z                       . z
              # |                        /
              # |              to:      +-------> x
              # | . x                   |
              # |/                      |
              # +-------> y             v y
  
              # This can be achieved by multiplying by the following matrix:
              # [[ 0,  1,  0 ],
              #  [ 0,  0, -1 ],
              #  [ 1,  0,  0 ]]
  
              # Or, in this case, is the same as swapping:
              # (x, y ,z) -> (y, -z, x)
              point_in_camera_coords = np.array(
                  [
                      sensor_points[1],
                      sensor_points[2] * -1,
                      sensor_points[0],
                  ]
              )
  
              # Finally we can use our K matrix to do the actual 3D -> 2D.
              points_2d = np.dot(K, point_in_camera_coords)
  
              # Remember to normalize the x, y values by the 3rd value.
              points_2d = np.array(
                  [
                      points_2d[0, :] / points_2d[2, :],
                      points_2d[1, :] / points_2d[2, :],
                      points_2d[2, :],
                  ]
              )
  
              # At this point, points_2d[0, :] contains all the x and points_2d[1, :]
              # contains all the y values of our points. In order to properly
              # visualize everything on a screen, the points that are out of the screen
              # must be discarted, the same with points behind the camera projection plane.
              points_2d = points_2d.T
              intensity = intensity.T
              points_in_canvas_mask = (
                  (points_2d[:, 0] > 0.0)
                  & (points_2d[:, 0] < image_w)
                  & (points_2d[:, 1] > 0.0)
                  & (points_2d[:, 1] < image_h)
                  & (points_2d[:, 2] > 0.0)
              )
              points_2d = points_2d[points_in_canvas_mask]
              intensity = intensity[points_in_canvas_mask]
  
              # Extract the screen coords (uv) as integers.
              u_coord = points_2d[:, 0].astype(np.int)
              v_coord = points_2d[:, 1].astype(np.int)
  
              # Since at the time of the creation of this script, the intensity function
              # is returning high values, these are adjusted to be nicely visualized.
              intensity = 4 * intensity - 3
              color_map = (
                  np.array(
                      [
                          np.interp(intensity, VID_RANGE, VIRIDIS[:, 0])
                          * 255.0,
                          np.interp(intensity, VID_RANGE, VIRIDIS[:, 1])
                          * 255.0,
                          np.interp(intensity, VID_RANGE, VIRIDIS[:, 2])
                          * 255.0,
                      ]
                  )
                  .astype(np.int)
                  .T
              )
  
              if args.dot_extent <= 0:
                  # Draw the 2d points on the image as a single pixel using numpy.
                  im_array[v_coord, u_coord] = color_map
              else:
                  # Draw the 2d points on the image as squares of extent args.dot_extent.
                  for i in range(len(points_2d)):
                      # I'm not a NumPy expert and I don't know how to set bigger dots
                      # without using this loop, so if anyone has a better solution,
                      # make sure to update this script. Meanwhile, it's fast enough :)
                      im_array[
                          v_coord[i]
                          - args.dot_extent : v_coord[i]
                          + args.dot_extent,
                          u_coord[i]
                          - args.dot_extent : u_coord[i]
                          + args.dot_extent,
                      ] = color_map[i]
  
              # Save the image using Pillow module.
              image = Image.fromarray(im_array)
              image.save("_out/%08d.png" % image_data.frame)
  
      finally:
          # Apply the original settings when exiting.
          world.apply_settings(original_settings)
  
          # Destroy the actors in the scene.
          if camera:
              camera.destroy()
          if lidar:
              lidar.destroy()
          if vehicle:
              vehicle.destroy()
  
file_path: carla/PythonAPI/examples/lidar_to_camera.py
imports: import glob
  import os
  import sys
  import carla
  import argparse
  from queue import Queue
  from queue import Empty
  from matplotlib import cm
label_desc: Use image node_id 2 for calling a global function with example usage: tutorial(args) without return types