Supervisely
About SuperviselyEcosystemContact usSlack
  • 💻Supervisely Developer Portal
  • 🎉Getting Started
    • Installation
    • Basics of authentication
    • Intro to Python SDK
    • Environment variables
    • Supervisely annotation format
      • Project Structure
      • Project Meta: Classes, Tags, Settings
      • Objects
      • Tags
      • Image Annotation
      • Video Annotation
      • Point Clouds Annotation
      • Point Cloud Episode Annotation
      • Volumes Annotation
    • Python SDK tutorials
      • Images
        • Images
        • Image and object tags
        • Spatial labels on images
        • Keypoints (skeletons)
        • Multispectral images
        • Multiview images
        • Advanced: Optimized Import
        • Advanced: Export
      • Videos
        • Videos
        • Video and object tags
        • Spatial labels on videos
      • Point Clouds
        • Point Clouds (LiDAR)
        • Point Cloud Episodes and object tags
        • 3D point cloud object segmentation based on sensor fusion and 2D mask guidance
        • 3D segmentation masks projection on 2D photo context image
      • Volumes
        • Volumes (DICOM)
        • Spatial labels on volumes
      • Common
        • Iterate over a project
        • Iterate over a local project
        • Progress Bar tqdm
        • Cloning projects for development
    • Command Line Interface (CLI)
      • Enterprise CLI Tool
        • Instance administration
        • Workflow automation
      • Supervisely SDK CLI
    • Connect your computer
      • Linux
      • Windows WSL
      • Troubleshooting
  • 🔥App development
    • Basics
      • Create app from any py-script
      • Configuration file
        • config.json
        • Example 1. Headless
        • Example 2. App with GUI
        • v1 - Legacy
          • Example 1. v1 Modal Window
          • Example 2. v1 app with GUI
      • Add private app
      • Add public app
      • App Compatibility
    • Apps with GUI
      • Hello World!
      • App in the Image Labeling Tool
      • App in the Video Labeling Tool
      • In-browser app in the Labeling Tool
    • Custom import app
      • Overview
      • From template - simple
      • From scratch - simple
      • From scratch GUI - advanced
      • Finding directories with specific markers
    • Custom export app
      • Overview
      • From template - simple
      • From scratch - advanced
    • Neural Network integration
      • Overview
      • Serving App
        • Introduction
        • Instance segmentation
        • Object detection
        • Semantic segmentation
        • Pose estimation
        • Point tracking
        • Object tracking
        • Mask tracking
        • Image matting
        • How to customize model inference
        • Example: Custom model inference with probability maps
      • Serving App with GUI
        • Introduction
        • How to use default GUI template
        • Default GUI template customization
        • How to create custom user interface
      • Inference API
      • Training App
        • Overview
        • Tensorboard template
        • Object detection
      • High level scheme
      • Custom inference pipeline
      • Train and predict automation model pipeline
    • Advanced
      • Advanced debugging
      • How to make your own widget
      • Tutorial - App Engine v1
        • Chapter 1 Headless
          • Part 1 — Hello world! [From your Python script to Supervisely APP]
          • Part 2 — Errors handling [Catching all bugs]
          • Part 3 — Site Packages [Customize your app]
          • Part 4 — SDK Preview [Lemons counter app]
          • Part 5 — Integrate custom tracker into Videos Annotator tool [OpenCV Tracker]
        • Chapter 2 Modal Window
          • Part 1 — Modal window [What is it?]
          • Part 2 — States and Widgets [Customize modal window]
        • Chapter 3 UI
          • Part 1 — While True Script [It's all what you need]
          • Part 2 — UI Rendering [Simplest UI Application]
          • Part 3 — APP Handlers [Handle Events and Errors]
          • Part 4 — State and Data [Mutable Fields]
          • Part 5 — Styling your app [Customizing the UI]
        • Chapter 4 Additionals
          • Part 1 — Remote Developing with PyCharm [Docker SSH Server]
      • Custom Configuration
        • Fixing SSL Certificate Errors in Supervisely
        • Fixing 400 HTTP errors when using HTTP instead of HTTPS
      • Autostart
      • Coordinate System
      • MLOps Workflow integration
    • Widgets
      • Input
        • Input
        • InputNumber
        • InputTag
        • BindedInputNumber
        • DatePicker
        • DateTimePicker
        • ColorPicker
        • TimePicker
        • ClassesMapping
        • ClassesColorMapping
      • Controls
        • Button
        • Checkbox
        • RadioGroup
        • Switch
        • Slider
        • TrainValSplits
        • FileStorageUpload
        • Timeline
        • Pagination
      • Text Elements
        • Text
        • TextArea
        • Editor
        • Copy to Clipboard
        • Markdown
        • Tooltip
        • ElementTag
        • ElementTagsList
      • Media
        • Image
        • LabeledImage
        • GridGallery
        • Video
        • VideoPlayer
        • ImagePairSequence
        • Icons
        • ObjectClassView
        • ObjectClassesList
        • ImageSlider
        • Carousel
        • TagMetaView
        • TagMetasList
        • ImageAnnotationPreview
        • ClassesMappingPreview
        • ClassesListPreview
        • TagsListPreview
        • MembersListPreview
      • Selection
        • Select
        • SelectTeam
        • SelectWorkspace
        • SelectProject
        • SelectDataset
        • SelectItem
        • SelectTagMeta
        • SelectAppSession
        • SelectString
        • Transfer
        • DestinationProject
        • TeamFilesSelector
        • FileViewer
        • Dropdown
        • Cascader
        • ClassesListSelector
        • TagsListSelector
        • MembersListSelector
        • TreeSelect
        • SelectCudaDevice
      • Thumbnails
        • ProjectThumbnail
        • DatasetThumbnail
        • VideoThumbnail
        • FolderThumbnail
        • FileThumbnail
      • Status Elements
        • Progress
        • NotificationBox
        • DoneLabel
        • DialogMessage
        • TaskLogs
        • Badge
        • ModelInfo
        • Rate
        • CircleProgress
      • Layouts and Containers
        • Card
        • Container
        • Empty
        • Field
        • Flexbox
        • Grid
        • Menu
        • OneOf
        • Sidebar
        • Stepper
        • RadioTabs
        • Tabs
        • TabsDynamic
        • ReloadableArea
        • Collapse
        • Dialog
        • IFrame
      • Tables
        • Table
        • ClassicTable
        • RadioTable
        • ClassesTable
        • RandomSplitsTable
        • FastTable
      • Charts and Plots
        • LineChart
        • GridChart
        • HeatmapChart
        • ApexChart
        • ConfusionMatrix
        • LinePlot
        • GridPlot
        • ScatterChart
        • TreemapChart
        • PieChart
      • Compare Data
        • MatchDatasets
        • MatchTagMetas
        • MatchObjClasses
        • ClassBalance
        • CompareAnnotations
      • Widgets demos on github
  • 😎Advanced user guide
    • Objects binding
    • Automate with Python SDK & API
      • Start and stop app
      • User management
      • Labeling Jobs
  • 🖥️UI widgets
    • Element UI library
    • Supervisely UI widgets
    • Apexcharts - modern & interactive charts
    • Plotly graphing library
  • 📚API References
    • REST API Reference
    • Python SDK Reference
Powered by GitBook
On this page
  • Custom Inference Settings
  • Model Information
  • Sliding window mode
  • Model files storage
  • Model meta for multitask models

Was this helpful?

Edit on GitHub
  1. App development
  2. Neural Network integration
  3. Serving App

How to customize model inference

PreviousImage mattingNextExample: Custom model inference with probability maps

Last updated 6 months ago

Was this helpful?

This document outlines some of the features of the Inference class that can be useful in customizing your model's behavior.

Custom Inference Settings

Your neural network (NN) model can use various parameters that you may want to expose to the user. These parameters might include confidence threshold, intersection over union (IoU) threshold, and others. We have provided a way to allow users to set up some of these parameters when they connect to your model from the Inference dashboard or the Labeling Tool.

To support such parameters in your model, you should provide a dictionary or YAML file with default values to your model class, like this:

settings = { 'confidence_threshold': 0.5 }
m = MyModel(model_dir=model_dir, custom_inference_settings=settings)

These parameters will be provided to the predict(image_path, settings) method as the settings parameter. In this method, you can use these parameters as needed, as shown in the following example from the :

def predict(self, image_path: str, settings: Dict[str, Any]) -> List[sly.nn.PredictionMask]:
    confidence_threshold = settings.get("confidence_threshold", 0.5)
    image = cv2.imread(image_path)  # BGR

    ####### CUSTOM CODE FOR MY MODEL STARTS (e.g. DETECTRON2) #######
    outputs = self.predictor(image)  # get predictions from Detectron2 model
    pred_classes = outputs["instances"].pred_classes.detach().cpu().numpy()
    pred_class_names = [self.class_names[pred_class] for pred_class in pred_classes]
    pred_scores = outputs["instances"].scores.detach().cpu().numpy().tolist()
    pred_masks = outputs["instances"].pred_masks.detach().cpu().numpy()
    ####### CUSTOM CODE FOR MY MODEL ENDS (e.g. DETECTRON2)  ########

    results = []
    for score, class_name, mask in zip(pred_scores, pred_class_names, pred_masks):
        # filter predictions by confidence
        if score >= confidence_threshold:
            results.append(sly.nn.PredictionMask(class_name, mask, score))
    return results

In this example, all predictions with a confidence score lower than the confidence_threshold parameter value will not be included in the result annotation.

After the user changes the parameter, the new value will be provided to your serving app in the request, and the settings dictionary will contain the new values in the predict() method.

If you want to add comments to your settings, as shown in the screenshot above, it is recommended to provide a path to a YAML file with comments to the custom_inference_settings parameter instead of a dictionary.

Model Information

When users use your served model, they will connect to your app from another app, which sets up parameters to apply your neural network to data projects.

To ensure that users have chosen a suitable model for their task, it can be helpful to provide additional information about your served model. You can provide this information using the get_info() method, which returns a dictionary of parameters.

By default, these parameters are related to the chosen computer vision problem, but you can add additional information. We recommend overriding this method in your model class and adding such information as model_name, checkpoint_name, pretrained_on_dataset, and device. Of course, you can add any custom parameters.

def get_info(self) -> dict:
    info = super().get_info()
    info["model_name"] = self.selected_model_name
    info["checkpoint_name"] = self.checkpoint_name
    info["pretrained_on_dataset"] = self.dataset_name
    info["device"] = self.device
    return info

It's important to understand that method get_info() of the Inference class calls method get_classes(), which is not implemented by default and must be declared explicitly for each model.

Sliding window mode

One problem with neural network model inference is that it can be challenging to apply them to large images with small objects. We provide tools to split the image into smaller parts, infer each part independently, and merge the results afterward.

This problem is significant for some computer vision tasks, but not for all. Therefore, it is crucial to consider this issue at the beginning.

We provide three modes to use sliding window:

  • none

This means not to use sliding window and prevent users from setting up sliding window parameters from Inference interfaces. In this mode, you will get the path to the full image as the parameter image_path in the predict(image_path, settings) method.

  • basic

In this mode, users can set up sliding window parameters, and you will get the path to a part of the image as the parameter image_path in the predict(image_path, settings) method.

In basic mode, all predictions are combined from all image parts into one result annotation.

  • advanced

In advanced sliding window mode, you should implement the predict_raw() method which will predict the objects like predict() method, but they will be changed after by NMS algorithm. This approach is more appropriate for the object detection task.

To ensure that your app uses the required sliding window mode, see the class of the task from which your model class inherits (for example, supervisely.nn.inference.InstanceSegmentation) and check the sliding_window_mode parameter in the constructor of the class.

If you want to change this parameter in your model class, provide the correct mode value to the sliding_window_mode parameter of your model constructor:

m = MyModel(sliding_window_mode="none")

Model files storage

To simplify data manipulations, we support the model_dir parameter which is used as the location for all files needed for model inference. This folder must be provided to the load_on_device(model_dir, device) method to prepare your model.

You can also use the download(src_path, dst_path) method of the model class to download all the required files in the load_on_device(model_dir, device) method. Currently, you can provide external URLs or the path to a file or folder in Team Files as the src_path. By default, the destination path is {model_dir}/{filename}, but you can specify a different destination path to change the location or rename the downloaded file.

model_name = 'MaskRCNN'
self.download(src_path=weights_url, dst_path=f'{model_dir}/{model_name}.pth')

It is recommended to provide the model_dir parameter in the constructor of your model to ensure that the download() method works correctly.

Model meta for multitask models

A served model can provide additional info about its state through model_meta property. (e.g. description of annotation classes, type of predicted object). This data helps inference GUI and other supervisely applications to display correct model properties and visualize predictions.

In most cases this property is automatically generated within the SDK, so you don't have to worry about it. But for multitasking applications it's important to check if model_meta is built correctly for the chosen task/model.

Let's look closely at how to correctly define model_meta for your custom model. The type of model_meta is ProjectMeta and it contains information about class names, shapes and colors (autogenerate feature). This property will be constructed automatically only once the first time it is called.

@property
def model_meta(self) -> ProjectMeta: 
    if self._model_meta is None:
        self.update_model_meta()
    return self._model_meta

def update_model_meta(self):
    """
    Update model meta.
    Make sure `self._get_obj_class_shape()` method returns the correct shape.
    """
    colors = get_predefined_colors(len(self.get_classes()))
    classes = []
    for name, rgb in zip(self.get_classes(), colors):
        classes.append(ObjClass(name, self._get_obj_class_shape(), rgb))
    self._model_meta = ProjectMeta(classes)
    self._get_confidence_tag_meta()

Since the model_meta is specific to a chosen model, it can be guaranteed that the property will not be called before the self.load_on_device() function is called. Therefore, it is important to make sure that after calling self.load_on_device(), the self.get_classes() and self._get_obj_class_shape() functions work correctly for your instance.

There's nothing complicated with self.get_classes():

def get_classes(self) -> List[str]:
    return self.class_names

def load_on_device(
        self,
        model_dir: str,
        device: Literal["cpu", "cuda", "cuda:0", "cuda:1", "cuda:2", "cuda:3"] = "cpu",
    ):
    ####### CUSTOM CODE: model instantiating, downloading weights, loading it on device.
    # define `class_names` list for chosen model
    # overwrite `self.class_names` attribute
    self.class_names = class_names
    self.update_model_meta()

Notice that model_meta property is "lazy" and will not update automatically if self._model_meta is already defined. So, if your serving app supports several models that can be chosen via GUI, you should update your model_meta manually by calling self.update_model_meta() at the end of self.load_on_device().

The self._get_obj_class_shape() is a bit tricky. Most serving apps are designed to solve only one task at a time and for this reason, this method is protected. For example, if you inherit from sly.nn.inference.ObjectDetection class, self._get_obj_class_shape() will always return sly.Rectangle shape. But some API allows you to create app that can handle multiple tasks (e.g. YOLOv8, open-mmlab/mmdetection). In this case, the method must be overridden.

def _get_obj_class_shape(self):
    if self.task_type == "object detection":
        return sly.Rectangle
    elif self.task_type == "instance segmentation":
        return sly.Bitmap
    raise ValueError(f"Unknown task type: {self.task_type}")

def load_on_device(
        self,
        model_dir: str,
        device: Literal["cpu", "cuda", "cuda:0", "cuda:1", "cuda:2", "cuda:3"] = "cpu",
    ):
    ####### CUSTOM CODE: model instantiating, downloading weights, loading it on device.
    self.class_names = class_names
    # define `task_type` for chosen model
    # overwrite `self.task_type` attribute
    self.task_type = "object detection"  # or instance segmentation
    self.update_model_meta()

If for some reason this functionality is not enough for your serving app, you can freely define all needed attributes as well as overwrite self._model_meta right inside the load_on_device() method. For example, it is currently impossible to construct ObjClass for sly.GraphNodes because geometry_config should be passed into constructor.

def load_on_device(
        self,
        model_dir: str,
        device: Literal["cpu", "cuda", "cuda:0", "cuda:1", "cuda:2", "cuda:3"] = "cpu",
    ):
    ####### CUSTOM CODE: model instantiating, downloading weights, loading it on device.
    self.class_names = class_names
    obj_classes = [sly.ObjClass(name, sly.GraphNodes, geometry_config=self.keypoints_template) for name in self.class_names]
    # Overwrite `_model_meta`, so there is no need to call `update_model_meta` after
    self._model_meta = sly.ProjectMeta(obj_classes=sly.ObjClassCollection(obj_classes))

If a user wants to change a parameter value before the inference, they can do so. For example, if they use the app to label a project using your NN model, they will see these parameters in the Inference settings > Additional settings section.

basic mode has the same problem as object detection neural networks without non-maximum suppression post-processing. Many labels from different image parts can collide, overlap and be split. We support the option to improve basic mode and add the to predicted labels.

You can refer to the as an example of using advanced sliding window mode.

Class table formed using Model Meta; can be displayed in every serving app with GUI

More information about model meta can be found .

🔥
Integrate Instance Segmentation model repository
Apply NN to Images Project
NMS post-processing
Serve YOLOv5 app repository
in this section