Ango Hub Docs
Open Ango HubContact iMerit
  • Ango Hub Documentation
  • Video Guides
  • Changelog
  • FAQs & Troubleshooting
  • All Keyboard and Mouse Shortcuts
  • Core Concepts
    • Assets
    • Attachments
    • Batches
    • Benchmarks
    • Category Schema (Ontologies)
    • Frame Interpolation
    • Geofencing
    • Idle Time Detection & Time Tracking
    • Instructions
    • Issues
      • Issue Error Codes
    • Label Validation
    • Labeler Performance
    • Labeling
    • Labeling Queue
    • Multiple Classification
    • Notifications
    • Organizations
    • Projects
    • Requeuing
    • Reviewing
    • Review Queue
    • Skipping
    • Stage History
    • Tasks
    • Usage
    • User Roles
    • Workflow
      • Complete
      • Consensus
      • Hold
      • Label
      • Logic
      • Plugin
      • Review
      • Start
      • Webhook
  • Labeling
    • Managing Users in Projects
      • Profile Page
    • Managing the Project Ontology
    • Labeling Editor Interface
      • Audio Labeling Editor
      • Image Labeling Editor
      • Video Labeling Editor
      • DICOM Labeling Editor
      • Medical Labeling Editor
        • 3D Bounding Box
        • Fill Between Slices
        • Island Tools
        • Line (Tape Measure)
        • Smoothing
      • PDF Labeling Editor
      • Text (NER) Labeling Editor
      • LLM Chat Labeling Editor
      • Markdown Labeling Editor
      • 3D Multi-Sensor Fusion Labeling Editor
    • Labeling Classes
      • Tools
        • Bounding Box
        • Brush
        • Entity
        • Message
        • Nested Classifications
        • PCT
        • PDF Tool
        • Point
        • Polygon
        • Polyline
        • Rotated Bounding Box
        • Segmentation
        • Spline
        • Voxel Brush
      • Classification
        • Checkbox
        • Multiple Dropdown
        • Radio
        • Rank
        • Single Dropdown
        • Text
        • Tree Dropdown Tools (Single and Multiple Selection)
      • Relation
        • Single Relation
        • Group Relation
    • Magnetic Lasso
    • Performance & Compatibility Considerations
  • Data
    • Data in Ango Hub
      • Embedding Private Bucket Files in MD Assets
    • Importing Assets
      • Asset Builder
      • Bundled Assets
        • Importing Multiple Images as One Multi-Page Asset
        • Importing Multiple Single-Frame DICOM Files as One Multi-Page Asset
        • Importing multiple DICOM files to be annotated and displayed at once
        • Importing Multiple Single-Frame DICOM Files as a DICOM Series
        • Importing Multiple Markdown Files as One Multi-Page Asset
      • File Explorer
      • Supported Asset File Types & Codecs
      • Importing Cloud (Remote) Assets
      • Importing From Local Machine
      • Creating and Importing LLM Chat Assets
      • Importing Data in the 3D Multi-Sensor Fusion Labeling Tool
      • Bulk Importing Markdown/HTML Assets
      • Importing Attachments during Asset Import
      • Importing Annotations during Asset Import
      • contextData: Adding Extra Data to Assets
      • Importing Reference Images as Overlay
      • Importing Reference Medical Data During Asset Import
    • Importing and Exporting Annotations
      • Importing Annotations
        • Ango Import Format
        • Importing Brush Traces
        • Importing NRRD Annotations
      • Exporting Annotations
        • Ango Export Format
          • Asset
            • Task
              • Tools
              • Classifications
              • Relations
          • Stage History
    • Adding and Managing LLMs
    • Storages
      • Set up a storage integration with Azure
      • Set up a storage integration with AWS S3
      • Set up a storage integration with MinIO and S3-compatible custom storage services
      • Set up a storage integration with GCP (Google Cloud Platform)
      • Set up CORS
      • Validating Storage Integrations
    • Purging Data from Ango Hub
  • Plugins
    • Overview of Plugins in Ango Hub
      • Installing Plugins
      • Plugin Setting Presets
      • Monitoring Plugin Progress
    • First-Party Plugins
      • Ango Export Converter Plugins
      • Asset Converter Plugins
      • Ango to Mask Converter
      • Batch Assignment
      • ChatGPT
      • Column-Agnostic Markdown Generator
      • CSV Export for Classification
      • DALL-E
      • DALL-E (Model Plugin)
      • File Explorer Plugin
      • General Object Detector
      • General Object Segmenter
      • Markdown Generator
      • One-Click Segmentation
      • Open World Object Detection
      • Optical Character Recognition
      • TPT Export
      • YOLO | Instance Segmentation
      • YOLO | Pose Estimation
      • YOLO | Object Detection
      • YOLO | Image Classification
    • Plugin Developer Documentation
      • Export Plugins
      • Batch Model Plugins
      • Model Plugins
      • File Explorer Plugins
      • Markdown Generator Plugins
      • Plugin Logger
      • [WIP] Deploying your Plugin
      • Plugin 'Host' Information
  • SDK
    • SDK Documentation
      • Project Level SDK Functions
        • add_members_to_project
        • assign_batches
        • assign_task
        • create_attachment
        • create_batch
        • create_issue
        • create_label_set
        • create_project
        • delete_issue
        • export
        • exportV3
        • get_assets
        • get_batches
        • get_issues
        • get_metrics
        • get_project
        • get_project_performance
        • get_task
        • get_tasks
        • get_task_history
        • import_labels
        • list_projects
        • requeue_tasks
        • rerun_webhook
        • update_workflow_stages
        • upload_files
        • upload_files_cloud
        • upload_files_with_asset_builder
        • upload_chat_assets
      • Organization Level SDK Functions
        • create_storage
        • delete_organization_invites
        • delete_organization_members
        • delete_storage
        • get_organization_invites
        • get_organization_members
        • get_storages
        • invite_members_to_org
        • update_organization_members_role
    • SDK - Useful Snippets
    • SDK Changelog
  • API
    • API Documentation
  • How-To
    • Add Members
      • Add multiple users to a project
    • Annotate
      • Annotate 3D Point Cloud Files on Ango Hub
      • Perform targeted OCR on images
    • Export Data
      • Automatically send Ango Hub Webhook contents to Google Sheets, Email, Slack, and more with Zapier
      • Download a JSON of your project ontology
      • Download DICOM Segmentation Masks
      • Download your annotations in the COCO, KITTI, or YOLO format
      • Download your Segmentation Masks
      • Get your export as separate JSON files for each asset
    • Manage a Project
      • Get your API Key
      • Get your Organization ID
      • Mute your notifications
      • Open an asset provided the Asset ID
      • Pre-label assets
      • Share a filtered view of the Tasks table with others
      • Transfer project ontologies between projects
      • Transfer project workflows between projects
    • Perform Model Evaluation on Ango Hub
  • Troubleshooting
    • I get a "0 Tasks Labeled" alert when trying to pre-label tasks
    • I get a 'The data couldn't be loaded properly' error when opening certain assets
    • I get a "Unknown Classification" warning when opening a task
  • Feature Availability Status for projects of the 3D Multi-Sensor Fusion type
  • Comparison between QuickServe and Ango Hub
  • Changes from Ango Hub Legacy
  • Video V2 Breaking Changes and Transition
  • Data Access, Storage, and Security
  • Two-Factor Authentication
  • Single Sign-On (SSO) Support
  • Customer Support
  • Ango Hub Status Page
Powered by GitBook
On this page
  • Enable Label Validation
  • Set Up Label Validation
  • Function Parameters
  • Function Returns
  • Testing and Debugging your Validation Code
  • Code Examples
  1. Core Concepts

Label Validation

PreviousIssue Error CodesNextLabeler Performance

Last updated 3 months ago

Warning for validations on projects created before Ango Hub 3.10 (3 Jan 2024)

Prior to Ango Hub 3.10, a toggle in the project settings allowed the project manager to decide whether the validations in the project prevented submission or not. This toggle was global for the entire project, meaning that all validations in the project either prevented submission or not.

Since Ango Hub 3.10, the toggle has been deprecated. Instead, the project manager can now decide whether each validation prevents submission or not by including a boolean named preventSubmission to the error. If true, the annotator will not be able to submit when the error is shown. If false, the annotator will be shown the option to submit the task anyway.

For validation code created before this change, or wherever the preventSubmission boolean is not present, we default to preventing submission.

You can show errors to labelers when they try to submit annotations that don't fit requirements of your choice, and if you so wish, you can prevent them from submitting their label entirely.

Ango Hub provides a fully customizable and programmatic way to validate annotations, allowing you to create JavaScript functions that hook directly into Ango Hub's code and run when annotators attempt to save or submit their annotations.

Label validation is run only when the labeler clicks on the Validate or the Submit button.

Label validation will not run unless you enable the Enable Custom Validation toggle in the Custom Validation section of the project settings:

Enable Label Validation

Navigate to the project where you'd like to enable label validation, then go to the Settings tab and enter the Label Validation section:

Enable Custom Validation: when enabled, validation will be activated for the current project.

In the code area below, you will enter your JavaScript function containing the logic of your label validation. You may click on Validate Code to check for syntax errors and try your code on sample labels.

Set Up Label Validation

In the code area of the Label Validation section, enter a JavaScript function that takes one parameter (we'll call it answers,) and returns a list of dicts with the errors you'd like to show. The function will be run every time an annotator tries to save or submit annotations.

Function Parameters

Ango Hub will pass one parameter to your function, a JSON object which contains all objects, relations, and classifications the annotator is trying to submit.

This is a sample 'answers' parameter you'll receive from Ango Hub. In this case, the user created two PDF areas, created a single relation between them, and replied to a classification with two answers:

{
  "tools": [
    {
      "pdf": {
        "position": {
          "boundingRect": {
            "x1": 157,
            "y1": 166.1374969482422,
            "x2": 200,
            "y2": 226.1374969482422,
            "width": 671.001473820641,
            "height": 670.9999999999999
          },
          "rects": [],
          "pageNumber": 1
        },
        "content": {
          "image": "data:image/png;base64="
        }
      },
      "objectId": "7e0bd6f41a01c78c15ae608",
      "classifications": [],
      "metadata": {
        "createdAt": 1674132659608,
        "createdBy": "614348d554e17400149964b1"
      },
      "schemaId": "801a2b8b8079b50d4541789"
    },
    {
      "pdf": {
        "position": {
          "boundingRect": {
            "x1": 106,
            "y1": 271.1374969482422,
            "x2": 135,
            "y2": 332.1374969482422,
            "width": 671.001473820641,
            "height": 670.9999999999999
          },
          "rects": [],
          "pageNumber": 1
        },
        "content": {
          "image": "data:image/png;base64="
        }
      },
      "objectId": "8083daa5d68a090aca6c738",
      "classifications": [],
      "metadata": {
        "createdAt": 1674132663738,
        "createdBy": "614348d554e17400149964b1"
      },
      "schemaId": "801a2b8b8079b50d4541789"
    }
  ],
  "classifications": [
    {
      "schemaId": "fe5af3c7471de99e3868448",
      "answer": [
        "3b935728b7f43b18b1f7553",
        "cb48de7b8c1a10908b2d747"
      ],
      "page": 0,
      "metadata": {
        "createdAt": 1674132625127,
        "createdBy": "614348d554e17400149964b1",
        "updatedAt": 1674132625364,
        "updatedBy": "614348d554e17400149964b1"
      }
    }
  ],
  "relations": [
    {
      "from": "7e0bd6f41a01c78c15ae608",
      "to": "8083daa5d68a090aca6c738",
      "objectId": "5ccee3e5af7eeb84b59a358",
      "schemaId": "c3ec9fbe6c52567a0dfb479",
      "direction": "straight",
      "metadata": {
        "createdAt": 1674132666358,
        "createdBy": "614348d554e17400149964b1"
      }
    }
  ]
}

Quickly previewing the 'answers' parameter

We developed a fast and efficient way to preview what will be passed to your function based on your needs:

  1. Open the labeling editor and perform a test annotation on Hub with the tools and answers you need.

  2. Without having to save or submit the annotation, from the three-dot menu in the top-right corner, click on Copy Answers:

You will have copied exactly what Ango Hub would have passed as parameter in your validation function based on the labels on the screen.

Function Returns

Your function will need to return a list of dictionaries, containing the ID of the object which is causing the error, an error message string, as well as whether the error should prevent the annotator from submitting the task or not.

This is what a sample return will look like:

[
  {
    objectId: "12345678",
    message: "A point has an X coordinate below 100.",
    preventSubmission: false
  },
  {
    objectId: "12345690",
    message: "Maximum 3 answers allowed.",
    preventSubmission: true
  }
]

Testing and Debugging your Validation Code

From Ango Hub, you can test and debug your function without having to perform labeling and submitting test annotations every time.

  1. From your project dashboard, enter the Settings tab, then the Label Validation section. In the code area, enter your validation function.

  2. To test and debug your code, click on the Validate Code button below the code area:

A dialog will open:

5. Paste the input parameter you have copied in Step 1, and click on Run.

Ango Hub will scan your function for syntax errors, and show them to you if present.

If your function successfully returned errors based on the annotations JSON you've just passed it as parameter, Ango Hub will show them to you below the code area:

By moving back and forth between writing your function and the validation dialog, you can create a quick build-debug workflow allowing you to create and test your functions quickly.

Code Examples

Give an error if a point exists with its X coordinate under 100:

function (answers) {
  let errors = [];

  const points = answers.objects.filter((obj) => obj.tool === "point" && obj["point"][0] < 100);

  points.map((b) => {
    errors.push(
      {
        objectId: b.objectId,
        message: "Point X under 100.",
        preventSubmission: false
      }
    );
  })

  return errors;
}

Give an error if an empty group relation exists:

function (answers) {
  let errors = [];

  const groupRelation = answers.relations.find((r) => r.tool === "group");

  if (groupRelation?.group.length > 0) {
    errors.push({
      objectId: groupRelation.objectId,
      message: "Group relation cannot be empty.",
      preventSubmission: false
    });
  }

  return errors;
}

Give an error if a bounding box is narrower than 100px:

function (answers) {
  let errors = [];

  const bbs = answers.objects.filter((obj) => obj.tool === "bounding-box" && obj["bounding-box"]["width"] < 100);

  bbs.map((b) => {
    errors.push(
      {
        objectId: b.objectId,
        message: "BB is narrower than 100px.",
        preventSubmission: false
      }
    );
  })

  return errors;
}

Give an error if a group relation you specify contains objects with names different from the ones you specify:

function (answers) {
  let errors = [];

  groupRelationToCheck = "TITLE OF GROUP RELATION TO CHECK"

  const acceptableObjectTypes = [
    "TITLE OF ACCEPTABLE OBJECT 1",
    "TITLE OF ACCEPTABLE OBJECT 2"
  ]

  const groupRelations = answers.relations.filter((r) => r.tool === "group" && r.title === groupRelationToCheck);

  groupRelations.forEach(groupRelation => {
    groupRelation.group.forEach(object => {
      if (!acceptableObjectTypes.includes(object.title)) {
        errors.push({
          objectId: groupRelation.objectId,
          message: `A ${groupRelationToCheck} group relation contains a ${object.title} object.`,
          preventSubmission: false
        })
      }
    })
  })

  return errors;
}

Give an error if a group relation you specify contains more than one object from the ones you specify:

function (answers) {
  let errors = [];

  groupRelationToCheck = "TITLE OF GROUP RELATION TO CHECK"

  const acceptableObjectTypes = [
    "TITLE OF ACCEPTABLE OBJECT 1",
    "TITLE OF ACCEPTABLE OBJECT 2"
  ]

  const groupRelations = answers.relations.filter((r) => r.tool === "group" && r.title === groupRelationToCheck);

  groupRelations.forEach(groupRelation => {
    acceptableObjectTypes.forEach(objectType => {
      matchingObjects = groupRelation.group.filter(object => object.title === objectType)
      if (matchingObjects.length > 1) {
        errors.push({
          objectId: groupRelation.objectId,
          message: `A group relation contains more than one ${objectType} object.`,
          preventSubmission: false
        })
      }
    })
  })

  return errors;
}

In tasks with brush tools, return an error if any pixel in the image has not been covered with a brush trace:

function (answers) {
  const data = currentBrushData.data;
  const size = data.length;
  for(let i = 0; i < size; i+=4) {
    if(!data[i] && !data[i+1] && !data[i+2]) {
      return [{message: 'Empty pixel!'}];
    }
  }
  return [];
}

For more on what can be passed in 'answers', we recommend following the steps in the next section. Following that, consulting the page will also help, as it details the format in which annotations are exported, which is the same as what you'll receive as parameter.

Obtain a sample of what will be passed into your function by following the steps .

Ango Annotation Format
in this section
⚠️