We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Fix encode_string function
### Bug description

Currently there is no check if the single characters are also available in the given vocabulary.
We need a check for this :) 

TODO's:

- [ ] check that in the function and throw a meaningful exception
- [ ] improve the corresponding test 

discussion:
#926 

### Code snippet to reproduce the bug

```python
from doctr.datasets.utils import encode_string
from doctr.datasets import VOCABS

x = encode_string(input_string='abcDÄÜ', vocab=VOCABS['english'])   # Ä and Ü does not exist in vocab
# raises ValueError: substring not found
```

### Error traceback

```
Traceback (most recent call last):
  File "/home/felix/Desktop/doctr/test.py", line 7, in <module>
    x = encode_string(input_string='abcDÄÜ', vocab=VOCABS['english'])   # Ä and Ü does not exist in vocab
  File "/home/felix/Desktop/doctr/doctr/datasets/utils.py", line 75, in encode_string
    return list(map(vocab.index, input_string))  # type: ignore[arg-type]
ValueError: substring not found
```

### Environment

not need :)

### Deep Learning backend

same

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `doctr/datasets/utils.py`
Content:
```
1 # Copyright (C) 2021-2022, Mindee.
2 
3 # This program is licensed under the Apache License version 2.
4 # See LICENSE or go to <https://www.apache.org/licenses/LICENSE-2.0.txt> for full license details.
5 
6 import string
7 import unicodedata
8 from collections.abc import Sequence
9 from functools import partial
10 from pathlib import Path
11 from typing import Any, Dict, List, Optional
12 from typing import Sequence as SequenceType
13 from typing import Tuple, TypeVar, Union
14 
15 import numpy as np
16 from PIL import Image
17 
18 from doctr.io.image import get_img_shape
19 from doctr.utils.geometry import convert_to_relative_coords, extract_crops, extract_rcrops
20 
21 from .vocabs import VOCABS
22 
23 __all__ = ['translate', 'encode_string', 'decode_sequence', 'encode_sequences']
24 
25 ImageTensor = TypeVar('ImageTensor')
26 
27 
28 def translate(
29     input_string: str,
30     vocab_name: str,
31     unknown_char: str = '■',
32 ) -> str:
33     """Translate a string input in a given vocabulary
34 
35     Args:
36         input_string: input string to translate
37         vocab_name: vocabulary to use (french, latin, ...)
38         unknown_char: unknown character for non-translatable characters
39 
40     Returns:
41         A string translated in a given vocab"""
42 
43     if VOCABS.get(vocab_name) is None:
44         raise KeyError("output vocabulary must be in vocabs dictionnary")
45 
46     translated = ''
47     for char in input_string:
48         if char not in VOCABS[vocab_name]:
49             # we need to translate char into a vocab char
50             if char in string.whitespace:
51                 # remove whitespaces
52                 continue
53             # normalize character if it is not in vocab
54             char = unicodedata.normalize('NFD', char).encode('ascii', 'ignore').decode('ascii')
55             if char == '' or char not in VOCABS[vocab_name]:
56                 # if normalization fails or char still not in vocab, return unknown character)
57                 char = unknown_char
58         translated += char
59     return translated
60 
61 
62 def encode_string(
63     input_string: str,
64     vocab: str,
65 ) -> List[int]:
66     """Given a predefined mapping, encode the string to a sequence of numbers
67 
68     Args:
69         input_string: string to encode
70         vocab: vocabulary (string), the encoding is given by the indexing of the character sequence
71 
72     Returns:
73         A list encoding the input_string"""
74 
75     return list(map(vocab.index, input_string))  # type: ignore[arg-type]
76 
77 
78 def decode_sequence(
79     input_seq: Union[np.array, SequenceType[int]],
80     mapping: str,
81 ) -> str:
82     """Given a predefined mapping, decode the sequence of numbers to a string
83 
84     Args:
85         input_seq: array to decode
86         mapping: vocabulary (string), the encoding is given by the indexing of the character sequence
87 
88     Returns:
89         A string, decoded from input_seq
90     """
91 
92     if not isinstance(input_seq, (Sequence, np.ndarray)):
93         raise TypeError("Invalid sequence type")
94     if isinstance(input_seq, np.ndarray) and (input_seq.dtype != np.int_ or input_seq.max() >= len(mapping)):
95         raise AssertionError("Input must be an array of int, with max less than mapping size")
96 
97     return ''.join(map(mapping.__getitem__, input_seq))
98 
99 
100 def encode_sequences(
101     sequences: List[str],
102     vocab: str,
103     target_size: Optional[int] = None,
104     eos: int = -1,
105     sos: Optional[int] = None,
106     pad: Optional[int] = None,
107     dynamic_seq_length: bool = False,
108     **kwargs: Any,
109 ) -> np.ndarray:
110     """Encode character sequences using a given vocab as mapping
111 
112     Args:
113         sequences: the list of character sequences of size N
114         vocab: the ordered vocab to use for encoding
115         target_size: maximum length of the encoded data
116         eos: encoding of End Of String
117         sos: optional encoding of Start Of String
118         pad: optional encoding for padding. In case of padding, all sequences are followed by 1 EOS then PAD
119         dynamic_seq_length: if `target_size` is specified, uses it as upper bound and enables dynamic sequence size
120 
121     Returns:
122         the padded encoded data as a tensor
123     """
124 
125     if 0 <= eos < len(vocab):
126         raise ValueError("argument 'eos' needs to be outside of vocab possible indices")
127 
128     if not isinstance(target_size, int) or dynamic_seq_length:
129         # Maximum string length + EOS
130         max_length = max(len(w) for w in sequences) + 1
131         if isinstance(sos, int):
132             max_length += 1
133         if isinstance(pad, int):
134             max_length += 1
135         target_size = max_length if not isinstance(target_size, int) else min(max_length, target_size)
136 
137     # Pad all sequences
138     if isinstance(pad, int):  # pad with padding symbol
139         if 0 <= pad < len(vocab):
140             raise ValueError("argument 'pad' needs to be outside of vocab possible indices")
141         # In that case, add EOS at the end of the word before padding
142         default_symbol = pad
143     else:  # pad with eos symbol
144         default_symbol = eos
145     encoded_data = np.full([len(sequences), target_size], default_symbol, dtype=np.int32)
146 
147     # Encode the strings
148     for idx, seq in enumerate(map(partial(encode_string, vocab=vocab), sequences)):
149         if isinstance(pad, int):  # add eos at the end of the sequence
150             seq.append(eos)
151         encoded_data[idx, :min(len(seq), target_size)] = seq[:min(len(seq), target_size)]
152 
153     if isinstance(sos, int):  # place sos symbol at the beginning of each sequence
154         if 0 <= sos < len(vocab):
155             raise ValueError("argument 'sos' needs to be outside of vocab possible indices")
156         encoded_data = np.roll(encoded_data, 1)
157         encoded_data[:, 0] = sos
158 
159     return encoded_data
160 
161 
162 def convert_target_to_relative(img: ImageTensor, target: Dict[str, Any]) -> Tuple[ImageTensor, Dict[str, Any]]:
163 
164     target['boxes'] = convert_to_relative_coords(target['boxes'], get_img_shape(img))
165     return img, target
166 
167 
168 def crop_bboxes_from_image(img_path: Union[str, Path], geoms: np.ndarray) -> List[np.ndarray]:
169     """Crop a set of bounding boxes from an image
170     Args:
171         img_path: path to the image
172         geoms: a array of polygons of shape (N, 4, 2) or of straight boxes of shape (N, 4)
173     Returns:
174         a list of cropped images
175     """
176     img = np.array(Image.open(img_path))
177     # Polygon
178     if geoms.ndim == 3 and geoms.shape[1:] == (4, 2):
179         return extract_rcrops(img, geoms.astype(dtype=int))
180     if geoms.ndim == 2 and geoms.shape[1] == 4:
181         return extract_crops(img, geoms.astype(dtype=int))
182     raise ValueError("Invalid geometry format")
183
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/doctr/datasets/utils.py b/doctr/datasets/utils.py
--- a/doctr/datasets/utils.py
+++ b/doctr/datasets/utils.py
@@ -72,7 +72,10 @@
     Returns:
         A list encoding the input_string"""
 
-    return list(map(vocab.index, input_string))  # type: ignore[arg-type]
+    try:
+        return list(map(vocab.index, input_string))  # type: ignore[arg-type]
+    except ValueError:
+        raise ValueError("some characters cannot be found in 'vocab'")
 
 
 def decode_sequence(

{"golden_diff": "diff --git a/doctr/datasets/utils.py b/doctr/datasets/utils.py\n--- a/doctr/datasets/utils.py\n+++ b/doctr/datasets/utils.py\n@@ -72,7 +72,10 @@\n     Returns:\n         A list encoding the input_string\"\"\"\n \n-    return list(map(vocab.index, input_string))  # type: ignore[arg-type]\n+    try:\n+        return list(map(vocab.index, input_string))  # type: ignore[arg-type]\n+    except ValueError:\n+        raise ValueError(\"some characters cannot be found in 'vocab'\")\n \n \n def decode_sequence(\n", "issue": "Fix encode_string function\n### Bug description\n\nCurrently there is no check if the single characters are also available in the given vocabulary.\r\nWe need a check for this :) \r\n\r\nTODO's:\r\n\r\n- [ ] check that in the function and throw a meaningful exception\r\n- [ ] improve the corresponding test \r\n\r\ndiscussion:\r\n#926 \n\n### Code snippet to reproduce the bug\n\n```python\r\nfrom doctr.datasets.utils import encode_string\r\nfrom doctr.datasets import VOCABS\r\n\r\nx = encode_string(input_string='abcD\u00c4\u00dc', vocab=VOCABS['english'])   # \u00c4 and \u00dc does not exist in vocab\r\n# raises ValueError: substring not found\r\n```\n\n### Error traceback\n\n```\r\nTraceback (most recent call last):\r\n  File \"/home/felix/Desktop/doctr/test.py\", line 7, in <module>\r\n    x = encode_string(input_string='abcD\u00c4\u00dc', vocab=VOCABS['english'])   # \u00c4 and \u00dc does not exist in vocab\r\n  File \"/home/felix/Desktop/doctr/doctr/datasets/utils.py\", line 75, in encode_string\r\n    return list(map(vocab.index, input_string))  # type: ignore[arg-type]\r\nValueError: substring not found\r\n```\n\n### Environment\n\nnot need :)\n\n### Deep Learning backend\n\nsame\n", "before_files": [{"content": "# Copyright (C) 2021-2022, Mindee.\n\n# This program is licensed under the Apache License version 2.\n# See LICENSE or go to <https://www.apache.org/licenses/LICENSE-2.0.txt> for full license details.\n\nimport string\nimport unicodedata\nfrom collections.abc import Sequence\nfrom functools import partial\nfrom pathlib import Path\nfrom typing import Any, Dict, List, Optional\nfrom typing import Sequence as SequenceType\nfrom typing import Tuple, TypeVar, Union\n\nimport numpy as np\nfrom PIL import Image\n\nfrom doctr.io.image import get_img_shape\nfrom doctr.utils.geometry import convert_to_relative_coords, extract_crops, extract_rcrops\n\nfrom .vocabs import VOCABS\n\n__all__ = ['translate', 'encode_string', 'decode_sequence', 'encode_sequences']\n\nImageTensor = TypeVar('ImageTensor')\n\n\ndef translate(\n    input_string: str,\n    vocab_name: str,\n    unknown_char: str = '\u25a0',\n) -> str:\n    \"\"\"Translate a string input in a given vocabulary\n\n    Args:\n        input_string: input string to translate\n        vocab_name: vocabulary to use (french, latin, ...)\n        unknown_char: unknown character for non-translatable characters\n\n    Returns:\n        A string translated in a given vocab\"\"\"\n\n    if VOCABS.get(vocab_name) is None:\n        raise KeyError(\"output vocabulary must be in vocabs dictionnary\")\n\n    translated = ''\n    for char in input_string:\n        if char not in VOCABS[vocab_name]:\n            # we need to translate char into a vocab char\n            if char in string.whitespace:\n                # remove whitespaces\n                continue\n            # normalize character if it is not in vocab\n            char = unicodedata.normalize('NFD', char).encode('ascii', 'ignore').decode('ascii')\n            if char == '' or char not in VOCABS[vocab_name]:\n                # if normalization fails or char still not in vocab, return unknown character)\n                char = unknown_char\n        translated += char\n    return translated\n\n\ndef encode_string(\n    input_string: str,\n    vocab: str,\n) -> List[int]:\n    \"\"\"Given a predefined mapping, encode the string to a sequence of numbers\n\n    Args:\n        input_string: string to encode\n        vocab: vocabulary (string), the encoding is given by the indexing of the character sequence\n\n    Returns:\n        A list encoding the input_string\"\"\"\n\n    return list(map(vocab.index, input_string))  # type: ignore[arg-type]\n\n\ndef decode_sequence(\n    input_seq: Union[np.array, SequenceType[int]],\n    mapping: str,\n) -> str:\n    \"\"\"Given a predefined mapping, decode the sequence of numbers to a string\n\n    Args:\n        input_seq: array to decode\n        mapping: vocabulary (string), the encoding is given by the indexing of the character sequence\n\n    Returns:\n        A string, decoded from input_seq\n    \"\"\"\n\n    if not isinstance(input_seq, (Sequence, np.ndarray)):\n        raise TypeError(\"Invalid sequence type\")\n    if isinstance(input_seq, np.ndarray) and (input_seq.dtype != np.int_ or input_seq.max() >= len(mapping)):\n        raise AssertionError(\"Input must be an array of int, with max less than mapping size\")\n\n    return ''.join(map(mapping.__getitem__, input_seq))\n\n\ndef encode_sequences(\n    sequences: List[str],\n    vocab: str,\n    target_size: Optional[int] = None,\n    eos: int = -1,\n    sos: Optional[int] = None,\n    pad: Optional[int] = None,\n    dynamic_seq_length: bool = False,\n    **kwargs: Any,\n) -> np.ndarray:\n    \"\"\"Encode character sequences using a given vocab as mapping\n\n    Args:\n        sequences: the list of character sequences of size N\n        vocab: the ordered vocab to use for encoding\n        target_size: maximum length of the encoded data\n        eos: encoding of End Of String\n        sos: optional encoding of Start Of String\n        pad: optional encoding for padding. In case of padding, all sequences are followed by 1 EOS then PAD\n        dynamic_seq_length: if `target_size` is specified, uses it as upper bound and enables dynamic sequence size\n\n    Returns:\n        the padded encoded data as a tensor\n    \"\"\"\n\n    if 0 <= eos < len(vocab):\n        raise ValueError(\"argument 'eos' needs to be outside of vocab possible indices\")\n\n    if not isinstance(target_size, int) or dynamic_seq_length:\n        # Maximum string length + EOS\n        max_length = max(len(w) for w in sequences) + 1\n        if isinstance(sos, int):\n            max_length += 1\n        if isinstance(pad, int):\n            max_length += 1\n        target_size = max_length if not isinstance(target_size, int) else min(max_length, target_size)\n\n    # Pad all sequences\n    if isinstance(pad, int):  # pad with padding symbol\n        if 0 <= pad < len(vocab):\n            raise ValueError(\"argument 'pad' needs to be outside of vocab possible indices\")\n        # In that case, add EOS at the end of the word before padding\n        default_symbol = pad\n    else:  # pad with eos symbol\n        default_symbol = eos\n    encoded_data = np.full([len(sequences), target_size], default_symbol, dtype=np.int32)\n\n    # Encode the strings\n    for idx, seq in enumerate(map(partial(encode_string, vocab=vocab), sequences)):\n        if isinstance(pad, int):  # add eos at the end of the sequence\n            seq.append(eos)\n        encoded_data[idx, :min(len(seq), target_size)] = seq[:min(len(seq), target_size)]\n\n    if isinstance(sos, int):  # place sos symbol at the beginning of each sequence\n        if 0 <= sos < len(vocab):\n            raise ValueError(\"argument 'sos' needs to be outside of vocab possible indices\")\n        encoded_data = np.roll(encoded_data, 1)\n        encoded_data[:, 0] = sos\n\n    return encoded_data\n\n\ndef convert_target_to_relative(img: ImageTensor, target: Dict[str, Any]) -> Tuple[ImageTensor, Dict[str, Any]]:\n\n    target['boxes'] = convert_to_relative_coords(target['boxes'], get_img_shape(img))\n    return img, target\n\n\ndef crop_bboxes_from_image(img_path: Union[str, Path], geoms: np.ndarray) -> List[np.ndarray]:\n    \"\"\"Crop a set of bounding boxes from an image\n    Args:\n        img_path: path to the image\n        geoms: a array of polygons of shape (N, 4, 2) or of straight boxes of shape (N, 4)\n    Returns:\n        a list of cropped images\n    \"\"\"\n    img = np.array(Image.open(img_path))\n    # Polygon\n    if geoms.ndim == 3 and geoms.shape[1:] == (4, 2):\n        return extract_rcrops(img, geoms.astype(dtype=int))\n    if geoms.ndim == 2 and geoms.shape[1] == 4:\n        return extract_crops(img, geoms.astype(dtype=int))\n    raise ValueError(\"Invalid geometry format\")\n", "path": "doctr/datasets/utils.py"}], "after_files": [{"content": "# Copyright (C) 2021-2022, Mindee.\n\n# This program is licensed under the Apache License version 2.\n# See LICENSE or go to <https://www.apache.org/licenses/LICENSE-2.0.txt> for full license details.\n\nimport string\nimport unicodedata\nfrom collections.abc import Sequence\nfrom functools import partial\nfrom pathlib import Path\nfrom typing import Any, Dict, List, Optional\nfrom typing import Sequence as SequenceType\nfrom typing import Tuple, TypeVar, Union\n\nimport numpy as np\nfrom PIL import Image\n\nfrom doctr.io.image import get_img_shape\nfrom doctr.utils.geometry import convert_to_relative_coords, extract_crops, extract_rcrops\n\nfrom .vocabs import VOCABS\n\n__all__ = ['translate', 'encode_string', 'decode_sequence', 'encode_sequences']\n\nImageTensor = TypeVar('ImageTensor')\n\n\ndef translate(\n    input_string: str,\n    vocab_name: str,\n    unknown_char: str = '\u25a0',\n) -> str:\n    \"\"\"Translate a string input in a given vocabulary\n\n    Args:\n        input_string: input string to translate\n        vocab_name: vocabulary to use (french, latin, ...)\n        unknown_char: unknown character for non-translatable characters\n\n    Returns:\n        A string translated in a given vocab\"\"\"\n\n    if VOCABS.get(vocab_name) is None:\n        raise KeyError(\"output vocabulary must be in vocabs dictionnary\")\n\n    translated = ''\n    for char in input_string:\n        if char not in VOCABS[vocab_name]:\n            # we need to translate char into a vocab char\n            if char in string.whitespace:\n                # remove whitespaces\n                continue\n            # normalize character if it is not in vocab\n            char = unicodedata.normalize('NFD', char).encode('ascii', 'ignore').decode('ascii')\n            if char == '' or char not in VOCABS[vocab_name]:\n                # if normalization fails or char still not in vocab, return unknown character)\n                char = unknown_char\n        translated += char\n    return translated\n\n\ndef encode_string(\n    input_string: str,\n    vocab: str,\n) -> List[int]:\n    \"\"\"Given a predefined mapping, encode the string to a sequence of numbers\n\n    Args:\n        input_string: string to encode\n        vocab: vocabulary (string), the encoding is given by the indexing of the character sequence\n\n    Returns:\n        A list encoding the input_string\"\"\"\n\n    try:\n        return list(map(vocab.index, input_string))  # type: ignore[arg-type]\n    except ValueError:\n        raise ValueError(\"some characters cannot be found in 'vocab'\")\n\n\ndef decode_sequence(\n    input_seq: Union[np.array, SequenceType[int]],\n    mapping: str,\n) -> str:\n    \"\"\"Given a predefined mapping, decode the sequence of numbers to a string\n\n    Args:\n        input_seq: array to decode\n        mapping: vocabulary (string), the encoding is given by the indexing of the character sequence\n\n    Returns:\n        A string, decoded from input_seq\n    \"\"\"\n\n    if not isinstance(input_seq, (Sequence, np.ndarray)):\n        raise TypeError(\"Invalid sequence type\")\n    if isinstance(input_seq, np.ndarray) and (input_seq.dtype != np.int_ or input_seq.max() >= len(mapping)):\n        raise AssertionError(\"Input must be an array of int, with max less than mapping size\")\n\n    return ''.join(map(mapping.__getitem__, input_seq))\n\n\ndef encode_sequences(\n    sequences: List[str],\n    vocab: str,\n    target_size: Optional[int] = None,\n    eos: int = -1,\n    sos: Optional[int] = None,\n    pad: Optional[int] = None,\n    dynamic_seq_length: bool = False,\n    **kwargs: Any,\n) -> np.ndarray:\n    \"\"\"Encode character sequences using a given vocab as mapping\n\n    Args:\n        sequences: the list of character sequences of size N\n        vocab: the ordered vocab to use for encoding\n        target_size: maximum length of the encoded data\n        eos: encoding of End Of String\n        sos: optional encoding of Start Of String\n        pad: optional encoding for padding. In case of padding, all sequences are followed by 1 EOS then PAD\n        dynamic_seq_length: if `target_size` is specified, uses it as upper bound and enables dynamic sequence size\n\n    Returns:\n        the padded encoded data as a tensor\n    \"\"\"\n\n    if 0 <= eos < len(vocab):\n        raise ValueError(\"argument 'eos' needs to be outside of vocab possible indices\")\n\n    if not isinstance(target_size, int) or dynamic_seq_length:\n        # Maximum string length + EOS\n        max_length = max(len(w) for w in sequences) + 1\n        if isinstance(sos, int):\n            max_length += 1\n        if isinstance(pad, int):\n            max_length += 1\n        target_size = max_length if not isinstance(target_size, int) else min(max_length, target_size)\n\n    # Pad all sequences\n    if isinstance(pad, int):  # pad with padding symbol\n        if 0 <= pad < len(vocab):\n            raise ValueError(\"argument 'pad' needs to be outside of vocab possible indices\")\n        # In that case, add EOS at the end of the word before padding\n        default_symbol = pad\n    else:  # pad with eos symbol\n        default_symbol = eos\n    encoded_data = np.full([len(sequences), target_size], default_symbol, dtype=np.int32)\n\n    # Encode the strings\n    for idx, seq in enumerate(map(partial(encode_string, vocab=vocab), sequences)):\n        if isinstance(pad, int):  # add eos at the end of the sequence\n            seq.append(eos)\n        encoded_data[idx, :min(len(seq), target_size)] = seq[:min(len(seq), target_size)]\n\n    if isinstance(sos, int):  # place sos symbol at the beginning of each sequence\n        if 0 <= sos < len(vocab):\n            raise ValueError(\"argument 'sos' needs to be outside of vocab possible indices\")\n        encoded_data = np.roll(encoded_data, 1)\n        encoded_data[:, 0] = sos\n\n    return encoded_data\n\n\ndef convert_target_to_relative(img: ImageTensor, target: Dict[str, Any]) -> Tuple[ImageTensor, Dict[str, Any]]:\n\n    target['boxes'] = convert_to_relative_coords(target['boxes'], get_img_shape(img))\n    return img, target\n\n\ndef crop_bboxes_from_image(img_path: Union[str, Path], geoms: np.ndarray) -> List[np.ndarray]:\n    \"\"\"Crop a set of bounding boxes from an image\n    Args:\n        img_path: path to the image\n        geoms: a array of polygons of shape (N, 4, 2) or of straight boxes of shape (N, 4)\n    Returns:\n        a list of cropped images\n    \"\"\"\n    img = np.array(Image.open(img_path))\n    # Polygon\n    if geoms.ndim == 3 and geoms.shape[1:] == (4, 2):\n        return extract_rcrops(img, geoms.astype(dtype=int))\n    if geoms.ndim == 2 and geoms.shape[1] == 4:\n        return extract_crops(img, geoms.astype(dtype=int))\n    raise ValueError(\"Invalid geometry format\")\n", "path": "doctr/datasets/utils.py"}]}