Source code for coremltools.converters.mil.mil.ops.defs.iOS17.tensor_operation

#  Copyright (c) 2023, Apple Inc. All rights reserved.
#
#  Use of this source code is governed by a BSD-3-clause license that can be
#  found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause

from coremltools.converters.mil.mil import types
from coremltools.converters.mil.mil.input_type import DefaultInputs, InputSpec, TensorInputType
from coremltools.converters.mil.mil.ops.defs._op_reqs import register_op
from coremltools.converters.mil.mil.ops.defs.iOS15.tensor_operation import (
    non_maximum_suppression as _nms_iOS15,
)
from coremltools.converters.mil.mil.ops.defs.iOS16.tensor_operation import topk as _topk_iOS16
from coremltools.converters.mil.mil.ops.defs.iOS17 import _IOS17_TARGET


[docs]@register_op(opset_version=_IOS17_TARGET) class non_maximum_suppression(_nms_iOS15): """ Performs non-maximum suppression (NMS) on the boxes according to their intersection-over-union (IoU). NMS iteratively removes lower-scoring boxes which have an IoU greater than ``iou_threshold`` with another (higher-scoring) box. The major differences between this version and the iOS 15 :py:class:`~.iOS15.tensor_operation.non_maximum_suppression` are as follows: - The input parameter ``score_threshold`` has been removed. - The inputs ``boxes`` and ``scores`` are ordered with number of boxes in the last dimension. - The fourth output containing number of boxes for each batch has been removed. Parameters ---------- boxes: tensor<[n, 4, B], T> (Required) * Box coordinates on which to perform NMS. The coordinates are expected in ``CENTER_SIZE_WIDTH_FIRST`` format ``(x, y, width, height)``, in which ``(x, y)`` is the center. scores: tensor<[n, K, B], T> (Required) * Scores for each one of the boxes. ``K`` is the number of classes. iou_threshold: const<T> (Required) * The intersection over union (IoU) threshold over which boxes are suppressed. NMS remove all overlapping boxes with ``IoU > iou_threshold``. max_boxes: const<i32> (Required) * Maximum number of boxes to select. If the number of surviving boxes are less, the output is padded up to this number. per_class_suppression: const<bool> (Optional) * Defaults to ``False``. * If ``True``, suppression is performed independently within boxes of each class. Returns ------- tensor<[n, 4, max_boxes], T> * Coordinates of selected boxes. tensor<[n, K, max_boxes], T> * Scores of selected boxes. tensor<[n, max_boxes], i32> * Indices of selected boxes. Attributes ---------- T: fp16, fp32 """ input_spec = InputSpec( boxes=TensorInputType(type_domain="T"), scores=TensorInputType(type_domain="T"), iou_threshold=TensorInputType(const=True, type_domain="T"), max_boxes=TensorInputType(const=True, type_domain=types.int32), per_class_suppression=TensorInputType(const=True, optional=True, type_domain=types.bool), ) def type_inference(self): boxes_dtype = self.boxes.dtype scores_dtype = self.scores.dtype n_batch, n_score_class, _ = self.scores.shape max_boxes = self.max_boxes.val return ( types.tensor(boxes_dtype, (n_batch, 4, max_boxes)), types.tensor(scores_dtype, (n_batch, n_score_class, max_boxes)), types.tensor(types.int32, (n_batch, max_boxes)), )
[docs]@register_op(opset_version=_IOS17_TARGET) class topk(_topk_iOS16): """ A version of ``topk`` for iOS 17+. The differences between this version and the iOS 16 :py:class:`~.iOS16.tensor_operation.topk` are: - New data type support. The newly added data types are: - int8, uint8, int16, unint16 for ``x`` and output. - int8, int16 for ``k``. - Validation restrictions on the optional ``indices`` output must be either uint16 or int32. - A new input parameter ``output_indices_dtype`` has been added to set the dtype of output ``indices``. Parameters ---------- x: <\*?, T> (Required) * Input tensor. k: const<K> (Optional) * Defaults to ``1``. * Number of values/indices to be computed along each axis. * Set to ``-1`` to select all elements. axis: const<i32> (Optional) * Defaults to ``-1`` (last dimension). * Axis to perform the operation. ascending: const<bool> (Optional) * Defaults to ``False``, sort in descending order. * ``True`` to sort in ascending order. sort: const<bool> (Optional) * Defaults to ``True``. * If ``True``, ``top-k`` elements are themselves sorted. Otherwise, no particular ordering is guaranteed. return_indices: const<bool> (Optional) * Defaults to ``True``. * If ``True``, returns both values and indices. Otherwise, returns only the ``top-k`` values. output_indices_dtype: const<str> (Optional, default="int32") * It can only be set when ``return_indices`` is ``True``. * This parameter can take ``"int32"`` or ``"uint16"`` as values. Returns ------- tensor<\*?, T> * Values of top/bottom ``k`` elements. tensor<\*?, U> * Only returned when ``return_indices = True`` * Indices of the top/bottom ``k`` elements along axis. * U is int32 or uint16 determined by ``output_indices_dtype`` (int32 by default). Attributes ---------- T: fp16, fp32, int8, int16, int32, uint8, uint16 K: int8, int16, int32 """ input_spec = InputSpec( x=TensorInputType(type_domain="T"), k=TensorInputType(const=True, optional=True, type_domain="K"), axis=TensorInputType(const=True, optional=True, type_domain=types.int32), ascending=TensorInputType(const=True, optional=True, type_domain=types.bool), sort=TensorInputType(const=True, optional=True, type_domain=types.bool), return_indices=TensorInputType(const=True, optional=True, type_domain=types.bool), output_indices_dtype=TensorInputType(const=True, optional=True, type_domain=types.str), ) type_domains = { "T": ( types.fp16, types.fp32, types.int8, types.int16, types.int32, types.uint8, types.uint16, ), "K": (types.int8, types.int16, types.int32), } _ALLOWED_OUTPUT_INDICES_DTYPES = {"int32", "uint16"} def default_inputs(self): parent_default_inputs = super().default_inputs() # If return_indices is not set, it is default to True. # output_indices_dtype can only be set when return_indices = True if self.return_indices is None or self.return_indices.val: return parent_default_inputs + DefaultInputs(output_indices_dtype="int32") return parent_default_inputs def type_inference(self): if not self.return_indices.val and self.output_indices_dtype is not None: raise ValueError( 'In iOS17 topk op, "output_indices_dtype" can only be set when "return_indices=True".' ) if self.return_indices.val: if self.output_indices_dtype.val not in self._ALLOWED_OUTPUT_INDICES_DTYPES: raise ValueError( f'"topk" op invalid output_indices_dtype: "{self.output_indices_dtype.val}". ' f"Valid options are: {self._ALLOWED_OUTPUT_INDICES_DTYPES}" ) value_type, indices_type = super().type_inference() indices_type = types.tensor( types.string_to_builtin(self.output_indices_dtype.val), indices_type.get_shape() ) return value_type, indices_type else: return super().type_inference()