Skip to content

Sample document schema

Every .medh5 file carries a JSON document at /meta describing the subject, the timepoints, the label set, the provenance and the curation records. This is its schema.

The file's arrays — images, masks, boxes, transforms — are HDF5 datasets and are not described here; see Storage for the layout and the specification for what each object means.

import h5py, json

with h5py.File("case_0001.medh5") as f:
    doc = json.loads(f["meta"][()])
    doc["identity"]["subject_id"]

Validation against this schema is E005, and is what --level structural checks. jsonschema is a core dependency, so every install checks it — and every commit() refuses a document that fails it:

medh5 validate case_0001.medh5 --level structural

The tables below are generated from the schema itself at build time.

The document

Property Type Required Constraints Description
identity identity yes
timepoints array of timepoint yes minItems 1 Ordered declaration of the sample's observation occasions. See docs/spec/medh5-1.0.md §3.7.
cohort cohort
label_set labelSet
provenance provenance
quality object each value: qualityRecord Quality records keyed by the value of an annotation's quality attribute.
splits array of splitClaim
acquisition object each value: object Acquisition parameters keyed by image id. Keys SHOULD follow DICOM keyword names.
deidentification deidentification
extra object Free-form writer data. Third-party extensions SHOULD use a reverse-DNS namespace key.

The document itself is closed: additionalProperties is false, so a key the schema does not name is a schema failure (E005) rather than a silently ignored extension. Use extra for anything the schema does not define.

Not every object is closed. identity, cohort set additionalProperties: true, so site- or study-specific keys may be added there directly.

quality, acquisition, extra are open maps: the keys are yours. Where a value schema is given it still applies; where additionalProperties is omitted entirely, as on extra, the values are unconstrained too.

Definitions

19 shared definitions, referenced by $ref above and by each other.

id

Type Constraints
string pattern ^[A-Za-z0-9_.-]{1,128}$

timestamp

Type Constraints
string format date-time

classId

Type Constraints
integer minimum 1; maximum 65534

identity

Subject-scoped identity. Per-occasion identifiers (study_uid, series_uids, dates, age) belong to the timepoint entry, not here.

Open: accepts keys beyond those listed below.

Property Type Required Constraints Description
sample_id string yes minLength 1
subject_id string yes minLength 1 Pseudonymised subject key. A sample never spans subjects, so assigning whole files to splits is subject-safe.
sex string or null F, M, O, unknown, null
laterality string or null left, right, bilateral, null
bodypart string

timepoint

Property Type Required Constraints Description
id id yes Referenced by grid timepoint attributes and annotation timepoints attributes.
index integer yes minimum 0 0-based acquisition order; dense and strictly increasing with time.
label string
date string Shifted per deidentification.date_shift_days.
days_from_baseline number Interval from index 0. Survives date shifting; prefer this over date in models.
study_uid string
series_uids object each value: string Image id -> pseudonymised source series identifier.
subject_age_years number minimum 0
description string

cohort

Open: accepts keys beyond those listed below.

Property Type Required Constraints Description
dataset_id string
site_id string
scanner_id string
group_id string Split grouping key; defaults to identity.subject_id.
acquisition_protocol string

labelSet

Property Type Required Constraints Description
id string yes minLength 1
version string yes minLength 1
sha256 string when form is ref pattern ^[0-9a-f]{64}$
uri string when form is ref
form yes inline, ref
classes array of labelClass when form is inline
relations array of relation
skeletons array of skeleton

labelClass

Property Type Required Constraints Description
id classId yes
key string yes pattern ^[a-z0-9][a-z0-9_]*$
name string yes minLength 1
parents array of classId is_a parents. Zero or more: the hierarchy is a DAG.
category string
laterality string or null left, right, bilateral, null
color array of integer minItems 4; maxItems 4; each item: minimum 0; maximum 255
codes array of ontologyCode
properties object

ontologyCode

Property Type Required Constraints Description
system string yes
code string yes
name string

relation

Property Type Required Constraints Description
subject classId yes
predicate string yes
object classId yes

skeleton

Property Type Required Constraints Description
id id yes
keypoints array of classId yes
edges array of array of classId yes each item: minItems 2; maxItems 2

provenance

Property Type Required Constraints Description
agents array of agent yes
activities array of activity yes

agent

Property Type Required Constraints Description
id id yes
type yes person, software, organization
name string yes minLength 1 MUST NOT be a direct identifier of a natural person; use a pseudonym.
version string
role annotator, reviewer, arbiter, converter, predictor, curator, other
qualification string
organization id

activity

Property Type Required Constraints Description
id id yes
type yes import, annotate, review, predict, resample, register, derive, deidentify, transcode, other
agent id yes
started timestamp
ended timestamp
tool string
inputs array of string HDF5 object paths, or external URIs for out-of-file sources.
outputs array of string
params object

qualityRecord

Property Type Required Constraints Description
status yes draft, submitted, reviewed, approved, rejected, deprecated
confidence number minimum 0; maximum 1
reviewed_by array of id
agreement array of agreement
issues array of issue
edit_effort_s number minimum 0

agreement

Property Type Required Constraints Description
metric string yes
value number yes
against string yes HDF5 path of the annotation compared against.
per_class object keys matching ^[0-9]{1,5}$: number; no other keys

issue

Property Type Required Constraints Description
code string yes
severity yes info, warning, error
class_ids array of classId
note string

splitClaim

Property Type Required Constraints Description
set_id string yes minLength 1
partition string yes
fold integer minimum 0
assigned_by string
assigned_at timestamp
manifest_sha256 string pattern ^[0-9a-f]{64}$ Digest of the authoritative dataset manifest; lets readers detect stale claims.

deidentification

Property Type Required Constraints Description
method string yes
profile string
date_shift_days integer
id_mapping external, irreversible, none
performed_by id
date timestamp
burned_in_annotation_checked boolean

The schema itself

Published verbatim at medh5-sample-1.0.schema.json, and shipped inside the package.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://github.com/XwK-P/medh5/schemas/medh5-sample-1.0.schema.json",
  "title": "MEDH5 1.0 sample document",
  "description": "JSON document stored in the scalar string dataset `meta` at a MEDH5 sample root. See docs/spec/medh5-1.0.md §2.4.",
  "type": "object",
  "required": ["identity", "timepoints"],
  "additionalProperties": false,
  "properties": {
    "identity": { "$ref": "#/$defs/identity" },
    "timepoints": {
      "type": "array",
      "description": "Ordered declaration of the sample's observation occasions. See docs/spec/medh5-1.0.md §3.7.",
      "minItems": 1,
      "items": { "$ref": "#/$defs/timepoint" }
    },
    "cohort": { "$ref": "#/$defs/cohort" },
    "label_set": { "$ref": "#/$defs/labelSet" },
    "provenance": { "$ref": "#/$defs/provenance" },
    "quality": {
      "type": "object",
      "description": "Quality records keyed by the value of an annotation's `quality` attribute.",
      "additionalProperties": { "$ref": "#/$defs/qualityRecord" }
    },
    "splits": { "type": "array", "items": { "$ref": "#/$defs/splitClaim" } },
    "acquisition": {
      "type": "object",
      "description": "Acquisition parameters keyed by image id. Keys SHOULD follow DICOM keyword names.",
      "additionalProperties": { "type": "object" }
    },
    "deidentification": { "$ref": "#/$defs/deidentification" },
    "extra": {
      "type": "object",
      "description": "Free-form writer data. Third-party extensions SHOULD use a reverse-DNS namespace key."
    }
  },

  "$defs": {
    "id": { "type": "string", "pattern": "^[A-Za-z0-9_.-]{1,128}$" },
    "timestamp": { "type": "string", "format": "date-time" },
    "classId": { "type": "integer", "minimum": 1, "maximum": 65534 },

    "identity": {
      "type": "object",
      "required": ["sample_id", "subject_id"],
      "additionalProperties": true,
      "description": "Subject-scoped identity. Per-occasion identifiers (study_uid, series_uids, dates, age) belong to the timepoint entry, not here.",
      "properties": {
        "sample_id": { "type": "string", "minLength": 1 },
        "subject_id": { "type": "string", "minLength": 1,
          "description": "Pseudonymised subject key. A sample never spans subjects, so assigning whole files to splits is subject-safe." },
        "sex": { "type": ["string", "null"], "enum": ["F", "M", "O", "unknown", null] },
        "laterality": { "type": ["string", "null"], "enum": ["left", "right", "bilateral", null] },
        "bodypart": { "type": "string" }
      }
    },

    "timepoint": {
      "type": "object",
      "required": ["id", "index"],
      "additionalProperties": false,
      "properties": {
        "id": { "$ref": "#/$defs/id",
          "description": "Referenced by grid `timepoint` attributes and annotation `timepoints` attributes." },
        "index": { "type": "integer", "minimum": 0,
          "description": "0-based acquisition order; dense and strictly increasing with time." },
        "label": { "type": "string", "examples": ["baseline", "follow_up_3mo", "post_treatment"] },
        "date": { "type": "string", "description": "Shifted per deidentification.date_shift_days." },
        "days_from_baseline": { "type": "number",
          "description": "Interval from index 0. Survives date shifting; prefer this over `date` in models." },
        "study_uid": { "type": "string" },
        "series_uids": { "type": "object", "additionalProperties": { "type": "string" },
          "description": "Image id -> pseudonymised source series identifier." },
        "subject_age_years": { "type": "number", "minimum": 0 },
        "description": { "type": "string" }
      }
    },

    "cohort": {
      "type": "object",
      "additionalProperties": true,
      "properties": {
        "dataset_id": { "type": "string" },
        "site_id": { "type": "string" },
        "scanner_id": { "type": "string" },
        "group_id": { "type": "string", "description": "Split grouping key; defaults to identity.subject_id." },
        "acquisition_protocol": { "type": "string" }
      }
    },

    "labelSet": {
      "type": "object",
      "required": ["id", "version", "form"],
      "additionalProperties": false,
      "properties": {
        "id": { "type": "string", "minLength": 1 },
        "version": { "type": "string", "minLength": 1 },
        "sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
        "uri": { "type": "string" },
        "form": { "enum": ["inline", "ref"] },
        "classes": { "type": "array", "items": { "$ref": "#/$defs/labelClass" } },
        "relations": { "type": "array", "items": { "$ref": "#/$defs/relation" } },
        "skeletons": { "type": "array", "items": { "$ref": "#/$defs/skeleton" } }
      },
      "allOf": [
        { "if": { "properties": { "form": { "const": "inline" } } },
          "then": { "required": ["classes"] } },
        { "if": { "properties": { "form": { "const": "ref" } } },
          "then": { "required": ["uri", "sha256"] } }
      ]
    },

    "labelClass": {
      "type": "object",
      "required": ["id", "key", "name"],
      "additionalProperties": false,
      "properties": {
        "id": { "$ref": "#/$defs/classId" },
        "key": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_]*$" },
        "name": { "type": "string", "minLength": 1 },
        "parents": { "type": "array", "items": { "$ref": "#/$defs/classId" },
          "description": "is_a parents. Zero or more: the hierarchy is a DAG." },
        "category": { "type": "string" },
        "laterality": { "type": ["string", "null"], "enum": ["left", "right", "bilateral", null] },
        "color": { "type": "array", "items": { "type": "integer", "minimum": 0, "maximum": 255 },
                   "minItems": 4, "maxItems": 4 },
        "codes": { "type": "array", "items": { "$ref": "#/$defs/ontologyCode" } },
        "properties": { "type": "object" }
      }
    },

    "ontologyCode": {
      "type": "object",
      "required": ["system", "code"],
      "additionalProperties": false,
      "properties": {
        "system": { "type": "string", "examples": ["SNOMED-CT", "RadLex", "FMA", "UBERON", "ICD-10", "LOINC"] },
        "code": { "type": "string" },
        "name": { "type": "string" }
      }
    },

    "relation": {
      "type": "object",
      "required": ["subject", "predicate", "object"],
      "additionalProperties": false,
      "properties": {
        "subject": { "$ref": "#/$defs/classId" },
        "predicate": { "type": "string", "examples": ["part_of", "adjacent_to", "supplies", "drains"] },
        "object": { "$ref": "#/$defs/classId" }
      }
    },

    "skeleton": {
      "type": "object",
      "required": ["id", "keypoints", "edges"],
      "additionalProperties": false,
      "properties": {
        "id": { "$ref": "#/$defs/id" },
        "keypoints": { "type": "array", "items": { "$ref": "#/$defs/classId" } },
        "edges": { "type": "array",
          "items": { "type": "array", "items": { "$ref": "#/$defs/classId" }, "minItems": 2, "maxItems": 2 } }
      }
    },

    "provenance": {
      "type": "object",
      "required": ["agents", "activities"],
      "additionalProperties": false,
      "properties": {
        "agents": { "type": "array", "items": { "$ref": "#/$defs/agent" } },
        "activities": { "type": "array", "items": { "$ref": "#/$defs/activity" } }
      }
    },

    "agent": {
      "type": "object",
      "required": ["id", "type", "name"],
      "additionalProperties": false,
      "properties": {
        "id": { "$ref": "#/$defs/id" },
        "type": { "enum": ["person", "software", "organization"] },
        "name": { "type": "string", "minLength": 1,
          "description": "MUST NOT be a direct identifier of a natural person; use a pseudonym." },
        "version": { "type": "string" },
        "role": { "enum": ["annotator", "reviewer", "arbiter", "converter", "predictor", "curator", "other"] },
        "qualification": { "type": "string" },
        "organization": { "$ref": "#/$defs/id" }
      }
    },

    "activity": {
      "type": "object",
      "required": ["id", "type", "agent"],
      "additionalProperties": false,
      "properties": {
        "id": { "$ref": "#/$defs/id" },
        "type": { "enum": ["import", "annotate", "review", "predict", "resample", "register",
                           "derive", "deidentify", "transcode", "other"] },
        "agent": { "$ref": "#/$defs/id" },
        "started": { "$ref": "#/$defs/timestamp" },
        "ended": { "$ref": "#/$defs/timestamp" },
        "tool": { "type": "string" },
        "inputs": { "type": "array", "items": { "type": "string" },
          "description": "HDF5 object paths, or external URIs for out-of-file sources." },
        "outputs": { "type": "array", "items": { "type": "string" } },
        "params": { "type": "object" }
      }
    },

    "qualityRecord": {
      "type": "object",
      "required": ["status"],
      "additionalProperties": false,
      "properties": {
        "status": { "enum": ["draft", "submitted", "reviewed", "approved", "rejected", "deprecated"] },
        "confidence": { "type": "number", "minimum": 0, "maximum": 1 },
        "reviewed_by": { "type": "array", "items": { "$ref": "#/$defs/id" } },
        "agreement": { "type": "array", "items": { "$ref": "#/$defs/agreement" } },
        "issues": { "type": "array", "items": { "$ref": "#/$defs/issue" } },
        "edit_effort_s": { "type": "number", "minimum": 0 }
      }
    },

    "agreement": {
      "type": "object",
      "required": ["metric", "value", "against"],
      "additionalProperties": false,
      "properties": {
        "metric": { "type": "string", "examples": ["dice", "iou", "hausdorff95", "surface_dice", "kappa", "tre"] },
        "value": { "type": "number" },
        "against": { "type": "string", "description": "HDF5 path of the annotation compared against." },
        "per_class": { "type": "object",
          "patternProperties": { "^[0-9]{1,5}$": { "type": "number" } },
          "additionalProperties": false }
      }
    },

    "issue": {
      "type": "object",
      "required": ["code", "severity"],
      "additionalProperties": false,
      "properties": {
        "code": { "type": "string" },
        "severity": { "enum": ["info", "warning", "error"] },
        "class_ids": { "type": "array", "items": { "$ref": "#/$defs/classId" } },
        "note": { "type": "string" }
      }
    },

    "splitClaim": {
      "type": "object",
      "required": ["set_id", "partition"],
      "additionalProperties": false,
      "properties": {
        "set_id": { "type": "string", "minLength": 1 },
        "partition": { "type": "string", "examples": ["train", "val", "test", "holdout"] },
        "fold": { "type": "integer", "minimum": 0 },
        "assigned_by": { "type": "string" },
        "assigned_at": { "$ref": "#/$defs/timestamp" },
        "manifest_sha256": { "type": "string", "pattern": "^[0-9a-f]{64}$",
          "description": "Digest of the authoritative dataset manifest; lets readers detect stale claims." }
      }
    },

    "deidentification": {
      "type": "object",
      "required": ["method"],
      "additionalProperties": false,
      "properties": {
        "method": { "type": "string" },
        "profile": { "type": "string", "examples": ["DICOM PS3.15 E.1 basic", "custom"] },
        "date_shift_days": { "type": "integer" },
        "id_mapping": { "enum": ["external", "irreversible", "none"] },
        "performed_by": { "$ref": "#/$defs/id" },
        "date": { "$ref": "#/$defs/timestamp" },
        "burned_in_annotation_checked": { "type": "boolean" }
      }
    }
  }
}