# ocr_accuracy

Python API: `luxonis_train.attached_modules.metrics.ocr_accuracy`

Exact-match accuracy of OCR predictions.

The metric also gives the share of the texts with exactly one and with exactly two wrong characters.

## Classes

### OCRAccuracy

Share of OCR texts with zero, one, or two wrong characters.

 * `Inputs:`: * `predictions` (`Tensor`): [B, T, C] logits
    * `target` (`Tensor`): [B, S] character codes, `0`-padded
 * `Outputs:`: * `Tensor`: scalar, the share of exact matches
    * `rank_0`, `rank_1`, `rank_2` (`Tensor`): scalar, the share of texts with exactly `0`, `1`, or `2` wrong characters
 * `Formula:`: The metric decodes each prediction greedily. It takes the class with the highest logit at each step and merges each
   run of equal classes into one class. It then removes `blank_class` and pads the result with `0` to T positions. The `encoder`
   of the node converts the target characters to class indices. The metric pads a shorter target with `blank_class` and crops a
   longer target to T positions. It compares the two sequences position by position. A text with e ≤ 2 different positions counts
   for `rank_e`. A text with more counts for no rank.

> **References**
> * Source: This project.
 * License: Apache-2.0 (this project)

> **Notes**
> * The comparison is not an edit distance. A missing character shifts the characters after it, and each shifted position counts as wrong.
 * The characters of a target after position T do not count.
 * The encoder of
   [OCRCTCHead](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/nodes/heads/ocr_ctc_head.md)
   uses the class `0` for the blank and for the padding. With another `blank_class`, the decoding keeps the blanks of the head.
   Each position that the metric adds to a target also counts as wrong.

> **Example**
> Attached to a `OCRCTCHead` in `model.nodes`:

```yaml
- name: OCRCTCHead
  inputs: [SVTRNeck]
  metrics:
    - name: OCRAccuracy
```

 * `Compatible with:`: * Used by:
   [OCRRecognitionModel](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/config/predefined_models/ocr_recognition/v1/model.md)
    * Nodes:
      [OCRCTCHead](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/nodes/heads/ocr_ctc_head.md)

#### Methods

##### init

```python
def __init__(blank_class: int = 0, **kwargs):
```

Initialize the metric.

The node must be an
[OCRCTCHead](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/nodes/heads/ocr_ctc_head.md).
Otherwise
[BaseAttachedModule](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/attached_modules/base_attached_module.md)
raises
[IncompatibleError](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/utils/exceptions.md).

Parameters

 * `blank_class` (`int`): The class index of the CTC blank. The decoding removes it from the predictions, and the metric pads the
   targets with it. Keep `0`, the blank of the encoder of
   [OCRCTCHead](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/nodes/heads/ocr_ctc_head.md).
 * `**kwargs`: Keyword arguments forwarded to
   [BaseMetric](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/attached_modules/metrics/base_metric.md),
   such as `node`.

##### compute

```python
def compute(self) -> tuple[Tensor, dict[str, Tensor]]:
```

Divide the counts by the number of texts.

> **Example**
> The alphabet `["a", "b"]` gives the classes blank, `"a"`, and `"b"`. The first text decodes to `"ab"`. The second text decodes to `"aa"`, with one wrong character:

```pycon
>>> import torch
>>> import torch.nn.functional as F
>>> from torch import Size
>>> from luxonis_train.nodes import OCRCTCHead
>>> head = OCRCTCHead(
...     alphabet=["a", "b"],
...     input_shapes=[{"features": [Size([1, 8, 1, 4])]}],
... )
>>> metric = OCRAccuracy(node=head)
>>> classes = torch.tensor([[1, 1, 2, 0], [1, 0, 1, 0]])
>>> text = torch.tensor([[97, 98], [97, 98]])
>>> metric.update(F.one_hot(classes, 3).float(), text)
>>> accuracy, ranks = metric.compute()
>>> accuracy.item(), ranks["rank_1"].item()
(0.5, 0.5)
```

Returns

 * `tuple[Tensor, dict[str, Tensor]]`: The share of exact matches, and a dictionary with the keys `"rank_0"`, `"rank_1"`, and
   `"rank_2"`. `"rank_k"` is the share of the texts with exactly `k` wrong characters. All values are scalars. They are `NaN` when
   no
   [update](https://docs.luxonis.com/software-v3/ai-inference/model-source/training/luxonis-train/luxonis-train-api-reference/attached_modules/metrics/ocr_accuracy.md)
   ran since the last `reset`.

##### update

```python
def update(predictions: Tensor, target: Tensor):
```

Decode one batch and add its counts to the states.

The method adds the number of texts with exactly `0`, `1`, and `2` wrong characters to `rank_0`, `rank_1`, and `rank_2`. It adds
the batch size to `total`. The class docstring describes the decoding and the comparison.

Parameters

 * `predictions` (`Tensor`): The logits of shape `[B, T, C]`, the main output of the node.
 * `target` (`Tensor`): The character codes of the texts, of shape `[B, S]`, padded with `0`. It is the `metadata/text` label of
   the task.

#### Attributes

##### node

##### rank_0

##### rank_1

##### rank_2

##### supported_tasks

##### total
