def compute_orthographic_projection_images(
    samples,
    size,
    output_dir,
    rel_dir=None,
    in_group_slice=None,
    out_group_slice=None,
    metadata_field="orthographic_projection_metadata",
    shading_mode=None,
    colormap=None,
    subsampling_rate=None,
    projection_normal=None,
    bounds=None,
    padding=None,
    overwrite=False,
    skip_failures=False,
    progress=None,
):
    """Computes orthographic projection images for the point clouds in the
    given collection.

    This operation will populate :class:`OrthographicProjectionMetadata`
    instances for each projection in the ``metadata_field`` of each sample.

    Examples::

        import fiftyone as fo
        import fiftyone.utils.utils3d as fou3d
        import fiftyone.zoo as foz

        dataset = foz.load_zoo_dataset("quickstart-groups")
        view = dataset.select_group_slices("pcd")

        fou3d.compute_orthographic_projection_images(view, (-1, 512), "/tmp/proj")

        session = fo.launch_app(view)

    Args:
        samples: a :class:`fiftyone.core.collections.SampleCollection`
        size: the desired ``(width, height)`` for the generated maps. Either
            dimension can be None or negative, in which case the appropriate
            aspect-preserving value is used
        output_dir: an output directory in which to store the images/maps
        rel_dir (None): an optional relative directory to strip from each input
            filepath to generate a unique identifier that is joined with
            ``output_dir`` to generate an output path for each image. This
            argument allows for populating nested subdirectories in
            ``output_dir`` that match the shape of the input paths
        in_group_slice (None): the name of the group slice containing the point
            cloud data. Only applicable if ``samples`` is a grouped collection.
            If ``samples`` is a grouped collection and this parameter is not
            provided, the first point cloud slice will be used
        out_group_slice (None): the name of a group slice to which to add new
            samples containing the feature images/maps. Only applicable if
            ``samples`` is a grouped collection
        metadata_field ("orthographic_projection_metadata"): the name of the
            field in which to store :class:`OrthographicProjectionMetadata`
            instances for each projection
        shading_mode (None): an optional shading algorithm for the points.
            Supported values are ``("intensity", "rgb", or "height")``. The
            ``"intensity"`` and ``"rgb"`` options are only valid if the PCD's
            header contains the ``"rgb"`` flag. By default, all points are
            shaded white
        colormap (None): an optional colormap to use when shading gradients,
            formatted as either:

            -   a dict mapping values in ``[0, 1]`` to ``(R, G, B)`` tuples in
                ``[0, 255]``
            -   a list of of ``(R, G, B)`` tuples in ``[0, 255]`` that cover
                ``[0, 1]`` linearly spaced
        subsampling_rate (None): an optional unsigned int that, if provided,
            defines a uniform subsampling rate. The selected point indices are
            [0, k, 2k, ...], where ``k = subsampling_rate``
        projection_normal (None): the normal vector of the plane onto which to
            perform the projection. By default, ``(0, 0, 1)`` is used
        bounds (None): an optional ``((xmin, ymin, zmin), (xmax, ymax, zmax))``
            tuple defining the field of view in the projected plane for which
            to generate each map. Either element of the tuple or any/all of its
            values can be None, in which case a tight crop of the point cloud
            along the missing dimension(s) are used
        padding (None): a relative padding(s) in ``[0, 1]]`` to apply to the
            field of view bounds prior to projection. Can either be a single
            value to apply in all directions or a ``[padx, pady, padz]``. For
            example, pass ``padding=0.25`` with no ``bounds`` to project onto
            a tight crop of each point cloud with 25% padding around it
        overwrite (False): whether to overwrite existing images
        skip_failures (False): whether to gracefully continue without raising
            an error if a projection fails
        progress (None): whether to render a progress bar (True/False), use the
            default value ``fiftyone.config.show_progress_bars`` (None), or a
            progress callback function to invoke instead
    """
    if in_group_slice is None and samples.media_type == fom.GROUP:
        in_group_slice = _get_3d_slice(samples)

    if in_group_slice is not None or out_group_slice is not None:
        fov.validate_collection(samples, media_type=fom.GROUP)
        group_field = samples.group_field

        view = samples.select_group_slices(in_group_slice).select_fields(
            group_field
        )
    else:
        view = samples.select_fields()

    fov.validate_collection(view, media_type={fom.POINT_CLOUD, fom.THREE_D})

    if out_group_slice is not None:
        out_samples = []

    filename_maker = fou.UniqueFilenameMaker(
        output_dir=output_dir, rel_dir=rel_dir, ignore_existing=overwrite
    )

    for sample in view.iter_samples(autosave=True, progress=progress):
        if view.media_type == fom.THREE_D:
            pcd_filepath = _get_pcd_filepath_from_scene(sample.filepath)
        else:
            pcd_filepath = sample.filepath

        image_path = filename_maker.get_output_path(
            pcd_filepath, output_ext=".png"
        )

        try:
            img, metadata = compute_orthographic_projection_image(
                pcd_filepath,
                size,
                shading_mode=shading_mode,
                colormap=colormap,
                subsampling_rate=subsampling_rate,
                projection_normal=projection_normal,
                bounds=bounds,
                padding=padding,
            )
        except Exception as e:
            if not skip_failures:
                raise

            if skip_failures != "ignore":
                logger.warning(e)

            continue

        foui.write(img, image_path)
        metadata.filepath = image_path

        sample[metadata_field] = metadata

        if out_group_slice is not None:
            s = Sample(filepath=image_path)
            s[group_field] = sample[group_field].element(out_group_slice)
            s[metadata_field] = metadata
            out_samples.append(s)

    if out_group_slice is not None:
        samples._root_dataset.add_samples(out_samples)
Computes orthographic projection images for the point clouds in the
    given collection.

    This operation will populate :class:`OrthographicProjectionMetadata`
    instances for each projection in the ``metadata_field`` of each sample.

    Examples::

        import fiftyone as fo
        import fiftyone.utils.utils3d as fou3d
        import fiftyone.zoo as foz

        dataset = foz.load_zoo_dataset("quickstart-groups")
        view = dataset.select_group_slices("pcd")

        fou3d.compute_orthographic_projection_images(view, (-1, 512), "/tmp/proj")

        session = fo.launch_app(view)

    Args:
        samples: a :class:`fiftyone.core.collections.SampleCollection`
        size: the desired ``(width, height)`` for the generated maps. Either
            dimension can be None or negative, in which case the appropriate
            aspect-preserving value is used
        output_dir: an output directory in which to store the images/maps
        rel_dir (None): an optional relative directory to strip from each input
            filepath to generate a unique identifier that is joined with
            ``output_dir`` to generate an output path for each image. This
            argument allows for populating nested subdirectories in
            ``output_dir`` that match the shape of the input paths
        in_group_slice (None): the name of the group slice containing the point
            cloud data. Only applicable if ``samples`` is a grouped collection.
            If ``samples`` is a grouped collection and this parameter is not
            provided, the first point cloud slice will be used
        out_group_slice (None): the name of a group slice to which to add new
            samples containing the feature images/maps. Only applicable if
            ``samples`` is a grouped collection
        metadata_field ("orthographic_projection_metadata"): the name of the
            field in which to store :class:`OrthographicProjectionMetadata`
            instances for each projection
        shading_mode (None): an optional shading algorithm for the points.
            Supported values are ``("intensity", "rgb", or "height")``. The
            ``"intensity"`` and ``"rgb"`` options are only valid if the PCD's
            header contains the ``"rgb"`` flag. By default, all points are
            shaded white
        colormap (None): an optional colormap to use when shading gradients,
            formatted as either:

            -   a dict mapping values in ``[0, 1]`` to ``(R, G, B)`` tuples in
                ``[0, 255]``
            -   a list of of ``(R, G, B)`` tuples in ``[0, 255]`` that cover
                ``[0, 1]`` linearly spaced
        subsampling_rate (None): an optional unsigned int that, if provided,
            defines a uniform subsampling rate. The selected point indices are
            [0, k, 2k, ...], where ``k = subsampling_rate``
        projection_normal (None): the normal vector of the plane onto which to
            perform the projection. By default, ``(0, 0, 1)`` is used
        bounds (None): an optional ``((xmin, ymin, zmin), (xmax, ymax, zmax))``
            tuple defining the field of view in the projected plane for which
            to generate each map. Either element of the tuple or any/all of its
            values can be None, in which case a tight crop of the point cloud
            along the missing dimension(s) are used
        padding (None): a relative padding(s) in ``[0, 1]]`` to apply to the
            field of view bounds prior to projection. Can either be a single
            value to apply in all directions or a ``[padx, pady, padz]``. For
            example, pass ``padding=0.25`` with no ``bounds`` to project onto
            a tight crop of each point cloud with 25% padding around it
        overwrite (False): whether to overwrite existing images
        skip_failures (False): whether to gracefully continue without raising
            an error if a projection fails
        progress (None): whether to render a progress bar (True/False), use the
            default value ``fiftyone.config.show_progress_bars`` (None), or a
            progress callback function to invoke instead
    
fiftyone/utils/utils3d.py