Examples of Project Parameters

Specifying Image Annotation Parameters at the Project Level

Project Creation

First things first, let's create a Project with some default parameters like geometries, instruction, and annotation_attributes

# Create a Project with Parameters

import scaleapi
from scaleapi.tasks import TaskType

client = scaleapi.ScaleClient("SCALE_API_KEY")

project = client.create_project(
  project_name = "Project_Param_Example",
  task_type = TaskType.ImageAnnotation,
  params = {
    "instruction": "Please label the kittens", 
    "geometries": {
      "box": {
        "objects_to_annotate": ["Kittens", "Puppies"]
      }
    },
    "annotation_attributes": {
      "happiness_rating":  {
        "type": "category",
        "description": "How happy is this animal?",
        "choices": [
          "Jumping for Joy",
          "Wagging Tail",
          "Content",
          "Less than happy"
        ],
        "allow_multiple": True
      }
    }
  }
)

Project param_history

Next, let's see what our project looks like when we retrieve it:

import json
project = client.get_project(project_name = "Project_Param_Example")
print(json.dumps(project.as_dict(), indent=2))

# {
#   "type": "imageannotation",
#   "created_at": "2021-06-25T19:51:37.643Z",
#   "param_history": [
#     {
#       "created_at": "2021-06-25T19:51:37.769Z",
#       "instruction": "Please label the kittens",
#       "version": 0,
#       "geometries": {
#         "box": {
#           "objects_to_annotate": [
#             "Kittens",
#             "Puppies"
#           ]
#         }
#       },
#       "annotation_attributes": {
#         "happiness_rating": {
#           "type": "category",
#           "description": "How happy is this animal?",
#           "choices": [
#             "Jumping for Joy",
#             "Wagging Tail",
#             "Content",
#             "Less than happy"
#           ],
#           "allow_multiple": true
#         }
#       }
#     }
#   ],
#   "name": "Project_Param_Example",
#   "pinned": false,
#   "archived": false
# }

Notice we have a param_history object, an array of all project versions to date with some nice details about when it was created and it's version number.

Task Creation

Next, let's create a task in this project and make sure it inherits everything correctly.

from scaleapi.tasks import TaskType
import json

payload = dict(
    project = "Project_Param_Example",
    attachment = "http://i.imgur.com/v4cBreD.jpg",
    unique_id = "s3://bucket/image1.jpg"
)

task = client.create_task(TaskType.ImageAnnotation, **payload)
print(json.dumps(task.as_dict(), indent=2))

# {
#   "task_id": "60d6359cc66fea003b8dd87d",
#   "created_at": "2021-06-25T19:59:24.297Z",
#   "type": "imageannotation",
#   "status": "pending",
#   "instruction": "",
#   "params": {
#     "attachment": "http://i.imgur.com/v4cBreD.jpg",
#     "attachment_type": "image",
#     "annotation_attributes": {
#       "happiness_rating": {
#         "type": "category",
#         "description": "How happy is this animal?",
#         "choices": [
#           "Jumping for Joy",
#           "Wagging Tail",
#           "Content",
#           "Less than happy"
#         ],
#         "allow_multiple": true
#       }
#     },
#     "geometries": {
#       "box": {
#         "objects_to_annotate": [
#           "Kittens",
#           "Puppies"
#         ],
#         "min_width": 0,
#         "min_height": 0,
#         "examples": []
#       }
#     },
#     "with_labels": true
#   },
#   "is_test": false,
#   "urgency": "standard",
#   "metadata": {},
#   "processed_attachments": [],
#   "project": "Project_Param_Example",
#   "callback_url": "https://www.example.com",
#   "unique_id": "s3://bucket/image1.jpg",
#   "project_param_version": 0,
#   "updated_at": "2021-06-25T19:59:24.554Z",
#   "work_started": false
# }

Amazing, we have a task now that has inherited the project-level parameters. Note the "project_param_version": 0 field toward the bottom of the JSON above tieing this task to a specific project version.

Ok, it's been some weeks, and we've realized we actually want to label kittens, puppies, AND cows.

Updating Project Parameters

Let's update our project parameters to accommodate this taxonomy change:

import json

project = client.update_project(
    project_name="Project_Param_Example",
    patch=True, # Leave all the other parameters as they were
    instruction="Please label the kittens, puppies, and cows.",
    geometries={
      "box": {
        "objects_to_annotate": [
          "Kittens",
          "Puppies",
          "Cows"
        ]
      }
    }
)
print(json.dumps(project.as_dict(), indent=2))

# {
#   "type": "imageannotation",
#   "created_at": "2021-06-25T19:51:37.643Z",
#   "param_history": [
#     {
#       "created_at": "2021-06-25T19:51:37.769Z",
#       "instruction": "Please label the kittens",
#       "version": 0,
#       "geometries": {
#         "box": {
#           "objects_to_annotate": [
#             "Kittens",
#             "Puppies"
#           ]
#         }
#       },
#       "annotation_attributes": {
#         "happiness_rating": {
#           "type": "category",
#           "description": "How happy is this animal?",
#           "choices": [
#             "Jumping for Joy",
#             "Wagging Tail",
#             "Content",
#             "Less than happy"
#           ],
#           "allow_multiple": true
#         }
#       }
#     },
#     {
#       "created_at": "2021-06-25T22:33:13.428Z",
#       "instruction": "Please label the kittens, puppies, and cows.",
#       "version": 1,
#       "geometries": {
#         "box": {
#           "objects_to_annotate": [
#             "Kittens",
#             "Puppies",
#             "Cows"
#           ]
#         }
#       },
#       "annotation_attributes": {
#         "happiness_rating": {
#           "type": "category",
#           "description": "How happy is this animal?",
#           "choices": [
#             "Jumping for Joy",
#             "Wagging Tail",
#             "Content",
#             "Less than happy"
#           ],
#           "allow_multiple": true
#         }
#       },
#       "patch": true
#     }
#   ],
#   "name": "Project_Param_Example",
#   "pinned": false,
#   "archived": false
# }

Notice that param_history now has 2 entries in it, version: 0 and version: 1.

Creating Tasks with Updated Params

New tasks submitted to this project will automatically use the latest version of the project.

Let's create one more task to verify that:

from scaleapi.tasks import TaskType
import json

payload = dict(
    project = "Project_Param_Example",
    attachment = "http://i.imgur.com/v4cBreD.jpg",
    unique_id = "s3://bucket/image2.jpg"
)

task = client.create_task(TaskType.ImageAnnotation, **payload)
print(json.dumps(task.as_dict(), indent=2))

# {
#   "task_id": "60d65a9d4eb4cf00338d947a",
#   "created_at": "2021-06-25T22:37:17.701Z",
#   "type": "imageannotation",
#   "status": "pending",
#   "instruction": "",
#   "params": {
#     "attachment": "http://i.imgur.com/v4cBreD.jpg",
#     "attachment_type": "image",
#     "annotation_attributes": {
#       "happiness_rating": {
#         "type": "category",
#         "description": "How happy is this animal?",
#         "choices": [
#           "Jumping for Joy",
#           "Wagging Tail",
#           "Content",
#           "Less than happy"
#         ],
#         "allow_multiple": true
#       }
#     },
#     "geometries": {
#       "box": {
#         "objects_to_annotate": [
#           "Kittens",
#           "Puppies",
#           "Cows"
#         ],
#         "min_width": 0,
#         "min_height": 0,
#         "examples": []
#       }
#     },
#     "with_labels": true
#   },
#   "is_test": false,
#   "urgency": "standard",
#   "metadata": {},
#   "processed_attachments": [],
#   "project": "Project_Param_Example",
#   "callback_url": "https://www.example.com",
#   "unique_id": "s3://bucket/image2.jpg",
#   "project_param_version": 1,
#   "updated_at": "2021-06-25T22:37:18.471Z",
#   "work_started": false
# }

Sure enough, our objects_to_annotate field now supports labeling cows too. 🐮

Other Applications

This same pattern can be applied to any Scale Task Type over many iterations.

To understand the full set of options, you'll want to read through the Update Parameters endpoint.