Skip to content

Commit c3c331f

Browse files
♻️ switch default image save format to PNG for image extractor (#243)
1 parent 822b36d commit c3c331f

File tree

3 files changed

+93
-20
lines changed

3 files changed

+93
-20
lines changed

mindee/image_extraction/common/extracted_image.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,16 @@ def __init__(
2929
"""
3030
self.buffer = io.BytesIO(input_source.file_object.read())
3131
self.buffer.name = input_source.filename
32+
if input_source.is_pdf():
33+
extension = "jpg"
34+
else:
35+
extension = Path(input_source.filename).resolve().suffix
3236
self.buffer.seek(0)
33-
self.internal_file_name = f"{input_source.filename}_p{page_id}_{element_id}.pdf"
37+
pg_number = str(page_id).zfill(3)
38+
elem_number = str(element_id).zfill(3)
39+
self.internal_file_name = (
40+
f"{input_source.filename}_page{pg_number}-{elem_number}.{extension}"
41+
)
3442
self._page_id = page_id
3543
self._element_id = 0 if element_id is None else element_id
3644

@@ -74,15 +82,15 @@ def page_id(self):
7482
"""
7583
ID of the page the receipt was found on.
7684
77-
:return:
85+
:return: A valid page ID.
7886
"""
7987
return self._page_id
8088

8189
@property
8290
def element_id(self):
8391
"""
84-
Id of the element on a given page.
92+
ID of the element on a given page.
8593
86-
:return:
94+
:return: A valid element ID.
8795
"""
8896
return self._element_id

mindee/image_extraction/common/image_extractor.py

Lines changed: 78 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import pypdfium2 as pdfium
55
from PIL import Image
66

7+
from mindee.error import MindeeError
78
from mindee.geometry import Point, get_min_max_x, get_min_max_y
89
from mindee.image_extraction.common import ExtractedImage
910
from mindee.input import BytesInput, LocalInputSource
@@ -15,7 +16,7 @@ def attach_image_as_new_file( # type: ignore
1516
"""
1617
Attaches an image as a new page in a PdfDocument object.
1718
18-
:param input_buffer: Input buffer. Only supports JPEG.
19+
:param input_buffer: Input buffer.
1920
:return: A PdfDocument handle.
2021
"""
2122
# Create a new page in the PdfDocument
@@ -41,6 +42,75 @@ def attach_image_as_new_file( # type: ignore
4142
return pdf
4243

4344

45+
def extract_image_from_polygon(
46+
page_content: Image.Image,
47+
polygon: List[Point],
48+
width: float,
49+
height: float,
50+
file_format: str,
51+
) -> bytes:
52+
"""
53+
Crops the image from the given polygon.
54+
55+
:param page_content: Contents of the page as a Pillow object.
56+
:param polygon: Polygon coordinates for the image.
57+
:param width: Width of the generated image.
58+
:param height: Height of the generated image.
59+
:param file_format: Format for the generated file.
60+
:return: A generated image as a buffer.
61+
"""
62+
min_max_x = get_min_max_x(polygon)
63+
min_max_y = get_min_max_y(polygon)
64+
cropped_image = page_content.crop(
65+
(
66+
int(min_max_x.min * width),
67+
int(min_max_y.min * height),
68+
int(min_max_x.max * width),
69+
int(min_max_y.max * height),
70+
)
71+
)
72+
return save_image_to_buffer(cropped_image, file_format)
73+
74+
75+
def save_image_to_buffer(image: Image.Image, file_format: str) -> bytes:
76+
"""
77+
Saves an image as a buffer.
78+
79+
:param image: Pillow wrapper for the image.
80+
:param file_format: Format to save the file as.
81+
:return: A valid buffer.
82+
"""
83+
buffer = io.BytesIO()
84+
image.save(buffer, format=file_format)
85+
buffer.seek(0)
86+
return buffer.read()
87+
88+
89+
def determine_file_format(input_source: LocalInputSource) -> str:
90+
"""
91+
Retrieves the file format from an input source.
92+
93+
:param input_source: Local input source to retrieve the format from.
94+
:return: A valid pillow file format.
95+
"""
96+
if input_source.is_pdf():
97+
return "JPEG"
98+
img = Image.open(input_source.file_object)
99+
if img.format is None:
100+
raise MindeeError("Image format was not found.")
101+
return img.format
102+
103+
104+
def get_file_extension(file_format: str):
105+
"""
106+
Extract the correct file extension.
107+
108+
:param file_format: Format of the file.
109+
:return: A valid file extension.
110+
"""
111+
return file_format.lower() if file_format != "JPEG" else "jpg"
112+
113+
44114
def extract_multiple_images_from_source(
45115
input_source: LocalInputSource, page_id: int, polygons: List[List[Point]]
46116
) -> List[ExtractedImage]:
@@ -56,27 +126,19 @@ def extract_multiple_images_from_source(
56126
page_content = page.render().to_pil()
57127
width, height = page.get_size()
58128

129+
file_format = determine_file_format(input_source)
130+
file_extension = get_file_extension(file_format)
131+
59132
extracted_elements = []
60133
for element_id, polygon in enumerate(polygons):
61-
min_max_x = get_min_max_x(polygon)
62-
min_max_y = get_min_max_y(polygon)
63-
64-
pillow_page = page_content.crop(
65-
(
66-
int(min_max_x.min * width),
67-
int(min_max_y.min * height),
68-
int(min_max_x.max * width),
69-
int(min_max_y.max * height),
70-
)
134+
image_data = extract_image_from_polygon(
135+
page_content, polygon, width, height, file_format
71136
)
72-
buffer = io.BytesIO()
73-
pillow_page.save(buffer, format="JPEG")
74-
buffer.seek(0)
75137
extracted_elements.append(
76138
ExtractedImage(
77139
BytesInput(
78-
buffer.read(),
79-
f"{input_source.filename}_p{page_id}_e{element_id}.jpg",
140+
image_data,
141+
f"{input_source.filename}_page{page_id + 1}-{element_id}.{file_extension}",
80142
),
81143
page_id,
82144
element_id,

tests/image_extraction/test_image_extractor.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ def test_barcode_image_extraction(barcode_path, barcode_json_path):
3535
assert len(extracted_barcodes_1d) == 1
3636
assert len(extracted_barcodes_2d) == 2
3737

38+
assert extracted_barcodes_1d[0].as_source().filename.endswith("jpg")
3839
assert Image.open(extracted_barcodes_1d[0].buffer).size == (353, 200)
3940
assert Image.open(extracted_barcodes_2d[0].buffer).size == (214, 216)
41+
assert extracted_barcodes_2d[0].as_source().filename.endswith("jpg")
42+
assert extracted_barcodes_2d[1].as_source().filename.endswith("jpg")
4043
assert Image.open(extracted_barcodes_2d[1].buffer).size == (193, 201)

0 commit comments

Comments
 (0)