Api application

31 minutes

1. What is the API? What can it be used for?

ATrISys is built on Cytomine and provides a set of HTTP APIs that allow users to automate the platform through programming, including:

  • Listing projects, images, and annotations
  • Batch downloading annotations (for AI training datasets)
  • Batch creating / updating annotations
  • Integrating with your own analysis programs or pipelines

Below is a basic introduction to API operations.
For detailed usage, please refer to the Cytomine official documentation.


2. Information Required for API Usage

Before using the API, you need the following four pieces of information:

  1. Host (server address): https://www.asynapse.xyz
  2. Public key / Private key: found under User Account → API Keys
  3. Project ID: Found in the project URL as project/1234 (1234 = Project ID)

3. Method 1: Using curl for Testing

3.1 Test Connection
curl -X GET \
"{HOST}/api/currentuser.json" \
-H "X-Auth-Token: {PUBLIC_KEY}" \
-H "X-Auth-Secret: {PRIVATE_KEY}"

Replace with:

  • {HOST}:https://www.asynapse.xyz/
  • {PUBLIC_KEY}:your public key
  • {PRIVATE_KEY}:your private key

If successful, the API returns a JSON containing:

  • User ID
  • Username
  • Email, etc.
3.2 List All Projects
curl -X GET \
"{HOST}/api/project.json" \
-H "X-Auth-Token: {PUBLIC_KEY}" \
-H "X-Auth-Secret: {PRIVATE_KEY}"

The response will be a JSON array, where each object represents a project:
{"id": 123, "name": "Breast Cancer Project", ...}
You can obtain the Project ID from here.

4. Method 2: Connecting with Python

4.1 Install Required Packages

The official Cytomine Python client is used:

pip install cytomine
4.2 Connect to Cytomine
from cytomine import Cytomine

HOST = "https://www.asynapse.xyz"
PUBLIC_KEY = "YOUR_PUBLIC_KEY"
PRIVATE_KEY = "YOUR_PRIVATE_KEY"

with Cytomine(HOST, PUBLIC_KEY, PRIVATE_KEY) as cyto:
    print("Connected")
Get Project Information
from cytomine.models import Project

project = Project().fetch(PROJECT_ID)

print(project.name, project.ontology)

The returned information includes:

  • Project name
  • Ontology ID
  • Creation date
  • Number of images, etc
4.4 Get Ontology Terms
from cytomine.models import Ontology, TermCollection

ontology = Ontology().fetch(project.ontology)
terms = TermCollection().fetch_with_filter("ontology", ontology.id)

for t in terms:
    print(t.id, t.name)

Each term represents an annotation category, such as:

  • Tumor
  • Stroma
  • Lymphocyte
4.5 List Project Images
from cytomine.models import ImageInstanceCollection

images = ImageInstanceCollection().fetch_with_filter("project", PROJECT_ID)
print("Number of images:", len(images))

for img in images:
    print(img.id, img.filename, img.width, img.height)
4.6 Retrieve All Annotations of an Image
from cytomine.models import AnnotationCollection

image_id = 12345
W, H = img.width, img.height
MSK_DIR = ""                               # output dir
GEO_DIR = ""                               # output dir
SAVE_WHOLE_MASK = True        
SAVE_PER_IMAGE_GEOJSON = True 

anns = AnnotationCollection(image=image_id, showWKT=True, showTerm=True)
anns.fetch()

mask_img = None

if SAVE_WHOLE_MASK:
    mask_img = Image.new("L", (W, H), 0)
    drawer = ImageDraw.Draw(mask_img, "L")

if SAVE_PER_IMAGE_GEOJSON:
    geo = {
        "type": "FeatureCollection",
        "features": []
    }

for a in anns:
    print(a.id, a.location, a.term)
    wkt_str = a.location
    try:
        poly = wkt.loads(wkt_str)
    except Exception:
        continue

    term_names = []
    if getattr(a, "term", None):
        term_id = a.term[0]
        tname = next((t.name for t in terms if t.id == term_id), "Unlabeled")
        term_names.append(tname)
    else:
        tname = "Unlabeled"

    class_id = class_name_to_id.get(tname, 255)

    if SAVE_PER_IMAGE_GEOJSON:
        feature = {
            "type": "Feature",
            "properties": {
                "image_id": image_id,
                "annotation_id": a.id,
                "class_id": class_id,
                "class_name": tname,
            },
            "geometry": mapping(poly)
        }
        geo["features"].append(feature)

    if SAVE_WHOLE_MASK:
        def draw_polygon(geom):
            if geom.geom_type == "Polygon":
                exterior = [(x, y) for x, y in np.array(geom.exterior.coords)]
                interiors = [
                    [(x, y) for x, y in np.array(ring.coords)]
                    for ring in geom.interiors
                ]
                drawer.polygon(exterior, fill=int(class_id))
                for hole in interiors:
                    drawer.polygon(hole, fill=0)
            elif geom.geom_type == "MultiPolygon":
                for g in geom.geoms:
                    draw_polygon(g)

        draw_polygon(poly)

# === 輸出區 ===
if SAVE_PER_IMAGE_GEOJSON:
    with open(os.path.join(GEO_DIR, f"{image_id}.geojson"), "w", encoding="utf-8") as jf:
        json.dump(geo, jf, ensure_ascii=False)

if SAVE_WHOLE_MASK:
    mask_img.save(os.path.join(MSK_DIR, f"{image_id}_mask.png"))
4.7 Download Image (Original WSI Region)
win_url = f"{HOST}/api/imageinstance/{image_id}/window-{W}-{H}-0-0.jpg?zoom=0"
r = cyto._session.get(win_url)
open("image.jpg","wb").write(r.content)

API Security Notes

When using the ATrISys/Cytomine API, all permissions follow the user’s original permissions on the platform. The API cannot access any data the user is not authorized to view via the web interface. API keys and private keys are available only under individual user accounts—please keep them confidential and never share them.

Next Post