def add_enumerated_image_sizes(spec, feature_name, sizes):
    """
    Annotate an input or output image feature in a neural network spec to
    to accommodate a list of enumerated image sizes.

    :param spec: MLModel
        The MLModel spec containing the feature.

    :param feature_name: str
        The name of the image feature for which to add size information.
        If the feature is not found in the input or output descriptions then
        an exception is thrown.

    :param sizes:  [] | NeuralNetworkImageSize
        A single or a list of NeuralNetworkImageSize objects which encode valid
        size information for a image feature.

    Examples
    --------
    .. sourcecode:: python

        import coremltools
        from coremltools.models.neural_network import flexible_shape_utils

        spec = coremltools.utils.load_spec("mymodel.mlmodel")
        image_sizes = [flexible_shape_utils.NeuralNetworkImageSize(128, 128)]
        image_sizes.append(flexible_shape_utils.NeuralNetworkImageSize(256, 256))
        flexible_shape_utils.add_enumerated_image_sizes(
            spec, feature_name="my_multiarray_featurename", sizes=image_sizes
        )

    :return:
        None. The spec object is updated
    """
    if not isinstance(sizes, list):
        sizes = [sizes]

    for size in sizes:
        if not isinstance(size, NeuralNetworkImageSize):
            raise Exception("Shape ranges should be of type NeuralNetworkImageSize")

    feature = _get_feature(spec, feature_name)
    if feature.type.WhichOneof("Type") != "imageType":
        raise Exception("Trying to add enumerated sizes to " "a non-image feature type")

    if feature.type.imageType.WhichOneof("SizeFlexibility") != "enumeratedSizes":
        feature.type.imageType.ClearField("SizeFlexibility")

    esizes_len = len(feature.type.imageType.enumeratedSizes.sizes)

    # Add default image size to list of enumerated sizes if enumerated sizes
    # field is currently empty
    if esizes_len == 0:
        fixed_height = feature.type.imageType.height
        fixed_width = feature.type.imageType.width
        sizes.append(NeuralNetworkImageSize(fixed_height, fixed_width))

    shapes_added_so_far = []
    for size in sizes:
        if [size.height, size.width] not in shapes_added_so_far:
            s = feature.type.imageType.enumeratedSizes.sizes.add()
            s.height = size.height
            s.width = size.width
            shapes_added_so_far.append([s.height, s.width])

    # Bump up specification version
    spec.specificationVersion = max(
        _MINIMUM_FLEXIBLE_SHAPES_SPEC_VERSION, spec.specificationVersion
    )

    Annotate an input or output image feature in a neural network spec to
    to accommodate a list of enumerated image sizes.

    :param spec: MLModel
        The MLModel spec containing the feature.

    :param feature_name: str
        The name of the image feature for which to add size information.
        If the feature is not found in the input or output descriptions then
        an exception is thrown.

    :param sizes:  [] | NeuralNetworkImageSize
        A single or a list of NeuralNetworkImageSize objects which encode valid
        size information for a image feature.

    Examples
    --------
    .. sourcecode:: python

        import coremltools
        from coremltools.models.neural_network import flexible_shape_utils

        spec = coremltools.utils.load_spec("mymodel.mlmodel")
        image_sizes = [flexible_shape_utils.NeuralNetworkImageSize(128, 128)]
        image_sizes.append(flexible_shape_utils.NeuralNetworkImageSize(256, 256))
        flexible_shape_utils.add_enumerated_image_sizes(
            spec, feature_name="my_multiarray_featurename", sizes=image_sizes
        )

    :return:
        None. The spec object is updated
    
coremltools/models/neural_network/flexible_shape_utils.py