# Digital Humanities in Potato: IIIF, Web Annotation, PAGE XML, TEI and Zooniverse

Source: https://www.potatoannotator.com/docs/guides/digital-humanities

**Potato 2.12.0 reads and writes four interchange formats used in digital humanities: IIIF manifests, W3C Web Annotation, PAGE XML and TEI. Each importer builds a runnable project from a library manifest, an annotation collection, a Transkribus or eScriptorium export, or a TEI edition, and each exporter writes the corrected work back to the format it came from. A Zooniverse workflow and its classifications import the same way.**

| Format | Import | Export | Typical source |
|---|---|---|---|
| [IIIF](https://en.wikipedia.org/wiki/International_Image_Interoperability_Framework) Presentation API 3 (and 2.x) manifests | `-f iiif` | `-f iiif_annotations` | Library and museum image collections, Mirador, Universal Viewer |
| [W3C Web Annotation](https://www.w3.org/TR/annotation-model/) (JSON-LD) | `-f web_annotation` | `-f web_annotation` | Annotorious, Mirador, Hypothesis |
| PAGE XML | `-f page_xml` | `-f page_xml` | Transkribus, eScriptorium, OCR-D |
| [TEI](https://en.wikipedia.org/wiki/Text_Encoding_Initiative) P5 | `-f tei` | `-f tei` | Scholarly editions, corpora |

The source's annotations come in as pre-annotations, which an annotator sees as a starting point and which are stored only once someone saves.

```bash
potato import -i <file or folder> -o <project folder> [-f <format>]
python potato/flask_server.py start <project folder>/config.yaml -p 8000
python -m potato.export -c <project folder>/config.yaml -f <format> -o <output folder>
```

**Local files are copied only from the input folder.** An importer copies a local image or text into the project's `media/` folder only when the file is inside the folder you import, or a folder you name with `--image-dir` or `--texts`. Any other reference, such as an absolute path, a `file://` URI, a `../` path or a symlink that leads out of the folder, is skipped with a warning, because the server publishes `media/`. Move the file into the input folder and import again.

## IIIF manifests

```bash
potato import -f iiif -i manifest.json -o book-project
potato import -f iiif -i https://example.org/iiif/book1/manifest.json -o book-project
```

The input is a manifest file, a folder holding one, or an http(s) URL. A URL is the only input that makes the importer use the network: it fetches the manifest and any annotation pages the manifest references by URL. For a local manifest, referenced pages are read from files of the same name beside it.

Each Canvas becomes one item. The image is the painting annotation's body `id`, or, when the body has no usable `id` or `--iiif-size` is given, a request to the body's Image service at that size (default `!2000,2000`). The manifest and canvas labels and `metadata` are shown above the image. Annotation pages on the canvas become shapes on an `image_annotation` scheme named `regions`, with their labels taken from the annotations' tags. Canvas coordinates are converted, so a canvas whose size differs from the image's pixel size still lines up. Presentation 2.x manifests are read on a best-effort basis.

```bash
python -m potato.export -c book-project/config.yaml -f iiif_annotations -o out \
    --option base_url=https://example.org/iiif-out/
```

The exporter writes one annotation page per annotated canvas to `out/annotations/<canvas>.json`, and a copy of the manifest to `out/manifest.json` with a reference to each page, in the manifest's own version. Open the copied manifest in Mirador or Universal Viewer to see the annotations. Boxes become `#xywh=` fragments, polygons, polylines, ellipses and freehand shapes become SVG selectors, landmarks become point selectors, and question answers become textual bodies on the canvas, each with the Potato user who drew it as `creator`. `--option embed=true` puts the annotation pages inside the manifest copy, so nothing else needs hosting. Masks have no IIIF form and are left out.

The example `examples/image/iiif-manifest/` has two generated folios, one with annotations already on it.

## W3C Web Annotation

```bash
potato import -f web_annotation -i annotations.jsonld -o project --texts documents/
```

The importer accepts an `AnnotationCollection`, an `AnnotationPage`, a list of annotations or pages, a single `Annotation`, the Hypothesis API search response, and IIIF 2.x `sc:AnnotationList` files.

| Target | Becomes |
|---|---|
| `TextQuoteSelector` or `TextPositionSelector` | A span on a `span` scheme |
| `FragmentSelector` `xywh=`, or a `#xywh=` fragment on the target | A box on an `image_annotation` scheme |
| `SvgSelector` with a polygon, polyline, rect, circle, ellipse or path | A polygon, polyline, box or ellipse |
| `PointSelector` | A landmark |
| The whole resource | A `radio` or `multiselect` answer from `tagging` and `classifying` bodies, or a `text` answer from `describing` and `commenting` bodies |

A span needs the text it was drawn on, which the importer looks for in `--texts`: a folder of text files named after the last path segment of each target, or a JSONL or JSON file of rows with `text` and one of `source`, `id`, `url`, `uri` or `iri`. When a position and a quote disagree, the span is found again by its quote, with `prefix` and `suffix` choosing between repeated occurrences. Pixel regions need the image size, which comes from `--texts` rows, the image file, or an SVG selector's `viewBox`. Pixel regions on an image of unknown size are left out with a warning rather than placed at a guessed scale.

Each annotation's `creator` becomes that person's answer, and with `--keep-annotators` those answers are written as each person's saved work. A creator of type `Software` is recorded as the model that made the suggestion. The `web_annotation` export writes `web_annotations.jsonld`, one `AnnotationCollection` of every span, region and answer.

## PAGE XML and HTR line correction

```bash
potato import -f page_xml -i transkribus-export/ -o htr-project                 # lines (default)
potato import -f page_xml -i transkribus-export/ -o layout-project --page-xml-mode regions
```

The input is one `.xml` file or a folder, searched recursively, so a Transkribus export with its `page/` subfolder works. Each page's image is looked for beside the XML, one folder up and in `--image-dir`. The 2013-07-15 and 2019-07-15 schema versions are read.

**Lines mode**, the default, makes one item per `TextLine`, in the page's reading order. The annotator sees the line cropped out of the page with the `image_region` display and corrects the recognised text in a `text_edit` scheme, which starts from the line's text and shows a word diff against it. When the recogniser stored a confidence, it comes across as `htr_confidence`. **Regions mode** makes one item per page, with every region and text line as a polygon on an `image_annotation` scheme named `layout`, labelled by region type.

`image_region` shows one rectangle of a larger image. The browser downloads the page image once and crops it, so moving between the lines of one page does not download it again:

```yaml
instance_display:
  fields:
    - key: line_image
      type: image_region
      label: Line
      padding: 12
```

```json
{"image": "/media/page-001.png", "width": 2480, "height": 3508,
 "region": [412, 980, 1630, 96],
 "polygon": [[412, 980], [2042, 984], [2040, 1076], [410, 1072]]}
```

| Option | Default | Meaning |
|---|---|---|
| `padding` | `8` | Image pixels shown around the region on each side |
| `outline` | `true` | Draws the polygon or rectangle over the crop |
| `show_page` | `true` | Adds a "Show the whole image" disclosure with the region outlined on the full page |
| `max_scale` | `2.0` | The crop is never drawn wider than this many times its size in pixels |
| `alt_text` | `""` | Accessible name of the crop |

```bash
python -m potato.export -c htr-project/config.yaml -f page_xml -o out
```

The exporter opens the project's copy of each file, changes only what was corrected, and writes everything else back as it was, including metadata, reading order and the schema version. A corrected line gets its new text, and the recogniser's confidence is removed. In layout mode, moved shapes update their element's coordinates, new shapes become new lines or regions, and deleted ones are removed with their reading-order reference. Lines nobody annotated are left unchanged.

The example `examples/text/htr-line-correction/` has a generated page image with five lines and a PAGE XML file whose recognised text has errors to correct.

## TEI P5

```bash
potato import -f tei -i edition/ -o tei-project              # one item per block
potato import -f tei -i letters.xml -o tei-project --tei-unit div
```

`--tei-unit` sets what counts as one item: `p` (paragraphs, heads and line groups, the default), `div`, `s` or `l`. Inline `<persName>`, `<placeName>`, `<orgName>`, `<geogName>`, `<name>`, `<date>` and `<time>` become spans labelled with the element name, and `<rs type="ship">` is labelled `ship`. The item text is a reading text: `<choice>` takes the regularised, corrected or expanded form, `<del>`, `<note>` and `<fw>` are left out, `<lb/>` is a newline, and `<gap/>` is `[...]`.

The `tei` export writes one TEI document per source file, wrapping each span in its label when the label is a TEI name element and in `<rs type="LABEL">` otherwise. A span that crosses another cannot nest in XML, so it goes into `<standOff><listAnnotation>` as a `string-range` annotation, and the export summary reports how many did. The exporter builds a new document from the project and does not edit the source file, so header metadata other than the title is not carried over.

## Zooniverse

`potato import` reads a [Zooniverse](https://www.zooniverse.org/) project's exports: the workflow's tasks become annotation schemes, the subjects become items, and the volunteers' classifications can come across as their saved work. It suits running a workflow privately, with unpublished data, a lab team or paid annotators, or computing agreement on an existing project's classifications.

From the project's **Lab → Data Exports**, request the workflow, classification and subject exports, and put the three CSV files in one folder:

```bash
potato import -i zooniverse-exports/ -o my-project --keep-annotators
```

| Zooniverse task | Potato |
|---|---|
| Question (single answer) | `radio` |
| Question (multiple answers), shortcut | `multiselect` |
| Text | `text` |
| Slider | `slider` |
| Dropdown (simple) | `select` |
| Drawing: point, rectangle, polygon, line, circle or ellipse, freehand | `image_annotation` with the matching tool |
| Highlighter | `span`, when the subject's text is in its metadata |
| Combo | Its tasks, each as above |
| Branching | `display_logic` on the tasks reached through that answer |

Zooniverse shows tasks one after another, and Potato shows them on one page, revealing each one when the answers that lead to it are given. One workflow version is imported, by default the most recent major version the classifications use. Survey tasks (species identification with follow-up questions), drawing sub-task questions, transcription tasks and classifications covering several subjects are counted in the output and not imported. Volunteers who were not logged in appear as `anon_<id>`, so one person may show up as several.

## Further Reading

- [Importing Projects](/docs/tools/importing-projects) - Every format `potato import` reads
- [Image Annotation](/docs/annotation-types/image-annotation) - Boxes, polygons and landmarks
- [Camera Traps](/docs/guides/camera-traps) - Citizen-science image review with detector output
- [Inter-Annotator Agreement](/docs/guides/inter-annotator-agreement) - Agreement on imported volunteer classifications

For implementation details, see the source documentation on [digital humanities formats](https://github.com/davidjurgens/potato/blob/master/docs/guides/digital-humanities.md) and [Zooniverse](https://github.com/davidjurgens/potato/blob/master/docs/guides/coming-from-zooniverse.md).
