Home

Awesome

QSL: Quick and Simple Labeler

QSL Screenshot

QSL is a simple, open-source media labeling tool that you can use as a Jupyter widget. More information available at https://qsl.robinbay.com. It supports:

Please note that that QSL is still under development and there are likely to be major bugs, breaking changes, etc. Bug reports and contributions are welcome!

Get started by installing using pip install qsl. Label a folder of images and save the labels to JSON using the standalone interface using qsl label labels.json images/*.jpg. Check out the Colab Notebook for an example of how to use the Jupyter Widget.

Examples

Each example below demonstrates different ways to label media using the tool. At the top of each are the arguments used to produce the example.

Images

import qsl

params = dict(
    config={
        "image": [
            {"name": "Location", "multiple": False, "options": [{"name": "Indoor"}, {"name": "Outdoor"}]},
            {"name": "Flags", "multiple": True, "freeform": True},
            {"name": "Type", "multiple": False, "options": [{"name": "Cat"}, {"name": "Dog"}]},
        ],
        "regions": [
            {"name": "Type", "multiple": False, "options": [{"name": "Eye"}, {"name": "Nose"}]}
        ]
    },
    items=[
        {"target": "https://picsum.photos/id/1025/500/500", "defaults": {"image": {"Type": ["Dog"]}}},
    ],
)
qsl.MediaLabeler(**params)

image labeling demo

Videos

import qsl

params = dict(
    config={
        "image": [
            {"name": "Location", "multiple": False, "options": [{"name": "Indoor"}, {"name": "Outdoor"}]},
            {"name": "Flags", "multiple": True, "freeform": True},
        ],
        "regions": [
            {"name": "Type", "multiple": False, "options": [{"name": "Eye"}, {"name": "Nose"}]}
        ]
    },
    items=[
        {
            "target": "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
            "type": "video",
        }
    ],
)
qsl.MediaLabeler(**params)

video labeling demo

Image Batches

import qsl

params = dict(
    config={
        "image": [
            {"name": "Type", "multiple": False, "options": [{"name": "Cat"}, {"name": "Dog"}]},
            {"name": "Location", "multiple": False, "options": [{"name": "Indoor"}, {"name": "Outdoor"}]},
            {"name": "Flags", "multiple": True, "freeform": True},
        ],
        "regions": [
            {"name": "Type", "multiple": False, "options": [{"name": "Eye"}, {"name": "Nose"}]}
        ]
    },
    items=[
        {"target": "https://picsum.photos/id/1025/500/500", "defaults": {"image": {"Type": ["Dog"]}}},
        {"target": "https://picsum.photos/id/1062/500/500", "metadata": {"source": "picsum"}},
        {"target": "https://picsum.photos/id/1074/500/500"},
        {"target": "https://picsum.photos/id/219/500/500"},
        {"target": "https://picsum.photos/id/215/500/500"},
        {"target": "https://picsum.photos/id/216/500/500"},
        {"target": "https://picsum.photos/id/217/500/500"},
        {"target": "https://picsum.photos/id/218/500/500"},
    ],
    batchSize=2
)
qsl.MediaLabeler(**params)

image batch labeling demo

Time Series

import qsl
import numpy as np

x = np.linspace(0, 2 * np.pi, 100)
params = dict(
    config={
        "image": [
            {"name": "Peaks", "multiple": True},
            {"name": "A or B", "freeform": True},
        ]
    },
    items=[
        {
            "target": {
                "plots": [
                    {
                        "x": {"name": "time", "values": x},
                        "y": {
                            "lines": [
                                {
                                    "name": "value",
                                    "values": np.sin(x),
                                    "color": "green",
                                    "dot": {"labelKey": "Peaks"},
                                }
                            ]
                        },
                        "areas": [
                            {
                                "x1": 0,
                                "x2": np.pi,
                                "label": "a",
                                "labelKey": "A or B",
                                "labelVal": "a",
                            },
                            {
                                "x1": np.pi,
                                "x2": 2 * np.pi,
                                "label": "b",
                                "labelKey": "A or B",
                                "labelVal": "b",
                            },
                        ],
                    }
                ],
            },
            "type": "time-series",
        }
    ],
)
qsl.MediaLabeler(**params)

time series labeling demo

Stacked Image

params = dict(
    items=[
        {
            "type": "image-stack",
            "target": {
                "images": [
                    {
                        "name": image["name"],
                        "target": image["filepath"],
                        "transform": cv2.getRotationMatrix2D(
                            center=(0, 0), angle=image["angle"], scale=1
                        ),
                    }
                    for image in qsl.testing.files.ROTATED_TEST_IMAGES
                ]
            },
        }
    ],
    config={
        "image": [
            {
                "name": "Type",
                "multiple": False,
                "options": [{"name": "Cat"}, {"name": "Dog"}],
            },
            {
                "name": "Location",
                "multiple": False,
                "options": [{"name": "Indoor"}, {"name": "Outdoor"}],
            },
            {"name": "Flags", "multiple": True, "freeform": True},
        ],
        "regions": [
            {
                "name": "Type",
                "multiple": False,
                "options": [{"name": "Eye"}, {"name": "Nose"}],
            }
        ],
    },
)
qsl.MediaLabeler(**params)

API

Jupyter Widget

qsl.MediaLabeler accepts the following arguments:

Command Line Application

You can launch the same labeling interface from the command line using qsl label <project-json-file> <...files>. If the project file does not exist, it will be created. The files you provide will be added. If the project file already exists, files that aren't already on the list will be added. You can edit the project file to modify the settings that cannot be changed from within the UI (i.e., allowConfigChange, maxCanvasSize, maxViewHeight, mode, and batchSize).

Development

Make sure you have rustup and wasm-pack installed.

  1. Create a local development environment using make init
  2. Run widget development with live re-building using make develop
  3. Run a Jupyter Lab instance using make lab. Changes to the JavaScript/TypeScript require a full refresh to take effect.