|
2 | 2 |
|
3 | 3 | import io |
4 | 4 | from pathlib import Path |
5 | | -from typing import BinaryIO |
6 | 5 |
|
7 | 6 | from mindee.dependencies import requires_bernard_ledit |
8 | 7 | from mindee.dependencies.checkers import BERNARD_LEDIT_AVAILABLE |
9 | 8 | from mindee.geometry.point import Point |
10 | | -from mindee.geometry.polygon import Polygon, get_min_max_x, get_min_max_y |
| 9 | +from mindee.geometry.polygon import Polygon |
11 | 10 | from mindee.image.extracted_image import ExtractedImage |
12 | 11 | from mindee.input.local_input_source import LocalInputSource |
13 | 12 |
|
14 | 13 | if BERNARD_LEDIT_AVAILABLE: |
15 | 14 | # pylint: disable=import-error |
16 | 15 | import bernard_ledit.image as bernard_image |
17 | | - import bernard_ledit.pdf as bernard_pdf |
18 | 16 | else: |
19 | | - bernard_pdf = None # type: ignore[assignment] # pylint: disable=invalid-name |
20 | 17 | bernard_image = None # type: ignore[assignment] # pylint: disable=invalid-name |
21 | 18 |
|
22 | 19 |
|
23 | | -@requires_bernard_ledit |
24 | | -def _attach_image_as_new_file( |
25 | | - input_buffer: BinaryIO, |
26 | | -) -> bernard_pdf.PdfDocument: |
27 | | - """ |
28 | | - Attaches an image as a new page in a PdfDocument object. |
29 | | -
|
30 | | - :param input_buffer: Input buffer. |
31 | | - :return: A PdfDocument handle. |
32 | | - """ |
33 | | - input_buffer.seek(0) |
34 | | - pdf = bernard_pdf.PdfDocument.new() |
35 | | - pdf.append_jpeg_page(input_buffer.read()) |
36 | | - return pdf |
37 | | - |
38 | | - |
39 | | -@requires_bernard_ledit |
40 | | -def extract_image_from_polygon( |
41 | | - page_content: bernard_image.Image, |
42 | | - polygon: list[Point], |
43 | | - width: float, |
44 | | - height: float, |
45 | | - file_format: str, |
46 | | - quality: int = 70, |
47 | | -) -> BinaryIO: |
48 | | - """ |
49 | | - Crops the image from the given polygon. |
50 | | -
|
51 | | - :param page_content: Contents of the page as a Bernard L'Édit Image object. |
52 | | - :param polygon: Polygon coordinates for the image. |
53 | | - :param width: Width of the generated image. |
54 | | - :param height: Height of the generated image. |
55 | | - :param file_format: Format for the generated file. |
56 | | - :param quality: JPEG quality for the output (default 70 — sufficient for OCR). |
57 | | - :return: A generated image as a buffer. |
58 | | - """ |
59 | | - min_max_x = get_min_max_x(polygon) |
60 | | - min_max_y = get_min_max_y(polygon) |
61 | | - cropped_image = page_content.crop( |
62 | | - left=int(min_max_x.min * width), |
63 | | - top=int(min_max_y.min * height), |
64 | | - right=int(min_max_x.max * width), |
65 | | - bottom=int(min_max_y.max * height), |
66 | | - ) |
67 | | - return _save_image_to_buffer(cropped_image, file_format, quality) |
68 | | - |
69 | | - |
70 | | -@requires_bernard_ledit |
71 | | -def _save_image_to_buffer( |
72 | | - image: bernard_image.Image, file_format: str, quality: int = 70 |
73 | | -) -> BinaryIO: |
74 | | - """ |
75 | | - Saves an image as a buffer. |
76 | | -
|
77 | | - :param image: Bernard L'Édit wrapper for the image. |
78 | | - :param file_format: Format to save the file as. |
79 | | - :param quality: JPEG quality (default 70 — sufficient for OCR). |
80 | | - :return: A valid buffer. |
81 | | - """ |
82 | | - buffer = io.BytesIO() |
83 | | - image.save(buffer, format=file_format, quality=quality) |
84 | | - buffer.seek(0) |
85 | | - return buffer |
86 | | - |
87 | | - |
88 | | -def get_file_extension(file_format: str): |
89 | | - """ |
90 | | - Extract the correct file extension. |
91 | | -
|
92 | | - :param file_format: Format of the file. |
93 | | - :return: A valid file extension. |
94 | | - """ |
95 | | - return file_format.lower() if file_format != "JPEG" else "jpg" |
96 | | - |
97 | | - |
98 | 20 | @requires_bernard_ledit |
99 | 21 | def extract_multiple_images_from_source( |
100 | 22 | input_source: LocalInputSource, |
101 | 23 | page_id: int, |
102 | 24 | polygons: list[Polygon | list[Point]], |
103 | | - quality: int = 70, |
104 | 25 | ) -> list[ExtractedImage]: |
105 | 26 | """ |
106 | | - Extracts elements from a page based on a list of bounding boxes. |
107 | | -
|
108 | | - :param input_source: Local Input source to extract elements from. |
109 | | - :param page_id: id of the page to extract from. |
110 | | - :param polygons: List of coordinates to pull the elements from. |
111 | | - :param quality: JPEG quality for extracted images (default 70). |
112 | | - :return: List of byte arrays representing the extracted elements. |
| 27 | + Extract multiple images from a source. |
| 28 | + :param input_source: Source to extract images from. |
| 29 | + :param page_id: Page ID. |
| 30 | + :param polygons: List of polygons to extract. |
| 31 | + :return: List of extracted images. |
113 | 32 | """ |
114 | 33 | stem = Path(input_source.filename).stem |
115 | | - doc = _load_pdf_doc(input_source) |
116 | | - page_content = doc.rasterize_page(page_id, 100) |
117 | | - width = doc.get_page(page_id).get_size().width |
118 | | - height = doc.get_page(page_id).get_size().height |
119 | | - |
120 | | - if input_source.is_pdf(): |
121 | | - file_format = "JPEG" |
122 | | - else: |
123 | | - input_source.file_object.seek(0) |
124 | | - file_format = bernard_image.guess_format(input_source.file_object) |
125 | | - |
126 | | - extracted_elements = [] |
127 | | - decoded_page = bernard_image.decode(page_content) |
128 | | - for element_id, polygon in enumerate(polygons): |
129 | | - image_data = extract_image_from_polygon( |
130 | | - decoded_page, |
131 | | - polygon, |
132 | | - width, |
133 | | - height, |
134 | | - file_format, |
135 | | - quality, |
136 | | - ) |
137 | | - extracted_elements.append( |
| 34 | + input_source.file_object.seek(0) |
| 35 | + pts = [[(p.x, p.y) for p in poly] for poly in polygons] |
| 36 | + images = bernard_image.extract_polygons(input_source.file_object, page_id, pts) # type: ignore[attr-defined] |
| 37 | + |
| 38 | + extracted = [] |
| 39 | + for element_id, img in enumerate(images): |
| 40 | + buf = io.BytesIO() |
| 41 | + img.save(buf) |
| 42 | + fmt = str(img.format).lower() if img.format else "jpeg" |
| 43 | + fmt = fmt if fmt != "jpeg" else "jpg" |
| 44 | + extracted.append( |
138 | 45 | ExtractedImage( |
139 | | - image_data, |
140 | | - f"{stem}_page-{(page_id + 1):03d}-item-{(element_id + 1):03d}." |
141 | | - f"{get_file_extension(file_format)}", |
| 46 | + buf, |
| 47 | + f"{stem}_page-{page_id + 1:03d}-item-{element_id + 1:03d}.{fmt}", |
142 | 48 | page_id, |
143 | 49 | element_id, |
144 | 50 | ) |
145 | 51 | ) |
146 | | - return extracted_elements |
147 | | - |
148 | | - |
149 | | -@requires_bernard_ledit |
150 | | -def _load_pdf_doc(input_file: LocalInputSource) -> bernard_pdf.PdfDocument: |
151 | | - """ |
152 | | - Loads a PDF document from a local input source. |
153 | | -
|
154 | | - :param input_file: Local input. |
155 | | - :return: A valid PdfDocument handle. |
156 | | - """ |
157 | | - if input_file.is_pdf(): |
158 | | - input_file.file_object.seek(0) |
159 | | - return bernard_pdf.PdfDocument(input_file.file_object.read()) |
160 | | - |
161 | | - return _attach_image_as_new_file(input_file.file_object) |
| 52 | + return extracted |
0 commit comments